]> git.saurik.com Git - apple/dyld.git/blob - src/ImageLoader.cpp
dyld-43.tar.gz
[apple/dyld.git] / src / ImageLoader.cpp
1 /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
2 *
3 * Copyright (c) 2004-2005 Apple Computer, Inc. All rights reserved.
4 *
5 * @APPLE_LICENSE_HEADER_START@
6 *
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
12 * file.
13 *
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.
21 *
22 * @APPLE_LICENSE_HEADER_END@
23 */
24
25 #define __STDC_LIMIT_MACROS
26 #include <stdint.h>
27 #include <errno.h>
28 #include <fcntl.h>
29 #include <mach/mach.h>
30 #include <mach-o/fat.h>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <sys/mman.h>
34
35 #include "ImageLoader.h"
36
37
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;
50
51
52
53 __attribute__((noreturn))
54 void throwf(const char* format, ...)
55 {
56 va_list list;
57 char* p;
58 va_start(list, format);
59 vasprintf(&p, format, list);
60 va_end(list);
61
62 const char* t = p;
63 throw t;
64 }
65
66 void ImageLoader::init(const char* path, uint64_t offsetInFat, dev_t device, ino_t inode, time_t modDate)
67 {
68 fPathHash = 0;
69 fPath = NULL;
70 if ( path != NULL )
71 this->setPath(path);
72 fLogicalPath = NULL;
73 fDevice = device;
74 fInode = inode;
75 fLastModified = modDate;
76 fOffsetInFatFile = offsetInFat;
77 //fSegments = NULL;
78 fLibraries = NULL;
79 fLibrariesCount = 0;
80 fReferenceCount = 0;
81 fAllLibraryChecksumsAndLoadAddressesMatch = false;
82 fLeaveMapped = false;
83 fHideSymbols = false;
84 fMatchByInstallName = false;
85 fLibrariesLoaded = false;
86 fBased = false;
87 fBoundAllNonLazy = false;
88 fBoundAllLazy = false;
89 fAnnounced = false;
90 fInitialized = false;
91 fNextAddImageIndex = 0;
92 }
93
94
95 ImageLoader::ImageLoader(const char* path, uint64_t offsetInFat, const struct stat& info)
96 {
97 init(path, offsetInFat, info.st_dev, info.st_ino, info.st_mtime);
98 }
99
100 ImageLoader::ImageLoader(const char* moduleName)
101 {
102 init(moduleName, 0, 0, 0, 0);
103 }
104
105
106 ImageLoader::~ImageLoader()
107 {
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];
112 delete seg;
113 }
114 if ( fPath != NULL )
115 delete [] fPath;
116 if ( fLogicalPath != NULL )
117 delete [] fLogicalPath;
118 }
119
120
121 void ImageLoader::setPath(const char* path)
122 {
123 if ( fPath != NULL ) {
124 // if duplicate path, do nothing
125 if ( strcmp(path, fPath) == 0 )
126 return;
127 delete [] fPath;
128 }
129 fPath = new char[strlen(path)+1];
130 strcpy((char*)fPath, path);
131 fPathHash = hash(fPath);
132 }
133
134 void ImageLoader::setLogicalPath(const char* path)
135 {
136 if ( fPath == NULL ) {
137 // no physical path set yet, so use this path as physical
138 this->setPath(path);
139 }
140 else if ( strcmp(path, fPath) == 0 ) {
141 // do not set logical path because it is the same as the physical path
142 fLogicalPath = NULL;
143 }
144 else {
145 fLogicalPath = new char[strlen(path)+1];
146 strcpy((char*)fLogicalPath, path);
147 }
148 }
149
150 const char* ImageLoader::getLogicalPath() const
151 {
152 if ( fLogicalPath != NULL )
153 return fLogicalPath;
154 else
155 return fPath;
156 }
157
158 uint32_t ImageLoader::hash(const char* path)
159 {
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
163 uint32_t h = 0;
164 for (const char* s=path; *s != '\0'; ++s)
165 h = h*5 + *s;
166 return h;
167 }
168
169 bool ImageLoader::matchInstallPath() const
170 {
171 return fMatchByInstallName;
172 }
173
174 void ImageLoader::setMatchInstallPath(bool match)
175 {
176 fMatchByInstallName = match;
177 }
178
179 bool ImageLoader::statMatch(const struct stat& stat_buf) const
180 {
181 return ( (this->fDevice == stat_buf.st_dev) && (this->fInode == stat_buf.st_ino) );
182 }
183
184 const char* ImageLoader::getShortName() const
185 {
186 // try to return leaf name
187 if ( fPath != NULL ) {
188 const char* s = strrchr(fPath, '/');
189 if ( s != NULL )
190 return &s[1];
191 }
192 return fPath;
193 }
194
195 uint64_t ImageLoader::getOffsetInFatFile() const
196 {
197 return fOffsetInFatFile;
198 }
199
200 void ImageLoader::setLeaveMapped()
201 {
202 fLeaveMapped = true;
203 const unsigned int segmentCount = fSegments.size();
204 for(unsigned int i=0; i < segmentCount; ++i){
205 fSegments[i]->setUnMapWhenDestructed(false);
206 }
207 }
208
209 void ImageLoader::setHideExports(bool hide)
210 {
211 fHideSymbols = hide;
212 }
213
214 bool ImageLoader::hasHiddenExports() const
215 {
216 return fHideSymbols;
217 }
218
219 bool ImageLoader::isLinked() const
220 {
221 return fBoundAllNonLazy;
222 }
223
224 time_t ImageLoader::lastModified()
225 {
226 return fLastModified;
227 }
228
229 bool ImageLoader::containsAddress(const void* addr) const
230 {
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() )
237 return true;
238 }
239 return false;
240 }
241
242 void ImageLoader::addMappedRegions(RegionsVector& regions) const
243 {
244 const unsigned int segmentCount = fSegments.size();
245 for(unsigned int i=0; i < segmentCount; ++i){
246 Segment* seg = fSegments[i];
247 MappedRegion region;
248 region.address = seg->getActualLoadAddress();
249 region.size = seg->getSize();
250 regions.push_back(region);
251 }
252 }
253
254
255 void ImageLoader::incrementReferenceCount()
256 {
257 ++fReferenceCount;
258 }
259
260 bool ImageLoader::decrementReferenceCount()
261 {
262 return ( --fReferenceCount == 0 );
263 }
264
265
266 const ImageLoader::Symbol* ImageLoader::resolveSymbol(const char* name, bool searchSelf, ImageLoader** foundIn) const
267 {
268 const ImageLoader::Symbol* sym;
269 // search self
270 if ( searchSelf ) {
271 sym = this->findExportedSymbol(name, NULL, false, foundIn);
272 if ( sym != NULL )
273 return sym;
274 }
275
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);
281 if ( sym != NULL )
282 return sym;
283 }
284 }
285
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);
291 if ( sym != NULL )
292 return sym;
293 }
294 }
295
296 return NULL;
297 }
298
299
300
301 void ImageLoader::link(const LinkContext& context, BindingLaziness bindness, InitializerRunning inits, uint32_t notifyCount)
302 {
303 uint64_t t1 = mach_absolute_time();
304 this->recursiveLoadLibraries(context);
305
306 uint64_t t2 = mach_absolute_time();
307 this->recursiveRebase(context);
308
309 uint64_t t3 = mach_absolute_time();
310 this->recursiveBind(context, bindness);
311
312 uint64_t t4 = mach_absolute_time();
313 this->recursiveImageNotification(context, notifyCount);
314
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
319 }
320
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;
326 }
327 fgTotalLoadLibrariesTime += t2 - t1;
328 fgTotalRebaseTime += t3 - t2;
329 fgTotalBindTime += t4 - t3;
330 fgTotalNotifyTime += t5 - t4;
331 }
332
333
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)
337 {
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
341
342 this->recursiveInitialization(context);
343 }
344
345 // called inside _dyld_register_func_for_add_image()
346 void ImageLoader::runNotification(const LinkContext& context, uint32_t notifyCount)
347 {
348 this->recursiveImageNotification(context, notifyCount);
349 }
350
351
352 intptr_t ImageLoader::assignSegmentAddresses(const LinkContext& context)
353 {
354 // preflight and calculate slide if needed
355 const unsigned int segmentCount = fSegments.size();
356 intptr_t slide = 0;
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 )
366 lowAddr = segLow;
367 if ( segHigh > highAddr )
368 highAddr = segHigh;
369
370 if ( context.slideAndPackDylibs || !seg->hasPreferredLoadAddress() || !Segment::reserveAddressRange(seg->getPreferredLoadAddress(), seg->getSize()) )
371 needsToSlide = true;
372 }
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;
377 }
378 }
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 )
383 continue;
384 if ( !Segment::reserveAddressRange(seg->getPreferredLoadAddress(), seg->getSize()) )
385 throw "can't map";
386 }
387 }
388 else {
389 // mach-o does not support independently sliding segments
390 }
391 return slide;
392 }
393
394
395 void ImageLoader::mapSegments(int fd, uint64_t offsetInFat, uint64_t lenInFat, uint64_t fileLen, const LinkContext& context)
396 {
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);
406 }
407 // update slide to reflect load location
408 this->setSlide(slide);
409
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);
413 }
414 }
415
416 void ImageLoader::mapSegments(const void* memoryImage, uint64_t imageLen, const LinkContext& context)
417 {
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);
427 }
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();
434 }
435 }
436
437 bool ImageLoader::allDependentLibrariesAsWhenPreBound() const
438 {
439 return fAllLibraryChecksumsAndLoadAddressesMatch;
440 }
441
442
443 void ImageLoader::recursiveLoadLibraries(const LinkContext& context)
444 {
445 if ( ! fLibrariesLoaded ) {
446 // break cycles
447 fLibrariesLoaded = true;
448
449 // get list of libraries this image needs
450 fLibrariesCount = this->doGetDependentLibraryCount();
451 fLibraries = new DependentLibrary[fLibrariesCount];
452 this->doGetDependentLibraries(fLibraries);
453
454 // try to load each
455 bool canUsePrelinkingInfo = true;
456 for(unsigned int i=0; i < fLibrariesCount; ++i){
457 DependentLibrary& requiredLib = fLibraries[i];
458 try {
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());
465 }
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);
473 }
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());
482 //}
483 }
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
493 }
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
497 }
498 }
499 fAllLibraryChecksumsAndLoadAddressesMatch = canUsePrelinkingInfo;
500
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);
510 }
511 }
512
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;
520 }
521 }
522 }
523
524 }
525 }
526
527 void ImageLoader::recursiveRebase(const LinkContext& context)
528 {
529 if ( ! fBased ) {
530 // break cycles
531 fBased = true;
532
533 try {
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);
539 }
540
541 // rebase this image
542 doRebase(context);
543 }
544 catch (const char* msg) {
545 // this image is not rebased
546 fBased = false;
547 throw msg;
548 }
549 }
550 }
551
552
553
554
555 void ImageLoader::recursiveBind(const LinkContext& context, BindingLaziness bindness)
556 {
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;
561 bool lazy = false;
562 switch( bindness ) {
563 case kNonLazyOnly:
564 nonLazy = true;
565 break;
566 case kLazyAndNonLazy:
567 nonLazy = true;
568 lazy = true;
569 break;
570 case kLazyOnly:
571 case kLazyOnlyNoDependents:
572 lazy = true;
573 break;
574 }
575 const bool doNonLazy = nonLazy && !fBoundAllNonLazy;
576 const bool doLazy = lazy && !fBoundAllLazy;
577 if ( doNonLazy || doLazy ) {
578 // break cycles
579 bool oldBoundAllNonLazy = fBoundAllNonLazy;
580 bool oldBoundAllLazy = fBoundAllLazy;
581 fBoundAllNonLazy = fBoundAllNonLazy || nonLazy;
582 fBoundAllLazy = fBoundAllLazy || lazy;
583
584 try {
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);
591 }
592 }
593 // bind this image
594 if ( doLazy && !doNonLazy )
595 doBind(context, kLazyOnly);
596 else if ( !doLazy && doNonLazy )
597 doBind(context, kNonLazyOnly);
598 else
599 doBind(context, kLazyAndNonLazy);
600 }
601 catch (const char* msg) {
602 // restore state
603 fBoundAllNonLazy = oldBoundAllNonLazy;
604 fBoundAllLazy = oldBoundAllLazy;
605 throw msg;
606 }
607 }
608 }
609
610 //
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.
616 //
617 void ImageLoader::recursiveImageNotification(const LinkContext& context, uint32_t addImageCount)
618 {
619 if ( fNextAddImageIndex < addImageCount ) {
620 // break cycles
621 const uint32_t initIndex = fNextAddImageIndex;
622 fNextAddImageIndex = addImageCount;
623
624 // notify all requestors about this image
625 context.imageNotification(this, initIndex);
626
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);
632 }
633 }
634 }
635
636
637 void ImageLoader::recursiveImageAnnouncement(const LinkContext& context, std::vector<ImageLoader*>& newImages)
638 {
639 if ( ! fAnnounced ) {
640 // break cycles
641 fAnnounced = true;
642
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);
648 }
649
650 // add to list of images to notify gdb about
651 newImages.push_back(this);
652 //fprintf(stderr, "next size = %d\n", newImages.size());
653
654 // remember that this image wants to be notified about other images
655 if ( this->hasImageNotification() )
656 context.addImageNeedingNotification(this);
657 }
658 }
659
660
661
662 void ImageLoader::recursiveInitialization(const LinkContext& context)
663 {
664 if ( ! fInitialized ) {
665 // break cycles
666 fInitialized = true;
667
668 try {
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);
674 }
675
676 // record termination order
677 if ( this->needsTermination() )
678 context.terminationRecorder(this);
679
680 // initialize this image
681 this->doInitialization(context);
682 }
683 catch (const char* msg) {
684 // this image is not initialized
685 fInitialized = false;
686 throw msg;
687 }
688 }
689 }
690
691 void ImageLoader::reprebindCommit(const LinkContext& context, bool commit)
692 {
693 // do nothing on unprebound images
694 if ( ! this->isPrebindable() )
695 return;
696
697 // do nothing if prebinding is up to date
698 if ( this->usablePrebinding(context) )
699 return;
700
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);
705 }
706 // recreate temp file name
707 char tempFilePath[PATH_MAX];
708 ImageLoader::addSuffix(realFilePath, "_redoprebinding", tempFilePath);
709
710 if ( commit ) {
711 // all files successfully reprebound, so do swap
712 int result = rename(tempFilePath, realFilePath);
713 if ( result != 0 ) {
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);
718 else
719 throwf("can't swap temporary re-prebound file: rename(%s,%s) returned errno=%d", tempFilePath, realFilePath, errno);
720 }
721 }
722 else {
723 // something went wrong during prebinding, delete the temp files
724 unlink(tempFilePath);
725 }
726 }
727
728 void ImageLoader::reprebind(const LinkContext& context, time_t timestamp)
729 {
730 // do nothing on unprebound images
731 if ( ! this->isPrebindable() )
732 return;
733
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());
738 return;
739 }
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);
747
748 // do format specific prebinding
749 this->doPrebinding(context, timestamp, fileToPrebind);
750
751 // flush and swap files
752 int result = msync(fileToPrebind, fileToPrebindSize, MS_ASYNC);
753 if ( result != 0 )
754 throw "error syncing re-prebound file";
755 result = munmap(fileToPrebind, fileToPrebindSize);
756 if ( result != 0 )
757 throw "error unmapping re-prebound file";
758
759 // log
760 if ( context.verbosePrebinding )
761 fprintf(stderr, "dyld: re-prebound: %s\n", this->getPath());
762 }
763
764 void ImageLoader::copyAndMap(const char* tempFile, uint8_t** fileToPrebind, uint64_t* fileToPrebindSize)
765 {
766 // reopen dylib
767 int src = open(this->getPath(), O_RDONLY);
768 if ( src == -1 )
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";
775
776 // create new file with all same permissions to hold copy of dylib
777 unlink(tempFile);
778 int dst = open(tempFile, O_CREAT | O_RDWR | O_TRUNC, stat_buf.st_mode);
779 if ( dst == -1 )
780 throw "can't create temp image";
781
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
785
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";
791
792 // copy contents
793 ssize_t len;
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;
800 else
801 throw "can't allcoate copy buffer";
802 }
803 while ( (len = read(src, buffer, kBufferSize)) > 0 ) {
804 write(dst, buffer, len);
805 }
806
807 // map in dst file
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";
812
813 // closing notes:
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";
820 }
821
822
823 static void printTime(const char* msg, uint64_t partTime, uint64_t totalTime)
824 {
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;
830 }
831 }
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);
838 }
839 else {
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);
845 }
846 }
847
848 static char* commatize(uint64_t in, char* out)
849 {
850 char* result = out;
851 char rawNum[30];
852 sprintf(rawNum, "%llu", in);
853 const int rawNumLen = strlen(rawNum);
854 for(int i=0; i < rawNumLen-1; ++i) {
855 *out++ = rawNum[i];
856 if ( ((rawNumLen-i) % 3) == 1 )
857 *out++ = ',';
858 }
859 *out++ = rawNum[rawNumLen-1];
860 *out = '\0';
861 return result;
862 }
863
864
865 void ImageLoader::printStatistics(unsigned int imageCount)
866 {
867 uint64_t totalTime = fgTotalLoadLibrariesTime + fgTotalRebaseTime + fgTotalBindTime + fgTotalNotifyTime + fgTotalInitTime;
868 char commaNum1[40];
869 char commaNum2[40];
870
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);
881 }
882
883
884 //
885 // copy path and add suffix to result
886 //
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
892 //
893 void ImageLoader::addSuffix(const char* path, const char* suffix, char* result)
894 {
895 strcpy(result, path);
896
897 char* start = strrchr(result, '/');
898 if ( start != NULL )
899 start++;
900 else
901 start = result;
902
903 char* dot = strrchr(start, '.');
904 if ( dot != NULL ) {
905 strcpy(dot, suffix);
906 strcat(&dot[strlen(suffix)], &path[dot-result]);
907 }
908 else {
909 strcat(result, suffix);
910 }
911 }
912
913
914 void Segment::map(int fd, uint64_t offsetInFatWrapper, intptr_t slide, const ImageLoader::LinkContext& context)
915 {
916 vm_offset_t fileOffset = this->getFileOffset() + offsetInFatWrapper;
917 vm_size_t size = this->getFileSize();
918 void* requestedLoadAddress = (void*)(this->getPreferredLoadAddress() + slide);
919 int protection = 0;
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;
927 }
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());
931
932 if ( context.verboseMapping )
933 fprintf(stderr, "%18s at %p->%p\n", this->getName(), loadAddress, (char*)loadAddress+this->getFileSize()-1);
934 }
935
936 void Segment::map(const void* memoryImage, intptr_t slide, const ImageLoader::LinkContext& context)
937 {
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";
944
945 if ( context.verboseMapping )
946 fprintf(stderr, "%18s at %p->%p\n", this->getName(), (char*)loadAddress, (char*)loadAddress+this->getFileSize()-1);
947 }
948
949 void Segment::setPermissions()
950 {
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;
959 }
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";
966 }
967
968 void Segment::tempWritable()
969 {
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";
976 }
977
978
979 bool Segment::hasTrailingZeroFill()
980 {
981 return ( this->writeable() && (this->getSize() > this->getFileSize()) );
982 }
983
984
985 uintptr_t Segment::reserveAnAddressRange(size_t length, const ImageLoader::LinkContext& context)
986 {
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;
995 }
996 else {
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";
1000 }
1001 return addr;
1002 }
1003
1004 bool Segment::reserveAddressRange(uintptr_t start, size_t length)
1005 {
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 )
1010 return false;
1011 return true;
1012 }
1013
1014
1015