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>
35 #include "ImageLoader.h"
38 uint32_t ImageLoader::fgImagesWithUsedPrebinding
= 0;
39 uint32_t ImageLoader::fgTotalRebaseFixups
= 0;
40 uint32_t ImageLoader::fgTotalBindFixups
= 0;
41 uint32_t ImageLoader::fgTotalLazyBindFixups
= 0;
42 uint32_t ImageLoader::fgTotalPossibleLazyBindFixups
= 0;
43 uint64_t ImageLoader::fgTotalLoadLibrariesTime
;
44 uint64_t ImageLoader::fgTotalRebaseTime
;
45 uint64_t ImageLoader::fgTotalBindTime
;
46 uint64_t ImageLoader::fgTotalNotifyTime
;
47 uint64_t ImageLoader::fgTotalInitTime
;
48 uintptr_t ImageLoader::fgNextSplitSegAddress
= 0x90000000;
49 uintptr_t Segment::fgNextNonSplitSegAddress
= 0x8FE00000;
53 __attribute__((noreturn
))
54 void throwf(const char* format
, ...)
58 va_start(list
, format
);
59 vasprintf(&p
, format
, list
);
66 void ImageLoader::init(const char* path
, uint64_t offsetInFat
, dev_t device
, ino_t inode
, time_t modDate
)
75 fLastModified
= modDate
;
76 fOffsetInFatFile
= offsetInFat
;
81 fAllLibraryChecksumsAndLoadAddressesMatch
= false;
84 fMatchByInstallName
= false;
85 fLibrariesLoaded
= false;
87 fBoundAllNonLazy
= false;
88 fBoundAllLazy
= false;
91 fNextAddImageIndex
= 0;
95 ImageLoader::ImageLoader(const char* path
, uint64_t offsetInFat
, const struct stat
& info
)
97 init(path
, offsetInFat
, info
.st_dev
, info
.st_ino
, info
.st_mtime
);
100 ImageLoader::ImageLoader(const char* moduleName
)
102 init(moduleName
, 0, 0, 0, 0);
106 ImageLoader::~ImageLoader()
108 // need to read up on STL and see if this is right way to destruct vector contents
109 const unsigned int segmentCount
= fSegments
.size();
110 for(unsigned int i
=0; i
< segmentCount
; ++i
){
111 Segment
* seg
= fSegments
[i
];
116 if ( fLogicalPath
!= NULL
)
117 delete [] fLogicalPath
;
121 void ImageLoader::setPath(const char* path
)
123 if ( fPath
!= NULL
) {
124 // if duplicate path, do nothing
125 if ( strcmp(path
, fPath
) == 0 )
129 fPath
= new char[strlen(path
)+1];
130 strcpy((char*)fPath
, path
);
131 fPathHash
= hash(fPath
);
134 void ImageLoader::setLogicalPath(const char* path
)
136 if ( fPath
== NULL
) {
137 // no physical path set yet, so use this path as physical
140 else if ( strcmp(path
, fPath
) == 0 ) {
141 // do not set logical path because it is the same as the physical path
145 fLogicalPath
= new char[strlen(path
)+1];
146 strcpy((char*)fLogicalPath
, path
);
150 const char* ImageLoader::getLogicalPath() const
152 if ( fLogicalPath
!= NULL
)
158 uint32_t ImageLoader::hash(const char* path
)
160 // this does not need to be a great hash
161 // it is just used to reduce the number of strcmp() calls
162 // of existing images when loading a new image
164 for (const char* s
=path
; *s
!= '\0'; ++s
)
169 bool ImageLoader::matchInstallPath() const
171 return fMatchByInstallName
;
174 void ImageLoader::setMatchInstallPath(bool match
)
176 fMatchByInstallName
= match
;
179 bool ImageLoader::statMatch(const struct stat
& stat_buf
) const
181 return ( (this->fDevice
== stat_buf
.st_dev
) && (this->fInode
== stat_buf
.st_ino
) );
184 const char* ImageLoader::getShortName() const
186 // try to return leaf name
187 if ( fPath
!= NULL
) {
188 const char* s
= strrchr(fPath
, '/');
195 uint64_t ImageLoader::getOffsetInFatFile() const
197 return fOffsetInFatFile
;
200 void ImageLoader::setLeaveMapped()
203 const unsigned int segmentCount
= fSegments
.size();
204 for(unsigned int i
=0; i
< segmentCount
; ++i
){
205 fSegments
[i
]->setUnMapWhenDestructed(false);
209 void ImageLoader::setHideExports(bool hide
)
214 bool ImageLoader::hasHiddenExports() const
219 bool ImageLoader::isLinked() const
221 return fBoundAllNonLazy
;
224 time_t ImageLoader::lastModified()
226 return fLastModified
;
229 bool ImageLoader::containsAddress(const void* addr
) const
231 const unsigned int segmentCount
= fSegments
.size();
232 for(unsigned int i
=0; i
< segmentCount
; ++i
){
233 Segment
* seg
= fSegments
[i
];
234 const uint8_t* start
= (const uint8_t*)seg
->getActualLoadAddress();
235 const uint8_t* end
= start
+ seg
->getSize();
236 if ( (start
<= addr
) && (addr
< end
) && !seg
->unaccessible() )
242 void ImageLoader::addMappedRegions(RegionsVector
& regions
) const
244 const unsigned int segmentCount
= fSegments
.size();
245 for(unsigned int i
=0; i
< segmentCount
; ++i
){
246 Segment
* seg
= fSegments
[i
];
248 region
.address
= seg
->getActualLoadAddress();
249 region
.size
= seg
->getSize();
250 regions
.push_back(region
);
255 void ImageLoader::incrementReferenceCount()
260 bool ImageLoader::decrementReferenceCount()
262 return ( --fReferenceCount
== 0 );
266 const ImageLoader::Symbol
* ImageLoader::resolveSymbol(const char* name
, bool searchSelf
, ImageLoader
** foundIn
) const
268 const ImageLoader::Symbol
* sym
;
271 sym
= this->findExportedSymbol(name
, NULL
, false, foundIn
);
276 // search directly dependent libraries
277 for (uint32_t i
=0; i
< fLibrariesCount
; ++i
) {
278 ImageLoader
* dependentImage
= fLibraries
[i
].image
;
279 if ( dependentImage
!= NULL
) {
280 const ImageLoader::Symbol
* sym
= dependentImage
->findExportedSymbol(name
, NULL
, false, foundIn
);
286 // search indirectly dependent libraries
287 for (uint32_t i
=0; i
< fLibrariesCount
; ++i
) {
288 ImageLoader
* dependentImage
= fLibraries
[i
].image
;
289 if ( dependentImage
!= NULL
) {
290 const ImageLoader::Symbol
* sym
= dependentImage
->resolveSymbol(name
, false, foundIn
);
301 void ImageLoader::link(const LinkContext
& context
, BindingLaziness bindness
, InitializerRunning inits
, uint32_t notifyCount
)
303 uint64_t t1
= mach_absolute_time();
304 this->recursiveLoadLibraries(context
);
306 uint64_t t2
= mach_absolute_time();
307 this->recursiveRebase(context
);
309 uint64_t t3
= mach_absolute_time();
310 this->recursiveBind(context
, bindness
);
312 uint64_t t4
= mach_absolute_time();
313 this->recursiveImageNotification(context
, notifyCount
);
315 if ( (inits
== kRunInitializers
) || (inits
== kDontRunInitializersButTellObjc
) ) {
316 std::vector
<ImageLoader
*> newImages
;
317 this->recursiveImageAnnouncement(context
, newImages
); // build bottom up list images being added
318 context
.notifyAdding(newImages
); // tell gdb or anyone who cares about these
321 uint64_t t5
= mach_absolute_time();
322 if ( inits
== kRunInitializers
) {
323 this->recursiveInitialization(context
);
324 uint64_t t6
= mach_absolute_time();
325 fgTotalInitTime
+= t6
- t5
;
327 fgTotalLoadLibrariesTime
+= t2
- t1
;
328 fgTotalRebaseTime
+= t3
- t2
;
329 fgTotalBindTime
+= t4
- t3
;
330 fgTotalNotifyTime
+= t5
- t4
;
334 // only called pre-main on main executable
335 // if crt.c is ever cleaned up, this could go away
336 void ImageLoader::runInitializers(const LinkContext
& context
)
338 std::vector
<ImageLoader
*> newImages
;
339 this->recursiveImageAnnouncement(context
, newImages
); // build bottom up list images being added
340 context
.notifyAdding(newImages
); // tell gdb or anyone who cares about these
342 this->recursiveInitialization(context
);
345 // called inside _dyld_register_func_for_add_image()
346 void ImageLoader::runNotification(const LinkContext
& context
, uint32_t notifyCount
)
348 this->recursiveImageNotification(context
, notifyCount
);
352 intptr_t ImageLoader::assignSegmentAddresses(const LinkContext
& context
)
354 // preflight and calculate slide if needed
355 const unsigned int segmentCount
= fSegments
.size();
357 if ( this->segmentsCanSlide() && this->segmentsMustSlideTogether() ) {
358 bool needsToSlide
= false;
359 uintptr_t lowAddr
= UINTPTR_MAX
;
360 uintptr_t highAddr
= 0;
361 for(unsigned int i
=0; i
< segmentCount
; ++i
){
362 Segment
* seg
= fSegments
[i
];
363 const uintptr_t segLow
= seg
->getPreferredLoadAddress();
364 const uintptr_t segHigh
= segLow
+ seg
->getSize();
365 if ( segLow
< lowAddr
)
367 if ( segHigh
> highAddr
)
370 if ( context
.slideAndPackDylibs
|| !seg
->hasPreferredLoadAddress() || !Segment::reserveAddressRange(seg
->getPreferredLoadAddress(), seg
->getSize()) )
373 if ( needsToSlide
) {
374 // find a chunk of address space to hold all segments
375 uintptr_t addr
= Segment::reserveAnAddressRange(highAddr
-lowAddr
, context
);
376 slide
= addr
- lowAddr
;
379 else if ( ! this->segmentsCanSlide() ) {
380 for(unsigned int i
=0; i
< segmentCount
; ++i
){
381 Segment
* seg
= fSegments
[i
];
382 if ( strcmp(seg
->getName(), "__PAGEZERO") == 0 )
384 if ( !Segment::reserveAddressRange(seg
->getPreferredLoadAddress(), seg
->getSize()) )
389 // mach-o does not support independently sliding segments
395 void ImageLoader::mapSegments(int fd
, uint64_t offsetInFat
, uint64_t lenInFat
, uint64_t fileLen
, const LinkContext
& context
)
397 if ( context
.verboseMapping
)
398 fprintf(stderr
, "dyld: Mapping %s\n", this->getPath());
399 // find address range for image
400 intptr_t slide
= this->assignSegmentAddresses(context
);
401 // map in all segments
402 const unsigned int segmentCount
= fSegments
.size();
403 for(unsigned int i
=0; i
< segmentCount
; ++i
){
404 Segment
* seg
= fSegments
[i
];
405 seg
->map(fd
, offsetInFat
, slide
, context
);
407 // update slide to reflect load location
408 this->setSlide(slide
);
410 // now that it is mapped and slide is set, mark that we should unmap it when done
411 for(unsigned int i
=0; i
< segmentCount
; ++i
){
412 fSegments
[i
]->setUnMapWhenDestructed(true);
416 void ImageLoader::mapSegments(const void* memoryImage
, uint64_t imageLen
, const LinkContext
& context
)
418 if ( context
.verboseMapping
)
419 fprintf(stderr
, "dyld: Mapping memory %p\n", memoryImage
);
420 // find address range for image
421 intptr_t slide
= this->assignSegmentAddresses(context
);
422 // map in all segments
423 const unsigned int segmentCount
= fSegments
.size();
424 for(unsigned int i
=0; i
< segmentCount
; ++i
){
425 Segment
* seg
= fSegments
[i
];
426 seg
->map(memoryImage
, slide
, context
);
428 // update slide to reflect load location
429 this->setSlide(slide
);
430 // set R/W permissions on all segments at slide location
431 for(unsigned int i
=0; i
< segmentCount
; ++i
){
432 Segment
* seg
= fSegments
[i
];
433 seg
->setPermissions();
437 bool ImageLoader::allDependentLibrariesAsWhenPreBound() const
439 return fAllLibraryChecksumsAndLoadAddressesMatch
;
443 void ImageLoader::recursiveLoadLibraries(const LinkContext
& context
)
445 if ( ! fLibrariesLoaded
) {
447 fLibrariesLoaded
= true;
449 // get list of libraries this image needs
450 fLibrariesCount
= this->doGetDependentLibraryCount();
451 fLibraries
= new DependentLibrary
[fLibrariesCount
];
452 this->doGetDependentLibraries(fLibraries
);
455 bool canUsePrelinkingInfo
= true;
456 for(unsigned int i
=0; i
< fLibrariesCount
; ++i
){
457 DependentLibrary
& requiredLib
= fLibraries
[i
];
459 requiredLib
.image
= context
.loadLibrary(requiredLib
.name
, true, this->getPath(), NULL
);
460 if ( requiredLib
.image
== this ) {
461 // found circular reference, perhaps DYLD_LIBARY_PATH is causing this rdar://problem/3684168
462 requiredLib
.image
= context
.loadLibrary(requiredLib
.name
, false, NULL
, NULL
);
463 if ( requiredLib
.image
!= this )
464 fprintf(stderr
, "dyld: warning DYLD_ setting caused circular dependency in %s\n", this->getPath());
466 LibraryInfo actualInfo
= requiredLib
.image
->doGetLibraryInfo();
467 requiredLib
.checksumMatches
= ( actualInfo
.checksum
== requiredLib
.info
.checksum
);
468 // check found library version is compatible
469 if ( actualInfo
.minVersion
< requiredLib
.info
.minVersion
) {
470 throwf("Incompatible library version: %s requires version %d.%d.%d or later, but %s provides version %d.%d.%d",
471 this->getShortName(), requiredLib
.info
.minVersion
>> 16, (requiredLib
.info
.minVersion
>> 8) & 0xff, requiredLib
.info
.minVersion
& 0xff,
472 requiredLib
.image
->getShortName(), actualInfo
.minVersion
>> 16, (actualInfo
.minVersion
>> 8) & 0xff, actualInfo
.minVersion
& 0xff);
474 // prebinding for this image disabled if any dependent library changed or slid
475 if ( !requiredLib
.checksumMatches
|| (requiredLib
.image
->getSlide() != 0) )
476 canUsePrelinkingInfo
= false;
477 //if ( context.verbosePrebinding ) {
478 // if ( !requiredLib.checksumMatches )
479 // fprintf(stderr, "dyld: checksum mismatch, (%lld v %lld) for %s referencing %s\n", requiredLib.info.checksum, actualInfo.checksum, this->getPath(), requiredLib.image->getPath());
480 // if ( requiredLib.image->getSlide() != 0 )
481 // fprintf(stderr, "dyld: dependent library slid for %s referencing %s\n", this->getPath(), requiredLib.image->getPath());
484 catch (const char* msg
) {
485 //if ( context.verbosePrebinding )
486 // fprintf(stderr, "dyld: exception during processing for %s referencing %s\n", this->getPath(), requiredLib.image->getPath());
487 if ( requiredLib
.required
) {
488 const char* formatString
= "Library not loaded: %s\n Referenced from: %s\n Reason: %s";
489 const char* referencedFrom
= this->getPath();
490 char buf
[strlen(requiredLib
.name
)+strlen(referencedFrom
)+strlen(formatString
)+strlen(msg
)+2];
491 sprintf(buf
, formatString
, requiredLib
.name
, referencedFrom
, msg
);
492 throw strdup(buf
); // this is a leak if exception doesn't halt program
494 // ok if weak library not found
495 requiredLib
.image
= NULL
;
496 canUsePrelinkingInfo
= false; // this disables all prebinding, we may want to just slam import vectors for this lib to zero
499 fAllLibraryChecksumsAndLoadAddressesMatch
= canUsePrelinkingInfo
;
501 // tell each to load its dependents
502 for(unsigned int i
=0; i
< fLibrariesCount
; ++i
){
503 DependentLibrary
& libInfo
= fLibraries
[i
];
504 if ( libInfo
.image
!= NULL
) {
505 libInfo
.isSubFramework
= libInfo
.image
->isSubframeworkOf(context
, this);
506 libInfo
.isReExported
= libInfo
.isSubFramework
|| this->hasSubLibrary(context
, libInfo
.image
);
507 //if ( libInfo.isReExported )
508 // fprintf(stderr, "%s re-exports %s\n", strrchr(this->getPath(), '/'), strrchr(libInfo.image->getPath(),'/'));
509 libInfo
.image
->recursiveLoadLibraries(context
);
513 // do deep prebind check
514 if ( fAllLibraryChecksumsAndLoadAddressesMatch
) {
515 for(unsigned int i
=0; i
< fLibrariesCount
; ++i
){
516 const DependentLibrary
& libInfo
= fLibraries
[i
];
517 if ( libInfo
.image
!= NULL
) {
518 if ( !libInfo
.image
->allDependentLibrariesAsWhenPreBound() )
519 fAllLibraryChecksumsAndLoadAddressesMatch
= false;
527 void ImageLoader::recursiveRebase(const LinkContext
& context
)
534 // rebase lower level libraries first
535 for(unsigned int i
=0; i
< fLibrariesCount
; ++i
){
536 DependentLibrary
& libInfo
= fLibraries
[i
];
537 if ( libInfo
.image
!= NULL
)
538 libInfo
.image
->recursiveRebase(context
);
544 catch (const char* msg
) {
545 // this image is not rebased
555 void ImageLoader::recursiveBind(const LinkContext
& context
, BindingLaziness bindness
)
557 // normally just non-lazy pointers are bound up front,
558 // but DYLD_BIND_AT_LAUNCH will cause lazy pointers to be bound up from
559 // and some dyld API's bind all lazys at runtime
560 bool nonLazy
= false;
566 case kLazyAndNonLazy
:
571 case kLazyOnlyNoDependents
:
575 const bool doNonLazy
= nonLazy
&& !fBoundAllNonLazy
;
576 const bool doLazy
= lazy
&& !fBoundAllLazy
;
577 if ( doNonLazy
|| doLazy
) {
579 bool oldBoundAllNonLazy
= fBoundAllNonLazy
;
580 bool oldBoundAllLazy
= fBoundAllLazy
;
581 fBoundAllNonLazy
= fBoundAllNonLazy
|| nonLazy
;
582 fBoundAllLazy
= fBoundAllLazy
|| lazy
;
585 // bind lower level libraries first
586 if ( bindness
!= kLazyOnlyNoDependents
) {
587 for(unsigned int i
=0; i
< fLibrariesCount
; ++i
){
588 DependentLibrary
& libInfo
= fLibraries
[i
];
589 if ( libInfo
.image
!= NULL
)
590 libInfo
.image
->recursiveBind(context
, bindness
);
594 if ( doLazy
&& !doNonLazy
)
595 doBind(context
, kLazyOnly
);
596 else if ( !doLazy
&& doNonLazy
)
597 doBind(context
, kNonLazyOnly
);
599 doBind(context
, kLazyAndNonLazy
);
601 catch (const char* msg
) {
603 fBoundAllNonLazy
= oldBoundAllNonLazy
;
604 fBoundAllLazy
= oldBoundAllLazy
;
611 // This is complex because _dyld_register_func_for_add_image() is defined to not only
612 // notify you of future image loads, but also of all currently loaded images. Therefore
613 // each image needs to track that all add-image-funcs have been notified about it.
614 // Since add-image-funcs cannot be removed, each has a unique index and each image
615 // records the thru which index notificiation has already been done.
617 void ImageLoader::recursiveImageNotification(const LinkContext
& context
, uint32_t addImageCount
)
619 if ( fNextAddImageIndex
< addImageCount
) {
621 const uint32_t initIndex
= fNextAddImageIndex
;
622 fNextAddImageIndex
= addImageCount
;
624 // notify all requestors about this image
625 context
.imageNotification(this, initIndex
);
627 // notify about lower level libraries first
628 for(unsigned int i
=0; i
< fLibrariesCount
; ++i
){
629 DependentLibrary
& libInfo
= fLibraries
[i
];
630 if ( libInfo
.image
!= NULL
)
631 libInfo
.image
->recursiveImageNotification(context
, addImageCount
);
637 void ImageLoader::recursiveImageAnnouncement(const LinkContext
& context
, std::vector
<ImageLoader
*>& newImages
)
639 if ( ! fAnnounced
) {
643 // announce lower level libraries first
644 for(unsigned int i
=0; i
< fLibrariesCount
; ++i
){
645 DependentLibrary
& libInfo
= fLibraries
[i
];
646 if ( libInfo
.image
!= NULL
)
647 libInfo
.image
->recursiveImageAnnouncement(context
, newImages
);
650 // add to list of images to notify gdb about
651 newImages
.push_back(this);
652 //fprintf(stderr, "next size = %d\n", newImages.size());
654 // remember that this image wants to be notified about other images
655 if ( this->hasImageNotification() )
656 context
.addImageNeedingNotification(this);
662 void ImageLoader::recursiveInitialization(const LinkContext
& context
)
664 if ( ! fInitialized
) {
669 // initialize lower level libraries first
670 for(unsigned int i
=0; i
< fLibrariesCount
; ++i
){
671 DependentLibrary
& libInfo
= fLibraries
[i
];
672 if ( libInfo
.image
!= NULL
)
673 libInfo
.image
->recursiveInitialization(context
);
676 // record termination order
677 if ( this->needsTermination() )
678 context
.terminationRecorder(this);
680 // initialize this image
681 this->doInitialization(context
);
683 catch (const char* msg
) {
684 // this image is not initialized
685 fInitialized
= false;
691 void ImageLoader::reprebindCommit(const LinkContext
& context
, bool commit
)
693 // do nothing on unprebound images
694 if ( ! this->isPrebindable() )
697 // do nothing if prebinding is up to date
698 if ( this->usablePrebinding(context
) )
701 // make sure we are not replacing a symlink with a file
702 char realFilePath
[PATH_MAX
];
703 if ( realpath(this->getPath(), realFilePath
) == NULL
) {
704 throwf("realpath() failed on %s, errno=%d", this->getPath(), errno
);
706 // recreate temp file name
707 char tempFilePath
[PATH_MAX
];
708 ImageLoader::addSuffix(realFilePath
, "_redoprebinding", tempFilePath
);
711 // all files successfully reprebound, so do swap
712 int result
= rename(tempFilePath
, realFilePath
);
714 // if there are two dylibs with the same install path, the second will fail to prebind
715 // because the _redoprebinding temp file is gone. In that case, log and go on.
716 if ( errno
== ENOENT
)
717 fprintf(stderr
, "update_prebinding: temp file missing: %s\n", tempFilePath
);
719 throwf("can't swap temporary re-prebound file: rename(%s,%s) returned errno=%d", tempFilePath
, realFilePath
, errno
);
723 // something went wrong during prebinding, delete the temp files
724 unlink(tempFilePath
);
728 void ImageLoader::reprebind(const LinkContext
& context
, time_t timestamp
)
730 // do nothing on unprebound images
731 if ( ! this->isPrebindable() )
734 // do nothing if prebinding is up to date
735 if ( this->usablePrebinding(context
) ) {
736 if ( context
.verbosePrebinding
)
737 fprintf(stderr
, "dyld: no need to re-prebind: %s\n", this->getPath());
740 // make copy of file and map it in
741 char tempFilePath
[PATH_MAX
];
742 realpath(this->getPath(), tempFilePath
);
743 ImageLoader::addSuffix(this->getPath(), "_redoprebinding", tempFilePath
);
744 uint8_t* fileToPrebind
;
745 uint64_t fileToPrebindSize
;
746 this->copyAndMap(tempFilePath
, &fileToPrebind
, &fileToPrebindSize
);
748 // do format specific prebinding
749 this->doPrebinding(context
, timestamp
, fileToPrebind
);
751 // flush and swap files
752 int result
= msync(fileToPrebind
, fileToPrebindSize
, MS_ASYNC
);
754 throw "error syncing re-prebound file";
755 result
= munmap(fileToPrebind
, fileToPrebindSize
);
757 throw "error unmapping re-prebound file";
760 if ( context
.verbosePrebinding
)
761 fprintf(stderr
, "dyld: re-prebound: %s\n", this->getPath());
764 void ImageLoader::copyAndMap(const char* tempFile
, uint8_t** fileToPrebind
, uint64_t* fileToPrebindSize
)
767 int src
= open(this->getPath(), O_RDONLY
);
769 throw "can't open image";
770 struct stat stat_buf
;
771 if ( fstat(src
, &stat_buf
) == -1)
772 throw "can't stat image";
773 if ( stat_buf
.st_mtime
!= fLastModified
)
774 throw "image file changed since it was loaded";
776 // create new file with all same permissions to hold copy of dylib
778 int dst
= open(tempFile
, O_CREAT
| O_RDWR
| O_TRUNC
, stat_buf
.st_mode
);
780 throw "can't create temp image";
782 // mark source as "don't cache"
783 (void)fcntl(src
, F_NOCACHE
, 1);
784 // we want to cache the dst because we are about to map it in and modify it
786 // copy permission bits
787 if ( chmod(tempFile
, stat_buf
.st_mode
& 07777) == -1 )
788 throw "can't chmod temp image";
789 if ( chown(tempFile
, stat_buf
.st_uid
, stat_buf
.st_gid
) == -1)
790 throw "can't chown temp image";
794 const uint32_t kBufferSize
= 128*1024;
795 static uint8_t* buffer
= NULL
;
796 if ( buffer
== NULL
) {
797 vm_address_t addr
= 0;
798 if ( vm_allocate(mach_task_self(), &addr
, kBufferSize
, true /*find range*/) == KERN_SUCCESS
)
799 buffer
= (uint8_t*)addr
;
801 throw "can't allcoate copy buffer";
803 while ( (len
= read(src
, buffer
, kBufferSize
)) > 0 ) {
804 write(dst
, buffer
, len
);
808 *fileToPrebindSize
= stat_buf
.st_size
- fOffsetInFatFile
; // this may map in too much, but it does not matter
809 *fileToPrebind
= (uint8_t*)mmap(NULL
, *fileToPrebindSize
, PROT_READ
| PROT_WRITE
, MAP_FILE
| MAP_SHARED
, dst
, fOffsetInFatFile
);
810 if ( *fileToPrebind
== (uint8_t*)(-1) )
811 throw "can't mmap temp image";
814 // ok to close file after mapped in
815 // ok to throw above without closing file because the throw will terminate update_prebinding
816 int result1
= close(dst
);
817 int result2
= close(src
);
818 if ( (result1
!= 0) || (result2
!= 0) )
819 throw "can't close file";
823 static void printTime(const char* msg
, uint64_t partTime
, uint64_t totalTime
)
825 static uint64_t sUnitsPerSecond
= 0;
826 if ( sUnitsPerSecond
== 0 ) {
827 struct mach_timebase_info timeBaseInfo
;
828 if ( mach_timebase_info(&timeBaseInfo
) == KERN_SUCCESS
) {
829 sUnitsPerSecond
= timeBaseInfo
.denom
;
832 if ( partTime
< sUnitsPerSecond
) {
833 uint32_t milliSecondsTimeTen
= (partTime
*10000)/sUnitsPerSecond
;
834 uint32_t milliSeconds
= milliSecondsTimeTen
/10;
835 uint32_t percentTimesTen
= (partTime
*1000)/totalTime
;
836 uint32_t percent
= percentTimesTen
/10;
837 fprintf(stderr
, "%s: %u.%u milliseconds (%u.%u%%)\n", msg
, milliSeconds
, milliSecondsTimeTen
-milliSeconds
*10, percent
, percentTimesTen
-percent
*10);
840 uint32_t secondsTimeTen
= (partTime
*10)/sUnitsPerSecond
;
841 uint32_t seconds
= secondsTimeTen
/100;
842 uint32_t percentTimesTen
= (partTime
*1000)/totalTime
;
843 uint32_t percent
= percentTimesTen
/10;
844 fprintf(stderr
, "%s: %u.%u seconds (%u.%u%%)\n", msg
, seconds
, secondsTimeTen
-seconds
*10, percent
, percentTimesTen
-percent
*10);
848 static char* commatize(uint64_t in
, char* out
)
852 sprintf(rawNum
, "%llu", in
);
853 const int rawNumLen
= strlen(rawNum
);
854 for(int i
=0; i
< rawNumLen
-1; ++i
) {
856 if ( ((rawNumLen
-i
) % 3) == 1 )
859 *out
++ = rawNum
[rawNumLen
-1];
865 void ImageLoader::printStatistics(unsigned int imageCount
)
867 uint64_t totalTime
= fgTotalLoadLibrariesTime
+ fgTotalRebaseTime
+ fgTotalBindTime
+ fgTotalNotifyTime
+ fgTotalInitTime
;
871 printTime("total time", totalTime
, totalTime
);
872 fprintf(stderr
, "total images loaded: %d (%d used prebinding)\n", imageCount
, fgImagesWithUsedPrebinding
);
873 printTime("total images loading time", fgTotalLoadLibrariesTime
, totalTime
);
874 fprintf(stderr
, "total rebase fixups: %s\n", commatize(fgTotalRebaseFixups
, commaNum1
));
875 printTime("total rebase fixups time", fgTotalRebaseFixups
, totalTime
);
876 fprintf(stderr
, "total binding fixups: %s\n", commatize(fgTotalBindFixups
, commaNum1
));
877 printTime("total binding fixups time", fgTotalBindTime
, totalTime
);
878 fprintf(stderr
, "total bindings lazily fixed up: %s of %s\n", commatize(fgTotalLazyBindFixups
, commaNum1
), commatize(fgTotalPossibleLazyBindFixups
, commaNum2
));
879 printTime("total notify time time", fgTotalNotifyTime
, totalTime
);
880 printTime("total init time time", fgTotalInitTime
, totalTime
);
885 // copy path and add suffix to result
887 // /path/foo.dylib _debug => /path/foo_debug.dylib
888 // foo.dylib _debug => foo_debug.dylib
889 // foo _debug => foo_debug
890 // /path/bar _debug => /path/bar_debug
891 // /path/bar.A.dylib _debug => /path/bar.A_debug.dylib
893 void ImageLoader::addSuffix(const char* path
, const char* suffix
, char* result
)
895 strcpy(result
, path
);
897 char* start
= strrchr(result
, '/');
903 char* dot
= strrchr(start
, '.');
906 strcat(&dot
[strlen(suffix
)], &path
[dot
-result
]);
909 strcat(result
, suffix
);
914 void Segment::map(int fd
, uint64_t offsetInFatWrapper
, intptr_t slide
, const ImageLoader::LinkContext
& context
)
916 vm_offset_t fileOffset
= this->getFileOffset() + offsetInFatWrapper
;
917 vm_size_t size
= this->getFileSize();
918 void* requestedLoadAddress
= (void*)(this->getPreferredLoadAddress() + slide
);
920 if ( !this->unaccessible() ) {
921 if ( this->executable() )
922 protection
|= PROT_EXEC
;
923 if ( this->readable() )
924 protection
|= PROT_READ
;
925 if ( this->writeable() )
926 protection
|= PROT_WRITE
;
928 void* loadAddress
= mmap(requestedLoadAddress
, size
, protection
, MAP_FILE
| MAP_FIXED
| MAP_PRIVATE
, fd
, fileOffset
);
929 if ( loadAddress
== ((void*)(-1)) )
930 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());
932 if ( context
.verboseMapping
)
933 fprintf(stderr
, "%18s at %p->%p\n", this->getName(), loadAddress
, (char*)loadAddress
+this->getFileSize()-1);
936 void Segment::map(const void* memoryImage
, intptr_t slide
, const ImageLoader::LinkContext
& context
)
938 vm_address_t loadAddress
= this->getPreferredLoadAddress() + slide
;
939 vm_address_t srcAddr
= (uintptr_t)memoryImage
+ this->getFileOffset();
940 vm_size_t size
= this->getFileSize();
941 kern_return_t r
= vm_copy(mach_task_self(), srcAddr
, size
, loadAddress
);
942 if ( r
!= KERN_SUCCESS
)
943 throw "can't map segment";
945 if ( context
.verboseMapping
)
946 fprintf(stderr
, "%18s at %p->%p\n", this->getName(), (char*)loadAddress
, (char*)loadAddress
+this->getFileSize()-1);
949 void Segment::setPermissions()
951 vm_prot_t protection
= 0;
952 if ( !this->unaccessible() ) {
953 if ( this->executable() )
954 protection
|= VM_PROT_EXECUTE
;
955 if ( this->readable() )
956 protection
|= VM_PROT_READ
;
957 if ( this->writeable() )
958 protection
|= VM_PROT_WRITE
;
960 vm_address_t addr
= this->getActualLoadAddress();
961 vm_size_t size
= this->getSize();
962 const bool setCurrentPermissions
= false;
963 kern_return_t r
= vm_protect(mach_task_self(), addr
, size
, setCurrentPermissions
, protection
);
964 if ( r
!= KERN_SUCCESS
)
965 throw "can't set vm permissions for mapped segment";
968 void Segment::tempWritable()
970 vm_address_t addr
= this->getActualLoadAddress();
971 vm_size_t size
= this->getSize();
972 const bool setCurrentPermissions
= false;
973 kern_return_t r
= vm_protect(mach_task_self(), addr
, size
, setCurrentPermissions
, VM_PROT_WRITE
| VM_PROT_READ
);
974 if ( r
!= KERN_SUCCESS
)
975 throw "can't set vm permissions for mapped segment";
979 bool Segment::hasTrailingZeroFill()
981 return ( this->writeable() && (this->getSize() > this->getFileSize()) );
985 uintptr_t Segment::reserveAnAddressRange(size_t length
, const ImageLoader::LinkContext
& context
)
987 vm_address_t addr
= 0;
988 vm_size_t size
= length
;
989 if ( context
.slideAndPackDylibs
) {
990 addr
= (fgNextNonSplitSegAddress
- length
) & (-4096); // page align
991 kern_return_t r
= vm_allocate(mach_task_self(), &addr
, size
, false /*use this range*/);
992 if ( r
!= KERN_SUCCESS
)
993 throw "out of address space";
994 fgNextNonSplitSegAddress
= addr
;
997 kern_return_t r
= vm_allocate(mach_task_self(), &addr
, size
, true /*find range*/);
998 if ( r
!= KERN_SUCCESS
)
999 throw "out of address space";
1004 bool Segment::reserveAddressRange(uintptr_t start
, size_t length
)
1006 vm_address_t addr
= start
;
1007 vm_size_t size
= length
;
1008 kern_return_t r
= vm_allocate(mach_task_self(), &addr
, size
, false /*only this range*/);
1009 if ( r
!= KERN_SUCCESS
)