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 fLibrariesLoaded
= false;
510 throw strdup(buf
); // this is a leak if exception doesn't halt program
512 // ok if weak library not found
513 requiredLib
.image
= NULL
;
514 canUsePrelinkingInfo
= false; // this disables all prebinding, we may want to just slam import vectors for this lib to zero
517 fAllLibraryChecksumsAndLoadAddressesMatch
= canUsePrelinkingInfo
;
519 // tell each to load its dependents
520 for(unsigned int i
=0; i
< fLibrariesCount
; ++i
){
521 DependentLibrary
& libInfo
= fLibraries
[i
];
522 if ( libInfo
.image
!= NULL
) {
523 libInfo
.isSubFramework
= libInfo
.image
->isSubframeworkOf(context
, this);
524 libInfo
.isReExported
= libInfo
.isSubFramework
|| this->hasSubLibrary(context
, libInfo
.image
);
525 //if ( libInfo.isReExported )
526 // fprintf(stderr, "%s re-exports %s\n", strrchr(this->getPath(), '/'), strrchr(libInfo.image->getPath(),'/'));
527 libInfo
.image
->recursiveLoadLibraries(context
);
531 // do deep prebind check
532 if ( fAllLibraryChecksumsAndLoadAddressesMatch
) {
533 for(unsigned int i
=0; i
< fLibrariesCount
; ++i
){
534 const DependentLibrary
& libInfo
= fLibraries
[i
];
535 if ( libInfo
.image
!= NULL
) {
536 if ( !libInfo
.image
->allDependentLibrariesAsWhenPreBound() )
537 fAllLibraryChecksumsAndLoadAddressesMatch
= false;
545 void ImageLoader::recursiveRebase(const LinkContext
& context
)
552 // rebase lower level libraries first
553 for(unsigned int i
=0; i
< fLibrariesCount
; ++i
){
554 DependentLibrary
& libInfo
= fLibraries
[i
];
555 if ( libInfo
.image
!= NULL
)
556 libInfo
.image
->recursiveRebase(context
);
562 catch (const char* msg
) {
563 // this image is not rebased
573 void ImageLoader::recursiveBind(const LinkContext
& context
, BindingLaziness bindness
)
575 // normally just non-lazy pointers are bound up front,
576 // but DYLD_BIND_AT_LAUNCH will cause lazy pointers to be bound up from
577 // and some dyld API's bind all lazys at runtime
578 bool nonLazy
= false;
584 case kLazyAndNonLazy
:
589 case kLazyOnlyNoDependents
:
593 const bool doNonLazy
= nonLazy
&& !fBoundAllNonLazy
;
594 const bool doLazy
= lazy
&& !fBoundAllLazy
;
595 if ( doNonLazy
|| doLazy
) {
597 bool oldBoundAllNonLazy
= fBoundAllNonLazy
;
598 bool oldBoundAllLazy
= fBoundAllLazy
;
599 fBoundAllNonLazy
= fBoundAllNonLazy
|| nonLazy
;
600 fBoundAllLazy
= fBoundAllLazy
|| lazy
;
603 // bind lower level libraries first
604 if ( bindness
!= kLazyOnlyNoDependents
) {
605 for(unsigned int i
=0; i
< fLibrariesCount
; ++i
){
606 DependentLibrary
& libInfo
= fLibraries
[i
];
607 if ( libInfo
.image
!= NULL
)
608 libInfo
.image
->recursiveBind(context
, bindness
);
612 if ( doLazy
&& !doNonLazy
)
613 doBind(context
, kLazyOnly
);
614 else if ( !doLazy
&& doNonLazy
)
615 doBind(context
, kNonLazyOnly
);
617 doBind(context
, kLazyAndNonLazy
);
619 catch (const char* msg
) {
621 fBoundAllNonLazy
= oldBoundAllNonLazy
;
622 fBoundAllLazy
= oldBoundAllLazy
;
629 // This is complex because _dyld_register_func_for_add_image() is defined to not only
630 // notify you of future image loads, but also of all currently loaded images. Therefore
631 // each image needs to track that all add-image-funcs have been notified about it.
632 // Since add-image-funcs cannot be removed, each has a unique index and each image
633 // records the thru which index notificiation has already been done.
635 void ImageLoader::recursiveImageNotification(const LinkContext
& context
, uint32_t addImageCount
)
637 if ( fNextAddImageIndex
< addImageCount
) {
639 const uint32_t initIndex
= fNextAddImageIndex
;
640 fNextAddImageIndex
= addImageCount
;
642 // notify all requestors about this image
643 context
.imageNotification(this, initIndex
);
645 // notify about lower level libraries first
646 for(unsigned int i
=0; i
< fLibrariesCount
; ++i
){
647 DependentLibrary
& libInfo
= fLibraries
[i
];
648 if ( libInfo
.image
!= NULL
)
649 libInfo
.image
->recursiveImageNotification(context
, addImageCount
);
655 void ImageLoader::recursiveImageAnnouncement(const LinkContext
& context
, std::vector
<ImageLoader
*>& newImages
)
657 if ( ! fAnnounced
) {
661 // announce lower level libraries first
662 for(unsigned int i
=0; i
< fLibrariesCount
; ++i
){
663 DependentLibrary
& libInfo
= fLibraries
[i
];
664 if ( libInfo
.image
!= NULL
)
665 libInfo
.image
->recursiveImageAnnouncement(context
, newImages
);
668 // add to list of images to notify gdb about
669 newImages
.push_back(this);
670 //fprintf(stderr, "next size = %d\n", newImages.size());
672 // remember that this image wants to be notified about other images
673 if ( this->hasImageNotification() )
674 context
.addImageNeedingNotification(this);
680 void ImageLoader::recursiveInitialization(const LinkContext
& context
)
682 if ( ! fInitialized
) {
687 // initialize lower level libraries first
688 for(unsigned int i
=0; i
< fLibrariesCount
; ++i
){
689 DependentLibrary
& libInfo
= fLibraries
[i
];
690 if ( libInfo
.image
!= NULL
)
691 libInfo
.image
->recursiveInitialization(context
);
694 // record termination order
695 if ( this->needsTermination() )
696 context
.terminationRecorder(this);
698 // initialize this image
699 this->doInitialization(context
);
701 catch (const char* msg
) {
702 // this image is not initialized
703 fInitialized
= false;
709 void ImageLoader::reprebindCommit(const LinkContext
& context
, bool commit
, bool unmapOld
)
711 // do nothing on unprebound images
712 if ( ! this->isPrebindable() )
715 // do nothing if prebinding is up to date
716 if ( this->usablePrebinding(context
) )
719 // make sure we are not replacing a symlink with a file
720 char realFilePath
[PATH_MAX
];
721 if ( realpath(this->getPath(), realFilePath
) == NULL
) {
722 throwf("realpath() failed on %s, errno=%d", this->getPath(), errno
);
724 // recreate temp file name
725 char tempFilePath
[strlen(realFilePath
)+strlen("_redoprebinding")+2];
726 ImageLoader::addSuffix(realFilePath
, "_redoprebinding", tempFilePath
);
729 // all files successfully reprebound, so do swap
730 int result
= rename(tempFilePath
, realFilePath
);
732 // if there are two dylibs with the same install path, the second will fail to prebind
733 // because the _redoprebinding temp file is gone. In that case, log and go on.
734 if ( errno
== ENOENT
)
735 fprintf(stderr
, "update_prebinding: temp file missing: %s\n", tempFilePath
);
737 throwf("can't swap temporary re-prebound file: rename(%s,%s) returned errno=%d", tempFilePath
, realFilePath
, errno
);
739 else if ( unmapOld
) {
740 this->prebindUnmap(context
);
744 // something went wrong during prebinding, delete the temp files
745 unlink(tempFilePath
);
749 uint64_t ImageLoader::reprebind(const LinkContext
& context
, time_t timestamp
)
751 // do nothing on unprebound images
752 if ( ! this->isPrebindable() )
755 // do nothing if prebinding is up to date
756 if ( this->usablePrebinding(context
) ) {
757 if ( context
.verbosePrebinding
)
758 fprintf(stderr
, "dyld: no need to re-prebind: %s\n", this->getPath());
761 // recreate temp file name
762 char realFilePath
[PATH_MAX
];
763 if ( realpath(this->getPath(), realFilePath
) == NULL
) {
764 throwf("realpath() failed on %s, errno=%d", this->getPath(), errno
);
766 char tempFilePath
[strlen(realFilePath
)+strlen("_redoprebinding")+2];
767 ImageLoader::addSuffix(realFilePath
, "_redoprebinding", tempFilePath
);
769 // make copy of file and map it in
770 uint8_t* fileToPrebind
;
771 uint64_t fileToPrebindSize
;
772 uint64_t freespace
= this->copyAndMap(tempFilePath
, &fileToPrebind
, &fileToPrebindSize
);
774 // do format specific prebinding
775 this->doPrebinding(context
, timestamp
, fileToPrebind
);
777 // flush and swap files
778 int result
= msync(fileToPrebind
, fileToPrebindSize
, MS_ASYNC
);
780 throw "error syncing re-prebound file";
781 result
= munmap(fileToPrebind
, fileToPrebindSize
);
783 throw "error unmapping re-prebound file";
786 if ( context
.verbosePrebinding
)
787 fprintf(stderr
, "dyld: re-prebound: %p %s\n", this->machHeader(), this->getPath());
792 uint64_t ImageLoader::copyAndMap(const char* tempFile
, uint8_t** fileToPrebind
, uint64_t* fileToPrebindSize
)
795 int src
= open(this->getPath(), O_RDONLY
);
797 throw "can't open image";
798 struct stat stat_buf
;
799 if ( fstat(src
, &stat_buf
) == -1)
800 throw "can't stat image";
801 if ( stat_buf
.st_mtime
!= fLastModified
)
802 throw "image file changed since it was loaded";
804 // create new file with all same permissions to hold copy of dylib
806 int dst
= open(tempFile
, O_CREAT
| O_RDWR
| O_TRUNC
, stat_buf
.st_mode
);
808 throw "can't create temp image";
810 // mark source as "don't cache"
811 (void)fcntl(src
, F_NOCACHE
, 1);
812 // we want to cache the dst because we are about to map it in and modify it
814 // copy permission bits
815 if ( chmod(tempFile
, stat_buf
.st_mode
& 07777) == -1 )
816 throwf("can't chmod temp image. errno=%d for %s", errno
, this->getPath());
817 if ( chown(tempFile
, stat_buf
.st_uid
, stat_buf
.st_gid
) == -1)
818 throwf("can't chown temp image. errno=%d for %s", errno
, this->getPath());
822 const uint32_t kBufferSize
= 128*1024;
823 static uint8_t* buffer
= NULL
;
824 if ( buffer
== NULL
) {
825 vm_address_t addr
= 0;
826 if ( vm_allocate(mach_task_self(), &addr
, kBufferSize
, true /*find range*/) == KERN_SUCCESS
)
827 buffer
= (uint8_t*)addr
;
829 throw "can't allcoate copy buffer";
831 while ( (len
= read(src
, buffer
, kBufferSize
)) > 0 ) {
832 if ( write(dst
, buffer
, len
) == -1 )
833 throwf("write failure copying dylib errno=%d for %s", errno
, this->getPath());
837 *fileToPrebindSize
= stat_buf
.st_size
- fOffsetInFatFile
; // this may map in too much, but it does not matter
838 *fileToPrebind
= (uint8_t*)mmap(NULL
, *fileToPrebindSize
, PROT_READ
| PROT_WRITE
, MAP_FILE
| MAP_SHARED
, dst
, fOffsetInFatFile
);
839 if ( *fileToPrebind
== (uint8_t*)(-1) )
840 throw "can't mmap temp image";
842 // get free space remaining on dst volume
843 struct statfs statfs_buf
;
844 if ( fstatfs(dst
, &statfs_buf
) != 0 )
845 throwf("can't fstatfs(), errno=%d for %s", errno
, tempFile
);
846 uint64_t freespace
= (uint64_t)statfs_buf
.f_bavail
* (uint64_t)statfs_buf
.f_bsize
;
849 // ok to close file after mapped in
850 // ok to throw above without closing file because the throw will terminate update_prebinding
851 int result1
= close(dst
);
852 int result2
= close(src
);
853 if ( (result1
!= 0) || (result2
!= 0) )
854 throw "can't close file";
860 static void printTime(const char* msg
, uint64_t partTime
, uint64_t totalTime
)
862 static uint64_t sUnitsPerSecond
= 0;
863 if ( sUnitsPerSecond
== 0 ) {
864 struct mach_timebase_info timeBaseInfo
;
865 if ( mach_timebase_info(&timeBaseInfo
) == KERN_SUCCESS
) {
866 sUnitsPerSecond
= timeBaseInfo
.denom
;
869 if ( partTime
< sUnitsPerSecond
) {
870 uint32_t milliSecondsTimeTen
= (partTime
*10000)/sUnitsPerSecond
;
871 uint32_t milliSeconds
= milliSecondsTimeTen
/10;
872 uint32_t percentTimesTen
= (partTime
*1000)/totalTime
;
873 uint32_t percent
= percentTimesTen
/10;
874 fprintf(stderr
, "%s: %u.%u milliseconds (%u.%u%%)\n", msg
, milliSeconds
, milliSecondsTimeTen
-milliSeconds
*10, percent
, percentTimesTen
-percent
*10);
877 uint32_t secondsTimeTen
= (partTime
*10)/sUnitsPerSecond
;
878 uint32_t seconds
= secondsTimeTen
/100;
879 uint32_t percentTimesTen
= (partTime
*1000)/totalTime
;
880 uint32_t percent
= percentTimesTen
/10;
881 fprintf(stderr
, "%s: %u.%u seconds (%u.%u%%)\n", msg
, seconds
, secondsTimeTen
-seconds
*10, percent
, percentTimesTen
-percent
*10);
885 static char* commatize(uint64_t in
, char* out
)
889 sprintf(rawNum
, "%llu", in
);
890 const int rawNumLen
= strlen(rawNum
);
891 for(int i
=0; i
< rawNumLen
-1; ++i
) {
893 if ( ((rawNumLen
-i
) % 3) == 1 )
896 *out
++ = rawNum
[rawNumLen
-1];
902 void ImageLoader::printStatistics(unsigned int imageCount
)
904 uint64_t totalTime
= fgTotalLoadLibrariesTime
+ fgTotalRebaseTime
+ fgTotalBindTime
+ fgTotalNotifyTime
+ fgTotalInitTime
;
908 printTime("total time", totalTime
, totalTime
);
909 fprintf(stderr
, "total images loaded: %d (%d used prebinding)\n", imageCount
, fgImagesWithUsedPrebinding
);
910 printTime("total images loading time", fgTotalLoadLibrariesTime
, totalTime
);
911 fprintf(stderr
, "total rebase fixups: %s\n", commatize(fgTotalRebaseFixups
, commaNum1
));
912 printTime("total rebase fixups time", fgTotalRebaseFixups
, totalTime
);
913 fprintf(stderr
, "total binding fixups: %s\n", commatize(fgTotalBindFixups
, commaNum1
));
914 printTime("total binding fixups time", fgTotalBindTime
, totalTime
);
915 fprintf(stderr
, "total bindings lazily fixed up: %s of %s\n", commatize(fgTotalLazyBindFixups
, commaNum1
), commatize(fgTotalPossibleLazyBindFixups
, commaNum2
));
916 printTime("total notify time time", fgTotalNotifyTime
, totalTime
);
917 printTime("total init time time", fgTotalInitTime
, totalTime
);
922 // copy path and add suffix to result
924 // /path/foo.dylib _debug => /path/foo_debug.dylib
925 // foo.dylib _debug => foo_debug.dylib
926 // foo _debug => foo_debug
927 // /path/bar _debug => /path/bar_debug
928 // /path/bar.A.dylib _debug => /path/bar.A_debug.dylib
930 void ImageLoader::addSuffix(const char* path
, const char* suffix
, char* result
)
932 strcpy(result
, path
);
934 char* start
= strrchr(result
, '/');
940 char* dot
= strrchr(start
, '.');
943 strcat(&dot
[strlen(suffix
)], &path
[dot
-result
]);
946 strcat(result
, suffix
);
951 void Segment::map(int fd
, uint64_t offsetInFatWrapper
, intptr_t slide
, const ImageLoader::LinkContext
& context
)
953 vm_offset_t fileOffset
= this->getFileOffset() + offsetInFatWrapper
;
954 vm_size_t size
= this->getFileSize();
955 void* requestedLoadAddress
= (void*)(this->getPreferredLoadAddress() + slide
);
957 if ( !this->unaccessible() ) {
958 if ( this->executable() )
959 protection
|= PROT_EXEC
;
960 if ( this->readable() )
961 protection
|= PROT_READ
;
962 if ( this->writeable() )
963 protection
|= PROT_WRITE
;
965 void* loadAddress
= mmap(requestedLoadAddress
, size
, protection
, MAP_FILE
| MAP_FIXED
| MAP_PRIVATE
, fd
, fileOffset
);
966 if ( loadAddress
== ((void*)(-1)) )
967 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());
969 if ( context
.verboseMapping
)
970 fprintf(stderr
, "%18s at %p->%p\n", this->getName(), loadAddress
, (char*)loadAddress
+this->getFileSize()-1);
973 void Segment::map(const void* memoryImage
, intptr_t slide
, const ImageLoader::LinkContext
& context
)
975 vm_address_t loadAddress
= this->getPreferredLoadAddress() + slide
;
976 vm_address_t srcAddr
= (uintptr_t)memoryImage
+ this->getFileOffset();
977 vm_size_t size
= this->getFileSize();
978 kern_return_t r
= vm_copy(mach_task_self(), srcAddr
, size
, loadAddress
);
979 if ( r
!= KERN_SUCCESS
)
980 throw "can't map segment";
982 if ( context
.verboseMapping
)
983 fprintf(stderr
, "%18s at %p->%p\n", this->getName(), (char*)loadAddress
, (char*)loadAddress
+this->getFileSize()-1);
986 void Segment::setPermissions()
988 vm_prot_t protection
= 0;
989 if ( !this->unaccessible() ) {
990 if ( this->executable() )
991 protection
|= VM_PROT_EXECUTE
;
992 if ( this->readable() )
993 protection
|= VM_PROT_READ
;
994 if ( this->writeable() )
995 protection
|= VM_PROT_WRITE
;
997 vm_address_t addr
= this->getActualLoadAddress();
998 vm_size_t size
= this->getSize();
999 const bool setCurrentPermissions
= false;
1000 kern_return_t r
= vm_protect(mach_task_self(), addr
, size
, setCurrentPermissions
, protection
);
1001 if ( r
!= KERN_SUCCESS
)
1002 throw "can't set vm permissions for mapped segment";
1005 void Segment::tempWritable()
1007 vm_address_t addr
= this->getActualLoadAddress();
1008 vm_size_t size
= this->getSize();
1009 const bool setCurrentPermissions
= false;
1010 kern_return_t r
= vm_protect(mach_task_self(), addr
, size
, setCurrentPermissions
, VM_PROT_WRITE
| VM_PROT_READ
);
1011 if ( r
!= KERN_SUCCESS
)
1012 throw "can't set vm permissions for mapped segment";
1016 bool Segment::hasTrailingZeroFill()
1018 return ( this->writeable() && (this->getSize() > this->getFileSize()) );
1022 uintptr_t Segment::reserveAnAddressRange(size_t length
, const ImageLoader::LinkContext
& context
)
1024 vm_address_t addr
= 0;
1025 vm_size_t size
= length
;
1026 if ( context
.slideAndPackDylibs
) {
1027 addr
= (fgNextNonSplitSegAddress
- length
) & (-4096); // page align
1028 kern_return_t r
= vm_allocate(mach_task_self(), &addr
, size
, false /*use this range*/);
1029 if ( r
!= KERN_SUCCESS
)
1030 throw "out of address space";
1031 fgNextNonSplitSegAddress
= addr
;
1034 kern_return_t r
= vm_allocate(mach_task_self(), &addr
, size
, true /*find range*/);
1035 if ( r
!= KERN_SUCCESS
)
1036 throw "out of address space";
1041 bool Segment::reserveAddressRange(uintptr_t start
, size_t length
)
1043 vm_address_t addr
= start
;
1044 vm_size_t size
= length
;
1045 kern_return_t r
= vm_allocate(mach_task_self(), &addr
, size
, false /*only this range*/);
1046 if ( r
!= KERN_SUCCESS
)