1 /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
3 * Copyright (c) 2004-2005 Apple Computer, Inc. All rights reserved.
5 * @APPLE_LICENSE_HEADER_START@
7 * This file contains Original Code and/or Modifications of Original Code
8 * as defined in and that are subject to the Apple Public Source License
9 * Version 2.0 (the 'License'). You may not use this file except in
10 * compliance with the License. Please obtain a copy of the License at
11 * http://www.opensource.apple.com/apsl/ and read it before using this
14 * The Original Code and all software distributed under the License are
15 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
19 * Please see the License for the specific language governing rights and
20 * limitations under the License.
22 * @APPLE_LICENSE_HEADER_END@
25 #define __STDC_LIMIT_MACROS
29 #include <mach/mach.h>
30 #include <mach-o/fat.h>
31 #include <sys/types.h>
34 #include <sys/param.h>
35 #include <sys/mount.h>
37 #include "ImageLoader.h"
40 uint32_t ImageLoader::fgImagesWithUsedPrebinding
= 0;
41 uint32_t ImageLoader::fgTotalRebaseFixups
= 0;
42 uint32_t ImageLoader::fgTotalBindFixups
= 0;
43 uint32_t ImageLoader::fgTotalLazyBindFixups
= 0;
44 uint32_t ImageLoader::fgTotalPossibleLazyBindFixups
= 0;
45 uint64_t ImageLoader::fgTotalLoadLibrariesTime
;
46 uint64_t ImageLoader::fgTotalRebaseTime
;
47 uint64_t ImageLoader::fgTotalBindTime
;
48 uint64_t ImageLoader::fgTotalNotifyTime
;
49 uint64_t ImageLoader::fgTotalInitTime
;
50 uintptr_t ImageLoader::fgNextSplitSegAddress
= 0x90000000;
51 uintptr_t Segment::fgNextNonSplitSegAddress
= 0x8FE00000;
55 __attribute__((noreturn
))
56 void throwf(const char* format
, ...)
60 va_start(list
, format
);
61 vasprintf(&p
, format
, list
);
68 void ImageLoader::init(const char* path
, uint64_t offsetInFat
, dev_t device
, ino_t inode
, time_t modDate
)
77 fLastModified
= modDate
;
78 fOffsetInFatFile
= offsetInFat
;
83 fAllLibraryChecksumsAndLoadAddressesMatch
= false;
86 fMatchByInstallName
= false;
87 fLibrariesLoaded
= false;
89 fBoundAllNonLazy
= false;
90 fBoundAllLazy
= false;
93 fNextAddImageIndex
= 0;
97 ImageLoader::ImageLoader(const char* path
, uint64_t offsetInFat
, const struct stat
& info
)
99 init(path
, offsetInFat
, info
.st_dev
, info
.st_ino
, info
.st_mtime
);
102 ImageLoader::ImageLoader(const char* moduleName
)
104 init(moduleName
, 0, 0, 0, 0);
108 ImageLoader::~ImageLoader()
110 // need to read up on STL and see if this is right way to destruct vector contents
111 const unsigned int segmentCount
= fSegments
.size();
112 for(unsigned int i
=0; i
< segmentCount
; ++i
){
113 Segment
* seg
= fSegments
[i
];
118 if ( fLogicalPath
!= NULL
)
119 delete [] fLogicalPath
;
123 void ImageLoader::setPath(const char* path
)
125 if ( fPath
!= NULL
) {
126 // if duplicate path, do nothing
127 if ( strcmp(path
, fPath
) == 0 )
131 fPath
= new char[strlen(path
)+1];
132 strcpy((char*)fPath
, path
);
133 fPathHash
= hash(fPath
);
136 void ImageLoader::setLogicalPath(const char* path
)
138 if ( fPath
== NULL
) {
139 // no physical path set yet, so use this path as physical
142 else if ( strcmp(path
, fPath
) == 0 ) {
143 // do not set logical path because it is the same as the physical path
147 fLogicalPath
= new char[strlen(path
)+1];
148 strcpy((char*)fLogicalPath
, path
);
152 const char* ImageLoader::getLogicalPath() const
154 if ( fLogicalPath
!= NULL
)
160 uint32_t ImageLoader::hash(const char* path
)
162 // this does not need to be a great hash
163 // it is just used to reduce the number of strcmp() calls
164 // of existing images when loading a new image
166 for (const char* s
=path
; *s
!= '\0'; ++s
)
171 bool ImageLoader::matchInstallPath() const
173 return fMatchByInstallName
;
176 void ImageLoader::setMatchInstallPath(bool match
)
178 fMatchByInstallName
= match
;
181 bool ImageLoader::statMatch(const struct stat
& stat_buf
) const
183 return ( (this->fDevice
== stat_buf
.st_dev
) && (this->fInode
== stat_buf
.st_ino
) );
186 const char* ImageLoader::getShortName() const
188 // try to return leaf name
189 if ( fPath
!= NULL
) {
190 const char* s
= strrchr(fPath
, '/');
197 uint64_t ImageLoader::getOffsetInFatFile() const
199 return fOffsetInFatFile
;
202 void ImageLoader::setLeaveMapped()
205 const unsigned int segmentCount
= fSegments
.size();
206 for(unsigned int i
=0; i
< segmentCount
; ++i
){
207 fSegments
[i
]->setUnMapWhenDestructed(false);
211 void ImageLoader::setHideExports(bool hide
)
216 bool ImageLoader::hasHiddenExports() const
221 bool ImageLoader::isLinked() const
223 return fBoundAllNonLazy
;
226 time_t ImageLoader::lastModified()
228 return fLastModified
;
231 bool ImageLoader::containsAddress(const void* addr
) const
233 const unsigned int segmentCount
= fSegments
.size();
234 for(unsigned int i
=0; i
< segmentCount
; ++i
){
235 Segment
* seg
= fSegments
[i
];
236 const uint8_t* start
= (const uint8_t*)seg
->getActualLoadAddress();
237 const uint8_t* end
= start
+ seg
->getSize();
238 if ( (start
<= addr
) && (addr
< end
) && !seg
->unaccessible() )
244 void ImageLoader::addMappedRegions(RegionsVector
& regions
) const
246 const unsigned int segmentCount
= fSegments
.size();
247 for(unsigned int i
=0; i
< segmentCount
; ++i
){
248 Segment
* seg
= fSegments
[i
];
250 region
.address
= seg
->getActualLoadAddress();
251 region
.size
= seg
->getSize();
252 regions
.push_back(region
);
257 void ImageLoader::incrementReferenceCount()
262 bool ImageLoader::decrementReferenceCount()
264 return ( --fReferenceCount
== 0 );
267 // private method that handles circular dependencies by only search any image once
268 const ImageLoader::Symbol
* ImageLoader::findExportedSymbolInDependentImagesExcept(const char* name
, std::set
<const ImageLoader
*>& dontSearchImages
, ImageLoader
** foundIn
) const
270 const ImageLoader::Symbol
* sym
;
272 if ( dontSearchImages
.count(this) == 0 ) {
273 sym
= this->findExportedSymbol(name
, NULL
, false, foundIn
);
276 dontSearchImages
.insert(this);
279 // search directly dependent libraries
280 for (uint32_t i
=0; i
< fLibrariesCount
; ++i
) {
281 ImageLoader
* dependentImage
= fLibraries
[i
].image
;
282 if ( (dependentImage
!= NULL
) && (dontSearchImages
.count(dependentImage
) == 0) ) {
283 const ImageLoader::Symbol
* sym
= dependentImage
->findExportedSymbol(name
, NULL
, false, foundIn
);
289 // search indirectly dependent libraries
290 for (uint32_t i
=0; i
< fLibrariesCount
; ++i
) {
291 ImageLoader
* dependentImage
= fLibraries
[i
].image
;
292 if ( (dependentImage
!= NULL
) && (dontSearchImages
.count(dependentImage
) == 0) ) {
293 const ImageLoader::Symbol
* sym
= dependentImage
->findExportedSymbolInDependentImagesExcept(name
, dontSearchImages
, foundIn
);
296 dontSearchImages
.insert(dependentImage
);
304 const ImageLoader::Symbol
* ImageLoader::findExportedSymbolInDependentImages(const char* name
, ImageLoader
** foundIn
) const
306 std::set
<const ImageLoader
*> dontSearchImages
;
307 dontSearchImages
.insert(this); // don't search this image
308 return this->findExportedSymbolInDependentImagesExcept(name
, dontSearchImages
, foundIn
);
311 const ImageLoader::Symbol
* ImageLoader::findExportedSymbolInImageOrDependentImages(const char* name
, ImageLoader
** foundIn
) const
313 std::set
<const ImageLoader
*> dontSearchImages
;
314 return this->findExportedSymbolInDependentImagesExcept(name
, dontSearchImages
, foundIn
);
318 void ImageLoader::link(const LinkContext
& context
, BindingLaziness bindness
, InitializerRunning inits
, uint32_t notifyCount
)
320 uint64_t t1
= mach_absolute_time();
321 this->recursiveLoadLibraries(context
);
323 uint64_t t2
= mach_absolute_time();
324 this->recursiveRebase(context
);
326 uint64_t t3
= mach_absolute_time();
327 this->recursiveBind(context
, bindness
);
329 uint64_t t4
= mach_absolute_time();
330 this->recursiveImageNotification(context
, notifyCount
);
332 if ( (inits
== kRunInitializers
) || (inits
== kDontRunInitializersButTellObjc
) ) {
333 std::vector
<ImageLoader
*> newImages
;
334 this->recursiveImageAnnouncement(context
, newImages
); // build bottom up list images being added
335 context
.notifyAdding(newImages
); // tell gdb or anyone who cares about these
338 uint64_t t5
= mach_absolute_time();
339 if ( inits
== kRunInitializers
) {
340 this->recursiveInitialization(context
);
341 uint64_t t6
= mach_absolute_time();
342 fgTotalInitTime
+= t6
- t5
;
344 fgTotalLoadLibrariesTime
+= t2
- t1
;
345 fgTotalRebaseTime
+= t3
- t2
;
346 fgTotalBindTime
+= t4
- t3
;
347 fgTotalNotifyTime
+= t5
- t4
;
351 // only called pre-main on main executable
352 // if crt.c is ever cleaned up, this could go away
353 void ImageLoader::runInitializers(const LinkContext
& context
)
355 std::vector
<ImageLoader
*> newImages
;
356 this->recursiveImageAnnouncement(context
, newImages
); // build bottom up list images being added
357 context
.notifyAdding(newImages
); // tell gdb or anyone who cares about these
359 this->recursiveInitialization(context
);
362 // called inside _dyld_register_func_for_add_image()
363 void ImageLoader::runNotification(const LinkContext
& context
, uint32_t notifyCount
)
365 this->recursiveImageNotification(context
, notifyCount
);
369 intptr_t ImageLoader::assignSegmentAddresses(const LinkContext
& context
)
371 // preflight and calculate slide if needed
372 const unsigned int segmentCount
= fSegments
.size();
374 if ( this->segmentsCanSlide() && this->segmentsMustSlideTogether() ) {
375 bool needsToSlide
= false;
376 uintptr_t lowAddr
= UINTPTR_MAX
;
377 uintptr_t highAddr
= 0;
378 for(unsigned int i
=0; i
< segmentCount
; ++i
){
379 Segment
* seg
= fSegments
[i
];
380 const uintptr_t segLow
= seg
->getPreferredLoadAddress();
381 const uintptr_t segHigh
= segLow
+ seg
->getSize();
382 if ( segLow
< lowAddr
)
384 if ( segHigh
> highAddr
)
387 if ( context
.slideAndPackDylibs
|| !seg
->hasPreferredLoadAddress() || !Segment::reserveAddressRange(seg
->getPreferredLoadAddress(), seg
->getSize()) )
390 if ( needsToSlide
) {
391 // find a chunk of address space to hold all segments
392 uintptr_t addr
= Segment::reserveAnAddressRange(highAddr
-lowAddr
, context
);
393 slide
= addr
- lowAddr
;
396 else if ( ! this->segmentsCanSlide() ) {
397 for(unsigned int i
=0; i
< segmentCount
; ++i
){
398 Segment
* seg
= fSegments
[i
];
399 if ( strcmp(seg
->getName(), "__PAGEZERO") == 0 )
401 if ( !Segment::reserveAddressRange(seg
->getPreferredLoadAddress(), seg
->getSize()) )
406 // mach-o does not support independently sliding segments
412 void ImageLoader::mapSegments(int fd
, uint64_t offsetInFat
, uint64_t lenInFat
, uint64_t fileLen
, const LinkContext
& context
)
414 if ( context
.verboseMapping
)
415 fprintf(stderr
, "dyld: Mapping %s\n", this->getPath());
416 // find address range for image
417 intptr_t slide
= this->assignSegmentAddresses(context
);
418 // map in all segments
419 const unsigned int segmentCount
= fSegments
.size();
420 for(unsigned int i
=0; i
< segmentCount
; ++i
){
421 Segment
* seg
= fSegments
[i
];
422 seg
->map(fd
, offsetInFat
, slide
, context
);
424 // update slide to reflect load location
425 this->setSlide(slide
);
427 // now that it is mapped and slide is set, mark that we should unmap it when done
428 for(unsigned int i
=0; i
< segmentCount
; ++i
){
429 fSegments
[i
]->setUnMapWhenDestructed(true);
433 void ImageLoader::mapSegments(const void* memoryImage
, uint64_t imageLen
, const LinkContext
& context
)
435 if ( context
.verboseMapping
)
436 fprintf(stderr
, "dyld: Mapping memory %p\n", memoryImage
);
437 // find address range for image
438 intptr_t slide
= this->assignSegmentAddresses(context
);
439 // map in all segments
440 const unsigned int segmentCount
= fSegments
.size();
441 for(unsigned int i
=0; i
< segmentCount
; ++i
){
442 Segment
* seg
= fSegments
[i
];
443 seg
->map(memoryImage
, slide
, context
);
445 // update slide to reflect load location
446 this->setSlide(slide
);
447 // set R/W permissions on all segments at slide location
448 for(unsigned int i
=0; i
< segmentCount
; ++i
){
449 Segment
* seg
= fSegments
[i
];
450 seg
->setPermissions();
454 bool ImageLoader::allDependentLibrariesAsWhenPreBound() const
456 return fAllLibraryChecksumsAndLoadAddressesMatch
;
460 void ImageLoader::recursiveLoadLibraries(const LinkContext
& context
)
462 if ( ! fLibrariesLoaded
) {
464 fLibrariesLoaded
= true;
466 // get list of libraries this image needs
467 fLibrariesCount
= this->doGetDependentLibraryCount();
468 fLibraries
= new DependentLibrary
[fLibrariesCount
];
469 this->doGetDependentLibraries(fLibraries
);
472 bool canUsePrelinkingInfo
= true;
473 for(unsigned int i
=0; i
< fLibrariesCount
; ++i
){
474 DependentLibrary
& requiredLib
= fLibraries
[i
];
476 requiredLib
.image
= context
.loadLibrary(requiredLib
.name
, true, this->getPath(), NULL
);
477 if ( requiredLib
.image
== this ) {
478 // found circular reference, perhaps DYLD_LIBARY_PATH is causing this rdar://problem/3684168
479 requiredLib
.image
= context
.loadLibrary(requiredLib
.name
, false, NULL
, NULL
);
480 if ( requiredLib
.image
!= this )
481 fprintf(stderr
, "dyld: warning DYLD_ setting caused circular dependency in %s\n", this->getPath());
483 LibraryInfo actualInfo
= requiredLib
.image
->doGetLibraryInfo();
484 requiredLib
.checksumMatches
= ( actualInfo
.checksum
== requiredLib
.info
.checksum
);
485 // check found library version is compatible
486 if ( actualInfo
.minVersion
< requiredLib
.info
.minVersion
) {
487 throwf("Incompatible library version: %s requires version %d.%d.%d or later, but %s provides version %d.%d.%d",
488 this->getShortName(), requiredLib
.info
.minVersion
>> 16, (requiredLib
.info
.minVersion
>> 8) & 0xff, requiredLib
.info
.minVersion
& 0xff,
489 requiredLib
.image
->getShortName(), actualInfo
.minVersion
>> 16, (actualInfo
.minVersion
>> 8) & 0xff, actualInfo
.minVersion
& 0xff);
491 // prebinding for this image disabled if any dependent library changed or slid
492 if ( !requiredLib
.checksumMatches
|| (requiredLib
.image
->getSlide() != 0) )
493 canUsePrelinkingInfo
= false;
494 //if ( context.verbosePrebinding ) {
495 // if ( !requiredLib.checksumMatches )
496 // fprintf(stderr, "dyld: checksum mismatch, (%lld v %lld) for %s referencing %s\n", requiredLib.info.checksum, actualInfo.checksum, this->getPath(), requiredLib.image->getPath());
497 // if ( requiredLib.image->getSlide() != 0 )
498 // fprintf(stderr, "dyld: dependent library slid for %s referencing %s\n", this->getPath(), requiredLib.image->getPath());
501 catch (const char* msg
) {
502 //if ( context.verbosePrebinding )
503 // fprintf(stderr, "dyld: exception during processing for %s referencing %s\n", this->getPath(), requiredLib.image->getPath());
504 if ( requiredLib
.required
) {
505 const char* formatString
= "Library not loaded: %s\n Referenced from: %s\n Reason: %s";
506 const char* referencedFrom
= this->getPath();
507 char buf
[strlen(requiredLib
.name
)+strlen(referencedFrom
)+strlen(formatString
)+strlen(msg
)+2];
508 sprintf(buf
, formatString
, requiredLib
.name
, referencedFrom
, msg
);
509 throw strdup(buf
); // this is a leak if exception doesn't halt program
511 // ok if weak library not found
512 requiredLib
.image
= NULL
;
513 canUsePrelinkingInfo
= false; // this disables all prebinding, we may want to just slam import vectors for this lib to zero
516 fAllLibraryChecksumsAndLoadAddressesMatch
= canUsePrelinkingInfo
;
518 // tell each to load its dependents
519 for(unsigned int i
=0; i
< fLibrariesCount
; ++i
){
520 DependentLibrary
& libInfo
= fLibraries
[i
];
521 if ( libInfo
.image
!= NULL
) {
522 libInfo
.isSubFramework
= libInfo
.image
->isSubframeworkOf(context
, this);
523 libInfo
.isReExported
= libInfo
.isSubFramework
|| this->hasSubLibrary(context
, libInfo
.image
);
524 //if ( libInfo.isReExported )
525 // fprintf(stderr, "%s re-exports %s\n", strrchr(this->getPath(), '/'), strrchr(libInfo.image->getPath(),'/'));
526 libInfo
.image
->recursiveLoadLibraries(context
);
530 // do deep prebind check
531 if ( fAllLibraryChecksumsAndLoadAddressesMatch
) {
532 for(unsigned int i
=0; i
< fLibrariesCount
; ++i
){
533 const DependentLibrary
& libInfo
= fLibraries
[i
];
534 if ( libInfo
.image
!= NULL
) {
535 if ( !libInfo
.image
->allDependentLibrariesAsWhenPreBound() )
536 fAllLibraryChecksumsAndLoadAddressesMatch
= false;
544 void ImageLoader::recursiveRebase(const LinkContext
& context
)
551 // rebase lower level libraries first
552 for(unsigned int i
=0; i
< fLibrariesCount
; ++i
){
553 DependentLibrary
& libInfo
= fLibraries
[i
];
554 if ( libInfo
.image
!= NULL
)
555 libInfo
.image
->recursiveRebase(context
);
561 catch (const char* msg
) {
562 // this image is not rebased
572 void ImageLoader::recursiveBind(const LinkContext
& context
, BindingLaziness bindness
)
574 // normally just non-lazy pointers are bound up front,
575 // but DYLD_BIND_AT_LAUNCH will cause lazy pointers to be bound up from
576 // and some dyld API's bind all lazys at runtime
577 bool nonLazy
= false;
583 case kLazyAndNonLazy
:
588 case kLazyOnlyNoDependents
:
592 const bool doNonLazy
= nonLazy
&& !fBoundAllNonLazy
;
593 const bool doLazy
= lazy
&& !fBoundAllLazy
;
594 if ( doNonLazy
|| doLazy
) {
596 bool oldBoundAllNonLazy
= fBoundAllNonLazy
;
597 bool oldBoundAllLazy
= fBoundAllLazy
;
598 fBoundAllNonLazy
= fBoundAllNonLazy
|| nonLazy
;
599 fBoundAllLazy
= fBoundAllLazy
|| lazy
;
602 // bind lower level libraries first
603 if ( bindness
!= kLazyOnlyNoDependents
) {
604 for(unsigned int i
=0; i
< fLibrariesCount
; ++i
){
605 DependentLibrary
& libInfo
= fLibraries
[i
];
606 if ( libInfo
.image
!= NULL
)
607 libInfo
.image
->recursiveBind(context
, bindness
);
611 if ( doLazy
&& !doNonLazy
)
612 doBind(context
, kLazyOnly
);
613 else if ( !doLazy
&& doNonLazy
)
614 doBind(context
, kNonLazyOnly
);
616 doBind(context
, kLazyAndNonLazy
);
618 catch (const char* msg
) {
620 fBoundAllNonLazy
= oldBoundAllNonLazy
;
621 fBoundAllLazy
= oldBoundAllLazy
;
628 // This is complex because _dyld_register_func_for_add_image() is defined to not only
629 // notify you of future image loads, but also of all currently loaded images. Therefore
630 // each image needs to track that all add-image-funcs have been notified about it.
631 // Since add-image-funcs cannot be removed, each has a unique index and each image
632 // records the thru which index notificiation has already been done.
634 void ImageLoader::recursiveImageNotification(const LinkContext
& context
, uint32_t addImageCount
)
636 if ( fNextAddImageIndex
< addImageCount
) {
638 const uint32_t initIndex
= fNextAddImageIndex
;
639 fNextAddImageIndex
= addImageCount
;
641 // notify all requestors about this image
642 context
.imageNotification(this, initIndex
);
644 // notify about lower level libraries first
645 for(unsigned int i
=0; i
< fLibrariesCount
; ++i
){
646 DependentLibrary
& libInfo
= fLibraries
[i
];
647 if ( libInfo
.image
!= NULL
)
648 libInfo
.image
->recursiveImageNotification(context
, addImageCount
);
654 void ImageLoader::recursiveImageAnnouncement(const LinkContext
& context
, std::vector
<ImageLoader
*>& newImages
)
656 if ( ! fAnnounced
) {
660 // announce lower level libraries first
661 for(unsigned int i
=0; i
< fLibrariesCount
; ++i
){
662 DependentLibrary
& libInfo
= fLibraries
[i
];
663 if ( libInfo
.image
!= NULL
)
664 libInfo
.image
->recursiveImageAnnouncement(context
, newImages
);
667 // add to list of images to notify gdb about
668 newImages
.push_back(this);
669 //fprintf(stderr, "next size = %d\n", newImages.size());
671 // remember that this image wants to be notified about other images
672 if ( this->hasImageNotification() )
673 context
.addImageNeedingNotification(this);
679 void ImageLoader::recursiveInitialization(const LinkContext
& context
)
681 if ( ! fInitialized
) {
686 // initialize lower level libraries first
687 for(unsigned int i
=0; i
< fLibrariesCount
; ++i
){
688 DependentLibrary
& libInfo
= fLibraries
[i
];
689 if ( libInfo
.image
!= NULL
)
690 libInfo
.image
->recursiveInitialization(context
);
693 // record termination order
694 if ( this->needsTermination() )
695 context
.terminationRecorder(this);
697 // initialize this image
698 this->doInitialization(context
);
700 catch (const char* msg
) {
701 // this image is not initialized
702 fInitialized
= false;
708 void ImageLoader::reprebindCommit(const LinkContext
& context
, bool commit
, bool unmapOld
)
710 // do nothing on unprebound images
711 if ( ! this->isPrebindable() )
714 // do nothing if prebinding is up to date
715 if ( this->usablePrebinding(context
) )
718 // make sure we are not replacing a symlink with a file
719 char realFilePath
[PATH_MAX
];
720 if ( realpath(this->getPath(), realFilePath
) == NULL
) {
721 throwf("realpath() failed on %s, errno=%d", this->getPath(), errno
);
723 // recreate temp file name
724 char tempFilePath
[strlen(realFilePath
)+strlen("_redoprebinding")+2];
725 ImageLoader::addSuffix(realFilePath
, "_redoprebinding", tempFilePath
);
728 // all files successfully reprebound, so do swap
729 int result
= rename(tempFilePath
, realFilePath
);
731 // if there are two dylibs with the same install path, the second will fail to prebind
732 // because the _redoprebinding temp file is gone. In that case, log and go on.
733 if ( errno
== ENOENT
)
734 fprintf(stderr
, "update_prebinding: temp file missing: %s\n", tempFilePath
);
736 throwf("can't swap temporary re-prebound file: rename(%s,%s) returned errno=%d", tempFilePath
, realFilePath
, errno
);
738 else if ( unmapOld
) {
739 this->prebindUnmap(context
);
743 // something went wrong during prebinding, delete the temp files
744 unlink(tempFilePath
);
748 uint64_t ImageLoader::reprebind(const LinkContext
& context
, time_t timestamp
)
750 // do nothing on unprebound images
751 if ( ! this->isPrebindable() )
754 // do nothing if prebinding is up to date
755 if ( this->usablePrebinding(context
) ) {
756 if ( context
.verbosePrebinding
)
757 fprintf(stderr
, "dyld: no need to re-prebind: %s\n", this->getPath());
760 // recreate temp file name
761 char realFilePath
[PATH_MAX
];
762 if ( realpath(this->getPath(), realFilePath
) == NULL
) {
763 throwf("realpath() failed on %s, errno=%d", this->getPath(), errno
);
765 char tempFilePath
[strlen(realFilePath
)+strlen("_redoprebinding")+2];
766 ImageLoader::addSuffix(realFilePath
, "_redoprebinding", tempFilePath
);
768 // make copy of file and map it in
769 uint8_t* fileToPrebind
;
770 uint64_t fileToPrebindSize
;
771 uint64_t freespace
= this->copyAndMap(tempFilePath
, &fileToPrebind
, &fileToPrebindSize
);
773 // do format specific prebinding
774 this->doPrebinding(context
, timestamp
, fileToPrebind
);
776 // flush and swap files
777 int result
= msync(fileToPrebind
, fileToPrebindSize
, MS_ASYNC
);
779 throw "error syncing re-prebound file";
780 result
= munmap(fileToPrebind
, fileToPrebindSize
);
782 throw "error unmapping re-prebound file";
785 if ( context
.verbosePrebinding
)
786 fprintf(stderr
, "dyld: re-prebound: %p %s\n", this->machHeader(), this->getPath());
791 uint64_t ImageLoader::copyAndMap(const char* tempFile
, uint8_t** fileToPrebind
, uint64_t* fileToPrebindSize
)
794 int src
= open(this->getPath(), O_RDONLY
);
796 throw "can't open image";
797 struct stat stat_buf
;
798 if ( fstat(src
, &stat_buf
) == -1)
799 throw "can't stat image";
800 if ( stat_buf
.st_mtime
!= fLastModified
)
801 throw "image file changed since it was loaded";
803 // create new file with all same permissions to hold copy of dylib
805 int dst
= open(tempFile
, O_CREAT
| O_RDWR
| O_TRUNC
, stat_buf
.st_mode
);
807 throw "can't create temp image";
809 // mark source as "don't cache"
810 (void)fcntl(src
, F_NOCACHE
, 1);
811 // we want to cache the dst because we are about to map it in and modify it
813 // copy permission bits
814 if ( chmod(tempFile
, stat_buf
.st_mode
& 07777) == -1 )
815 throwf("can't chmod temp image. errno=%d for %s", errno
, this->getPath());
816 if ( chown(tempFile
, stat_buf
.st_uid
, stat_buf
.st_gid
) == -1)
817 throwf("can't chown temp image. errno=%d for %s", errno
, this->getPath());
821 const uint32_t kBufferSize
= 128*1024;
822 static uint8_t* buffer
= NULL
;
823 if ( buffer
== NULL
) {
824 vm_address_t addr
= 0;
825 if ( vm_allocate(mach_task_self(), &addr
, kBufferSize
, true /*find range*/) == KERN_SUCCESS
)
826 buffer
= (uint8_t*)addr
;
828 throw "can't allcoate copy buffer";
830 while ( (len
= read(src
, buffer
, kBufferSize
)) > 0 ) {
831 if ( write(dst
, buffer
, len
) == -1 )
832 throwf("write failure copying dylib errno=%d for %s", errno
, this->getPath());
836 *fileToPrebindSize
= stat_buf
.st_size
- fOffsetInFatFile
; // this may map in too much, but it does not matter
837 *fileToPrebind
= (uint8_t*)mmap(NULL
, *fileToPrebindSize
, PROT_READ
| PROT_WRITE
, MAP_FILE
| MAP_SHARED
, dst
, fOffsetInFatFile
);
838 if ( *fileToPrebind
== (uint8_t*)(-1) )
839 throw "can't mmap temp image";
841 // get free space remaining on dst volume
842 struct statfs statfs_buf
;
843 if ( fstatfs(dst
, &statfs_buf
) != 0 )
844 throwf("can't fstatfs(), errno=%d for %s", errno
, tempFile
);
845 uint64_t freespace
= statfs_buf
.f_bavail
* statfs_buf
.f_bsize
;
848 // ok to close file after mapped in
849 // ok to throw above without closing file because the throw will terminate update_prebinding
850 int result1
= close(dst
);
851 int result2
= close(src
);
852 if ( (result1
!= 0) || (result2
!= 0) )
853 throw "can't close file";
859 static void printTime(const char* msg
, uint64_t partTime
, uint64_t totalTime
)
861 static uint64_t sUnitsPerSecond
= 0;
862 if ( sUnitsPerSecond
== 0 ) {
863 struct mach_timebase_info timeBaseInfo
;
864 if ( mach_timebase_info(&timeBaseInfo
) == KERN_SUCCESS
) {
865 sUnitsPerSecond
= timeBaseInfo
.denom
;
868 if ( partTime
< sUnitsPerSecond
) {
869 uint32_t milliSecondsTimeTen
= (partTime
*10000)/sUnitsPerSecond
;
870 uint32_t milliSeconds
= milliSecondsTimeTen
/10;
871 uint32_t percentTimesTen
= (partTime
*1000)/totalTime
;
872 uint32_t percent
= percentTimesTen
/10;
873 fprintf(stderr
, "%s: %u.%u milliseconds (%u.%u%%)\n", msg
, milliSeconds
, milliSecondsTimeTen
-milliSeconds
*10, percent
, percentTimesTen
-percent
*10);
876 uint32_t secondsTimeTen
= (partTime
*10)/sUnitsPerSecond
;
877 uint32_t seconds
= secondsTimeTen
/100;
878 uint32_t percentTimesTen
= (partTime
*1000)/totalTime
;
879 uint32_t percent
= percentTimesTen
/10;
880 fprintf(stderr
, "%s: %u.%u seconds (%u.%u%%)\n", msg
, seconds
, secondsTimeTen
-seconds
*10, percent
, percentTimesTen
-percent
*10);
884 static char* commatize(uint64_t in
, char* out
)
888 sprintf(rawNum
, "%llu", in
);
889 const int rawNumLen
= strlen(rawNum
);
890 for(int i
=0; i
< rawNumLen
-1; ++i
) {
892 if ( ((rawNumLen
-i
) % 3) == 1 )
895 *out
++ = rawNum
[rawNumLen
-1];
901 void ImageLoader::printStatistics(unsigned int imageCount
)
903 uint64_t totalTime
= fgTotalLoadLibrariesTime
+ fgTotalRebaseTime
+ fgTotalBindTime
+ fgTotalNotifyTime
+ fgTotalInitTime
;
907 printTime("total time", totalTime
, totalTime
);
908 fprintf(stderr
, "total images loaded: %d (%d used prebinding)\n", imageCount
, fgImagesWithUsedPrebinding
);
909 printTime("total images loading time", fgTotalLoadLibrariesTime
, totalTime
);
910 fprintf(stderr
, "total rebase fixups: %s\n", commatize(fgTotalRebaseFixups
, commaNum1
));
911 printTime("total rebase fixups time", fgTotalRebaseFixups
, totalTime
);
912 fprintf(stderr
, "total binding fixups: %s\n", commatize(fgTotalBindFixups
, commaNum1
));
913 printTime("total binding fixups time", fgTotalBindTime
, totalTime
);
914 fprintf(stderr
, "total bindings lazily fixed up: %s of %s\n", commatize(fgTotalLazyBindFixups
, commaNum1
), commatize(fgTotalPossibleLazyBindFixups
, commaNum2
));
915 printTime("total notify time time", fgTotalNotifyTime
, totalTime
);
916 printTime("total init time time", fgTotalInitTime
, totalTime
);
921 // copy path and add suffix to result
923 // /path/foo.dylib _debug => /path/foo_debug.dylib
924 // foo.dylib _debug => foo_debug.dylib
925 // foo _debug => foo_debug
926 // /path/bar _debug => /path/bar_debug
927 // /path/bar.A.dylib _debug => /path/bar.A_debug.dylib
929 void ImageLoader::addSuffix(const char* path
, const char* suffix
, char* result
)
931 strcpy(result
, path
);
933 char* start
= strrchr(result
, '/');
939 char* dot
= strrchr(start
, '.');
942 strcat(&dot
[strlen(suffix
)], &path
[dot
-result
]);
945 strcat(result
, suffix
);
950 void Segment::map(int fd
, uint64_t offsetInFatWrapper
, intptr_t slide
, const ImageLoader::LinkContext
& context
)
952 vm_offset_t fileOffset
= this->getFileOffset() + offsetInFatWrapper
;
953 vm_size_t size
= this->getFileSize();
954 void* requestedLoadAddress
= (void*)(this->getPreferredLoadAddress() + slide
);
956 if ( !this->unaccessible() ) {
957 if ( this->executable() )
958 protection
|= PROT_EXEC
;
959 if ( this->readable() )
960 protection
|= PROT_READ
;
961 if ( this->writeable() )
962 protection
|= PROT_WRITE
;
964 void* loadAddress
= mmap(requestedLoadAddress
, size
, protection
, MAP_FILE
| MAP_FIXED
| MAP_PRIVATE
, fd
, fileOffset
);
965 if ( loadAddress
== ((void*)(-1)) )
966 throwf("mmap() error %d at address=0x%08lX, size=0x%08lX in Segment::map() mapping %s", errno
, (uintptr_t)requestedLoadAddress
, (uintptr_t)size
, this->getImage()->getPath());
968 if ( context
.verboseMapping
)
969 fprintf(stderr
, "%18s at %p->%p\n", this->getName(), loadAddress
, (char*)loadAddress
+this->getFileSize()-1);
972 void Segment::map(const void* memoryImage
, intptr_t slide
, const ImageLoader::LinkContext
& context
)
974 vm_address_t loadAddress
= this->getPreferredLoadAddress() + slide
;
975 vm_address_t srcAddr
= (uintptr_t)memoryImage
+ this->getFileOffset();
976 vm_size_t size
= this->getFileSize();
977 kern_return_t r
= vm_copy(mach_task_self(), srcAddr
, size
, loadAddress
);
978 if ( r
!= KERN_SUCCESS
)
979 throw "can't map segment";
981 if ( context
.verboseMapping
)
982 fprintf(stderr
, "%18s at %p->%p\n", this->getName(), (char*)loadAddress
, (char*)loadAddress
+this->getFileSize()-1);
985 void Segment::setPermissions()
987 vm_prot_t protection
= 0;
988 if ( !this->unaccessible() ) {
989 if ( this->executable() )
990 protection
|= VM_PROT_EXECUTE
;
991 if ( this->readable() )
992 protection
|= VM_PROT_READ
;
993 if ( this->writeable() )
994 protection
|= VM_PROT_WRITE
;
996 vm_address_t addr
= this->getActualLoadAddress();
997 vm_size_t size
= this->getSize();
998 const bool setCurrentPermissions
= false;
999 kern_return_t r
= vm_protect(mach_task_self(), addr
, size
, setCurrentPermissions
, protection
);
1000 if ( r
!= KERN_SUCCESS
)
1001 throw "can't set vm permissions for mapped segment";
1004 void Segment::tempWritable()
1006 vm_address_t addr
= this->getActualLoadAddress();
1007 vm_size_t size
= this->getSize();
1008 const bool setCurrentPermissions
= false;
1009 kern_return_t r
= vm_protect(mach_task_self(), addr
, size
, setCurrentPermissions
, VM_PROT_WRITE
| VM_PROT_READ
);
1010 if ( r
!= KERN_SUCCESS
)
1011 throw "can't set vm permissions for mapped segment";
1015 bool Segment::hasTrailingZeroFill()
1017 return ( this->writeable() && (this->getSize() > this->getFileSize()) );
1021 uintptr_t Segment::reserveAnAddressRange(size_t length
, const ImageLoader::LinkContext
& context
)
1023 vm_address_t addr
= 0;
1024 vm_size_t size
= length
;
1025 if ( context
.slideAndPackDylibs
) {
1026 addr
= (fgNextNonSplitSegAddress
- length
) & (-4096); // page align
1027 kern_return_t r
= vm_allocate(mach_task_self(), &addr
, size
, false /*use this range*/);
1028 if ( r
!= KERN_SUCCESS
)
1029 throw "out of address space";
1030 fgNextNonSplitSegAddress
= addr
;
1033 kern_return_t r
= vm_allocate(mach_task_self(), &addr
, size
, true /*find range*/);
1034 if ( r
!= KERN_SUCCESS
)
1035 throw "out of address space";
1040 bool Segment::reserveAddressRange(uintptr_t start
, size_t length
)
1042 vm_address_t addr
= start
;
1043 vm_size_t size
= length
;
1044 kern_return_t r
= vm_allocate(mach_task_self(), &addr
, size
, false /*only this range*/);
1045 if ( r
!= KERN_SUCCESS
)