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@
27 #include <sys/types.h>
30 #include <mach/shared_memory_server.h>
31 #include <mach/mach.h>
32 #include <mach/thread_status.h>
33 #include <mach-o/loader.h>
34 #include <mach-o/reloc.h>
35 #include <mach-o/nlist.h>
36 #include <sys/sysctl.h>
37 #if __ppc__ || __ppc64__
38 #include <mach-o/ppc/reloc.h>
41 #include "ImageLoaderMachO.h"
42 #include "mach-o/dyld_gdb.h"
44 // no header for this yet, rdar://problem/3850825
45 extern "C" void sys_icache_invalidate(void *, size_t);
47 // optimize strcmp for ppc
49 #include <ppc_intrinsics.h>
51 #define astrcmp(a,b) strcmp(a,b)
54 // relocation_info.r_length field has value 3 for 64-bit executables and value 2 for 32-bit executables
57 #define LC_SEGMENT_COMMAND LC_SEGMENT_64
58 #define LC_ROUTINES_COMMAND LC_ROUTINES_64
59 struct macho_header
: public mach_header_64
{};
60 struct macho_segment_command
: public segment_command_64
{};
61 struct macho_section
: public section_64
{};
62 struct macho_nlist
: public nlist_64
{};
63 struct macho_routines_command
: public routines_command_64
{};
66 #define LC_SEGMENT_COMMAND LC_SEGMENT
67 #define LC_ROUTINES_COMMAND LC_ROUTINES
68 struct macho_header
: public mach_header
{};
69 struct macho_segment_command
: public segment_command
{};
70 struct macho_section
: public section
{};
71 struct macho_nlist
: public nlist
{};
72 struct macho_routines_command
: public routines_command
{};
76 uint32_t ImageLoaderMachO::fgHintedBinaryTreeSearchs
= 0;
77 uint32_t ImageLoaderMachO::fgUnhintedBinaryTreeSearchs
= 0;
80 //#define LINKEDIT_USAGE_DEBUG 1
82 #if LINKEDIT_USAGE_DEBUG
84 static std::set
<uintptr_t> sLinkEditPageBuckets
;
87 extern ImageLoader
* findImageContainingAddress(const void* addr
);
90 static void noteAccessedLinkEditAddress(const void* addr
)
92 uintptr_t page
= ((uintptr_t)addr
) & (-4096);
93 sLinkEditPageBuckets
.insert(page
);
94 fprintf(stderr
, "dyld: accessing page 0x%08lX in __LINKEDIT of %s\n", page
, dyld::findImageContainingAddress(addr
)->getPath());
98 // only way to share initialization in C++
99 void ImageLoaderMachO::init()
102 fLinkEditBase
= NULL
;
108 fHasSubLibraries
= false;
109 fHasSubUmbrella
= false;
111 fModInitSection
= NULL
;
112 fModTermSection
= NULL
;
114 fImageNotifySection
= NULL
;
115 fTwoLevelHints
= NULL
;
117 fReExportThruFramework
= NULL
;
118 fTextSegmentWithFixups
= NULL
;
121 // create image by copying an in-memory mach-o file
122 ImageLoaderMachO::ImageLoaderMachO(const char* moduleName
, const struct mach_header
* mh
, uint64_t len
, const LinkContext
& context
)
123 : ImageLoader(moduleName
)
128 // temporary use this buffer until TEXT is mapped in
129 fMachOData
= (const uint8_t*)mh
;
132 this->instantiateSegments((const uint8_t*)mh
);
135 if ( mh
->filetype
!= MH_EXECUTE
)
136 ImageLoader::mapSegments((const void*)mh
, len
, context
);
138 // get pointers to interesting things
139 this->parseLoadCmds();
143 // create image by mapping in a mach-o file
144 ImageLoaderMachO::ImageLoaderMachO(const char* path
, int fd
, const uint8_t firstPage
[4096], uint64_t offsetInFat
,
145 uint64_t lenInFat
, const struct stat
& info
, const LinkContext
& context
)
146 : ImageLoader(path
, offsetInFat
, info
)
151 // read load commands
152 const unsigned int dataSize
= sizeof(macho_header
) + ((macho_header
*)firstPage
)->sizeofcmds
;
153 uint8_t buffer
[dataSize
];
154 const uint8_t* fileData
= firstPage
;
155 if ( dataSize
> 4096 ) {
156 // only read more if cmds take up more space than first page
158 memcpy(buffer
, firstPage
, 4096);
159 pread(fd
, &buffer
[4096], dataSize
-4096, offsetInFat
+4096);
162 // temporary use this buffer until TEXT is mapped in
163 fMachOData
= fileData
;
165 // the meaning of many fields changes in split seg mach-o files
166 fIsSplitSeg
= ((((macho_header
*)fileData
)->flags
& MH_SPLIT_SEGS
) != 0) && (((macho_header
*)fileData
)->filetype
== MH_DYLIB
);
169 this->instantiateSegments(fileData
);
171 // map segments, except for main executable which is already mapped in by kernel
172 if ( ((macho_header
*)fileData
)->filetype
!= MH_EXECUTE
)
173 this->mapSegments(fd
, offsetInFat
, lenInFat
, info
.st_size
, context
);
175 // get pointers to interesting things
176 this->parseLoadCmds();
182 void ImageLoaderMachO::instantiateSegments(const uint8_t* fileData
)
184 const uint32_t cmd_count
= ((macho_header
*)fileData
)->ncmds
;
185 const struct load_command
* const cmds
= (struct load_command
*)&fileData
[sizeof(macho_header
)];
187 // construct Segment object for each LC_SEGMENT cmd and add to list
188 const struct load_command
* cmd
= cmds
;
189 for (unsigned long i
= 0; i
< cmd_count
; ++i
) {
190 if ( cmd
->cmd
== LC_SEGMENT_COMMAND
) {
191 fSegments
.push_back(new SegmentMachO((struct macho_segment_command
*)cmd
, this, fileData
));
193 cmd
= (const struct load_command
*)(((char*)cmd
)+cmd
->cmdsize
);
199 bool ImageLoaderMachO::segmentsMustSlideTogether() const
204 bool ImageLoaderMachO::segmentsCanSlide() const
206 const macho_header
* mh
= (macho_header
*)fMachOData
;
207 return ( (mh
->filetype
== MH_DYLIB
) || (mh
->filetype
== MH_BUNDLE
) );
210 bool ImageLoaderMachO::isBundle() const
212 const macho_header
* mh
= (macho_header
*)fMachOData
;
213 return ( mh
->filetype
== MH_BUNDLE
);
216 bool ImageLoaderMachO::isDylib() const
218 const macho_header
* mh
= (macho_header
*)fMachOData
;
219 return ( mh
->filetype
== MH_DYLIB
);
222 bool ImageLoaderMachO::forceFlat() const
224 const macho_header
* mh
= (macho_header
*)fMachOData
;
225 return ( (mh
->flags
& MH_FORCE_FLAT
) != 0 );
228 bool ImageLoaderMachO::usesTwoLevelNameSpace() const
230 const macho_header
* mh
= (macho_header
*)fMachOData
;
231 return ( (mh
->flags
& MH_TWOLEVEL
) != 0 );
234 bool ImageLoaderMachO::isPrebindable() const
236 const macho_header
* mh
= (macho_header
*)fMachOData
;
237 return ( (mh
->flags
& MH_PREBOUND
) != 0 );
240 bool ImageLoaderMachO::hasCoalescedExports() const
242 const macho_header
* mh
= (macho_header
*)fMachOData
;
243 return ( (mh
->flags
& MH_WEAK_DEFINES
) != 0 );
246 bool ImageLoaderMachO::needsCoalescing() const
248 const macho_header
* mh
= (macho_header
*)fMachOData
;
249 return ( (mh
->flags
& MH_BINDS_TO_WEAK
) != 0 );
252 #if !__LP64__ // split segs not supported for 64-bits
254 #if 1 // hack until kernel headers and glue are in system
255 struct _shared_region_mapping_np
{
256 mach_vm_address_t address
;
258 mach_vm_offset_t file_offset
;
259 vm_prot_t max_prot
; /* read/write/execute/COW/ZF */
260 vm_prot_t init_prot
; /* read/write/execute/COW/ZF */
262 struct _shared_region_range_np
{
263 mach_vm_address_t address
;
268 // Requests the kernel to map a number of regions from the fd into the
269 // shared sections address range (0x90000000-0xAFFFFFFF).
270 // If shared_region_make_private_np() has not been called by this process,
271 // the file mapped in is seen in the address space of all processes that
272 // participate in using the shared region.
273 // If shared_region_make_private_np() _has_ been called by this process,
274 // the file mapped in is only seen by this process.
275 // If the slide parameter is not NULL and then regions cannot be mapped
276 // as requested, the kernel will try to map the file in at a different
277 // address in the shared region and return the distance slid.
278 // If the mapping requesting cannot be fulfilled, returns non-zero.
280 _shared_region_map_file_np(
281 int fd
, // file descriptor to map into shared region
282 unsigned int regionCount
, // number of entres in array of regions
283 const _shared_region_mapping_np regions
[], // the array of regions to map
284 uint64_t* slide
) // the amount all regions were slid, NULL means don't attempt to slide
286 //fprintf(stderr, "%s(%i, %u, %8p, %8p)\n", __func__, fd, regionCount, regions, slide);
287 //for ( unsigned int i=0; i < regionCount; ++i) {
288 // fprintf(stderr, "\taddress=0x%08llX, size=0x%08llX\n", regions[i].address, regions[i].size);
290 int r
= syscall(299, fd
, regionCount
, regions
, slide
);
292 // fprintf(stderr, "%s(%i, %u, %8p, %8p) errno=%i (%s)\n", __func__, fd, regionCount, regions, slide, errno, strerror(errno));
295 // Called by dyld if shared_region_map_file() fails.
296 // Requests the kernel to take this process out of using the shared region.
297 // The specified ranges are created as private copies from the shared region for this process.
299 _shared_region_make_private_np(
300 unsigned int rangeCount
, // number of entres in array of msrp_range
301 const _shared_region_range_np ranges
[]) // the array of shared regions to make private
303 //fprintf(stderr, "%s(%u, %8p)\n", __func__, rangeCount, ranges);
304 int r
= syscall(300, rangeCount
, ranges
);
306 // fprintf(stderr, "%s(%u, %8p) errno=%i (%s)\n", __func__, rangeCount, ranges, errno, strerror(errno));
309 #define KERN_SHREG_PRIVATIZABLE 54
310 #endif // hack until kernel headers and glue are in system
312 static uintptr_t sNextAltLoadAddress
320 _shared_region_map_file_with_mmap(
321 int fd
, // file descriptor to map into shared region
322 unsigned int regionCount
, // number of entres in array of regions
323 const _shared_region_mapping_np regions
[]) // the array of regions to map
325 // map in each region
326 for(unsigned int i
=0; i
< regionCount
; ++i
) {
327 void* mmapAddress
= (void*)(uintptr_t)(regions
[i
].address
);
328 size_t size
= regions
[i
].size
;
329 if ( (regions
[i
].init_prot
& VM_PROT_ZF
) != 0 ) {
330 // do nothing already vm_allocate() which zero fills
334 if ( regions
[i
].init_prot
& VM_PROT_EXECUTE
)
335 protection
|= PROT_EXEC
;
336 if ( regions
[i
].init_prot
& VM_PROT_READ
)
337 protection
|= PROT_READ
;
338 if ( regions
[i
].init_prot
& VM_PROT_WRITE
)
339 protection
|= PROT_WRITE
;
340 off_t offset
= regions
[i
].file_offset
;
341 //fprintf(stderr, "mmap(%p, 0x%08lX, block=0x%08X, %s\n", mmapAddress, size, biggestDiff, fPath);
342 mmapAddress
= mmap(mmapAddress
, size
, protection
, MAP_FILE
| MAP_FIXED
| MAP_PRIVATE
, fd
, offset
);
343 if ( mmapAddress
== ((void*)(-1)) )
354 hasSharedRegionMapFile(void)
356 int mib
[CTL_MAXNAME
];
361 mib
[1] = KERN_SHREG_PRIVATIZABLE
;
363 if (sysctl(mib
, 2, &value
, &size
, NULL
, 0) != 0) {
371 ImageLoaderMachO::sharedRegionMapFilePrivateOutside(int fd
,
372 uint64_t offsetInFat
,
375 const LinkContext
& context
)
377 const unsigned int segmentCount
= fSegments
.size();
378 const unsigned int extraZeroFillEntries
= getExtraZeroFillEntriesCount();
379 const unsigned int regionCount
= segmentCount
+extraZeroFillEntries
;
380 _shared_region_mapping_np regions
[regionCount
];
381 initMappingTable(offsetInFat
, regions
);
383 // find space somewhere to allocate split seg
384 bool foundRoom
= false;
385 vm_size_t biggestDiff
= 0;
386 while ( ! foundRoom
) {
388 for(unsigned int i
=0; i
< regionCount
; ++i
) {
389 vm_address_t addr
= sNextAltLoadAddress
+ regions
[i
].address
- regions
[0].address
;
390 vm_size_t size
= regions
[i
].size
;
391 r
= vm_allocate(mach_task_self(), &addr
, size
, false /*only this range*/);
393 // no room here, deallocate what has succeeded so far
394 for(unsigned int j
=0; j
< i
; ++j
) {
395 vm_address_t addr
= sNextAltLoadAddress
+ regions
[j
].address
- regions
[0].address
;
396 vm_size_t size
= regions
[j
].size
;
397 (void)vm_deallocate(mach_task_self(), addr
, size
);
399 sNextAltLoadAddress
+= 0x00100000; // skip ahead 1MB and try again
400 if ( (sNextAltLoadAddress
& 0xF0000000) == 0x90000000 )
401 throw "can't map split seg anywhere";
405 vm_size_t high
= (regions
[i
].address
+ size
- regions
[0].address
) & 0x0FFFFFFF;
406 if ( high
> biggestDiff
)
411 // map in each region
412 uintptr_t slide
= sNextAltLoadAddress
- regions
[0].address
;
413 this->setSlide(slide
);
414 for(unsigned int i
=0; i
< regionCount
; ++i
) {
415 if ( (regions
[i
].init_prot
& VM_PROT_ZF
) != 0 ) {
416 // do nothing vm_allocate() zero-fills by default
419 void* mmapAddress
= (void*)(uintptr_t)(regions
[i
].address
+ slide
);
420 size_t size
= regions
[i
].size
;
422 if ( regions
[i
].init_prot
& VM_PROT_EXECUTE
)
423 protection
|= PROT_EXEC
;
424 if ( regions
[i
].init_prot
& VM_PROT_READ
)
425 protection
|= PROT_READ
;
426 if ( regions
[i
].init_prot
& VM_PROT_WRITE
)
427 protection
|= PROT_WRITE
;
428 off_t offset
= regions
[i
].file_offset
;
429 //fprintf(stderr, "mmap(%p, 0x%08lX, block=0x%08X, %s\n", mmapAddress, size, biggestDiff, fPath);
430 mmapAddress
= mmap(mmapAddress
, size
, protection
, MAP_FILE
| MAP_FIXED
| MAP_PRIVATE
, fd
, offset
);
431 if ( mmapAddress
== ((void*)(-1)) )
435 // set so next maps right after this one
436 sNextAltLoadAddress
+= biggestDiff
;
437 sNextAltLoadAddress
= (sNextAltLoadAddress
+ 4095) & (-4096);
440 if ( context
.verboseMapping
) {
441 fprintf(stderr
, "dyld: Mapping split-seg outside shared region, slid by 0x%08lX %s\n", this->fSlide
, this->getPath());
442 for(unsigned int segIndex
=0,entryIndex
=0; segIndex
< segmentCount
; ++segIndex
, ++entryIndex
){
443 Segment
* seg
= fSegments
[segIndex
];
444 const _shared_region_mapping_np
* entry
= ®ions
[entryIndex
];
445 if ( (entry
->init_prot
& VM_PROT_ZF
) == 0 )
446 fprintf(stderr
, "%18s at 0x%08lX->0x%08lX\n",
447 seg
->getName(), seg
->getActualLoadAddress(), seg
->getActualLoadAddress()+seg
->getFileSize()-1);
448 if ( entryIndex
< (regionCount
-1) ) {
449 const _shared_region_mapping_np
* nextEntry
= ®ions
[entryIndex
+1];
450 if ( (nextEntry
->init_prot
& VM_PROT_ZF
) != 0 ) {
451 uint64_t segOffset
= nextEntry
->address
- entry
->address
;
452 fprintf(stderr
, "%18s at 0x%08lX->0x%08lX (zerofill)\n",
453 seg
->getName(), (uintptr_t)(seg
->getActualLoadAddress() + segOffset
), (uintptr_t)(seg
->getActualLoadAddress() + segOffset
+ nextEntry
->size
- 1));
464 void ImageLoaderMachO::mapSegments(int fd
, uint64_t offsetInFat
, uint64_t lenInFat
, uint64_t fileLen
, const LinkContext
& context
)
466 enum SharedRegionState
468 kSharedRegionStartState
= 0,
469 kSharedRegionLoadFileState
,
470 kSharedRegionMapFileState
,
471 kSharedRegionMapFilePrivateState
,
472 kSharedRegionMapFilePrivateMMapState
,
473 kSharedRegionMapFilePrivateOutsideState
,
475 static SharedRegionState sSharedRegionState
= kSharedRegionStartState
;
477 // non-split segment libraries handled by super class
479 return ImageLoader::mapSegments(fd
, offsetInFat
, lenInFat
, fileLen
, context
);
481 if ( kSharedRegionStartState
== sSharedRegionState
) {
482 if ( hasSharedRegionMapFile() ) {
483 if ( context
.slideAndPackDylibs
) {
484 sharedRegionMakePrivate(context
);
485 // remove underlying submap and block out 0x90000000 to 0xAFFFFFFF
486 vm_address_t addr
= (vm_address_t
)0x90000000;
487 vm_deallocate(mach_task_self(), addr
, 0x20000000);
488 vm_allocate(mach_task_self(), &addr
, 0x20000000, false);
489 sSharedRegionState
= kSharedRegionMapFilePrivateMMapState
;
491 else if ( context
.sharedRegionMode
== kUsePrivateSharedRegion
) {
492 sharedRegionMakePrivate(context
);
493 sSharedRegionState
= kSharedRegionMapFilePrivateState
;
495 else if ( context
.sharedRegionMode
== kDontUseSharedRegion
) {
496 sSharedRegionState
= kSharedRegionMapFilePrivateOutsideState
;
499 sSharedRegionState
= kSharedRegionMapFileState
;
503 sSharedRegionState
= kSharedRegionLoadFileState
;
507 if ( kSharedRegionLoadFileState
== sSharedRegionState
) {
508 if ( 0 != sharedRegionLoadFile(fd
, offsetInFat
, lenInFat
, fileLen
, context
) ) {
509 sSharedRegionState
= kSharedRegionMapFilePrivateOutsideState
;
513 if ( kSharedRegionMapFileState
== sSharedRegionState
) {
514 if ( 0 != sharedRegionMapFile(fd
, offsetInFat
, lenInFat
, fileLen
, context
) ) {
515 sharedRegionMakePrivate(context
);
516 sSharedRegionState
= kSharedRegionMapFilePrivateState
;
520 if ( (kSharedRegionMapFilePrivateState
== sSharedRegionState
) || (kSharedRegionMapFilePrivateMMapState
== sSharedRegionState
) ) {
521 if ( 0 != sharedRegionMapFilePrivate(fd
, offsetInFat
, lenInFat
, fileLen
, context
, (kSharedRegionMapFilePrivateMMapState
== sSharedRegionState
)) ) {
522 sSharedRegionState
= kSharedRegionMapFilePrivateOutsideState
;
526 if ( kSharedRegionMapFilePrivateOutsideState
== sSharedRegionState
) {
527 if ( 0 != sharedRegionMapFilePrivateOutside(fd
, offsetInFat
, lenInFat
, fileLen
, context
) ) {
528 throw "mapping error";
534 ImageLoaderMachO::getExtraZeroFillEntriesCount()
536 // calculate mapping entries
537 const unsigned int segmentCount
= fSegments
.size();
538 unsigned int extraZeroFillEntries
= 0;
539 for(unsigned int i
=0; i
< segmentCount
; ++i
){
540 Segment
* seg
= fSegments
[i
];
541 if ( seg
->hasTrailingZeroFill() )
542 ++extraZeroFillEntries
;
545 return extraZeroFillEntries
;
549 ImageLoaderMachO::initMappingTable(uint64_t offsetInFat
,
550 _shared_region_mapping_np
*mappingTable
)
552 unsigned int segmentCount
= fSegments
.size();
553 for(unsigned int segIndex
=0,entryIndex
=0; segIndex
< segmentCount
; ++segIndex
, ++entryIndex
){
554 Segment
* seg
= fSegments
[segIndex
];
555 _shared_region_mapping_np
* entry
= &mappingTable
[entryIndex
];
556 entry
->address
= seg
->getActualLoadAddress();
557 entry
->size
= seg
->getFileSize();
558 entry
->file_offset
= seg
->getFileOffset() + offsetInFat
;
559 entry
->init_prot
= VM_PROT_NONE
;
560 if ( !seg
->unaccessible() ) {
561 if ( seg
->executable() )
562 entry
->init_prot
|= VM_PROT_EXECUTE
;
563 if ( seg
->readable() )
564 entry
->init_prot
|= VM_PROT_READ
;
565 if ( seg
->writeable() )
566 entry
->init_prot
|= VM_PROT_WRITE
| VM_PROT_COW
;
568 entry
->max_prot
= entry
->init_prot
;
569 if ( seg
->hasTrailingZeroFill() ) {
570 _shared_region_mapping_np
* zfentry
= &mappingTable
[++entryIndex
];
571 zfentry
->address
= entry
->address
+ seg
->getFileSize();
572 zfentry
->size
= seg
->getSize() - seg
->getFileSize();
573 zfentry
->file_offset
= 0;
574 zfentry
->init_prot
= entry
->init_prot
| VM_PROT_COW
| VM_PROT_ZF
;
575 zfentry
->max_prot
= zfentry
->init_prot
;
581 ImageLoaderMachO::sharedRegionMakePrivate(const LinkContext
& context
)
583 if ( context
.verboseMapping
)
584 fprintf(stderr
, "dyld: making shared regions private\n");
586 // shared mapping failed, so make private copy of shared region and try mapping private
587 RegionsVector allRegions
;
588 context
.getAllMappedRegions(allRegions
);
589 std::vector
<_shared_region_range_np
> splitSegRegions
;
590 const unsigned int allRegiontCount
= allRegions
.size();
591 for(unsigned int i
=0; i
< allRegiontCount
; ++i
){
592 MappedRegion region
= allRegions
[i
];
593 uint8_t highByte
= region
.address
>> 28;
594 if ( (highByte
== 9) || (highByte
== 0xA) ) {
595 _shared_region_range_np splitRegion
;
596 splitRegion
.address
= region
.address
;
597 splitRegion
.size
= region
.size
;
598 splitSegRegions
.push_back(splitRegion
);
601 int result
= _shared_region_make_private_np(splitSegRegions
.size(), &splitSegRegions
[0]);
602 // notify gdb or other lurkers that this process is no longer using the shared region
603 dyld_all_image_infos
.processDetachedFromSharedRegion
= true;
608 ImageLoaderMachO::sharedRegionMapFile(int fd
,
609 uint64_t offsetInFat
,
612 const LinkContext
& context
)
614 // build table of segments to map
615 const unsigned int segmentCount
= fSegments
.size();
616 const unsigned int extraZeroFillEntries
= getExtraZeroFillEntriesCount();
617 const unsigned int mappingTableCount
= segmentCount
+extraZeroFillEntries
;
618 _shared_region_mapping_np mappingTable
[mappingTableCount
];
619 initMappingTable(offsetInFat
, mappingTable
);
621 uint64_t *slidep
= NULL
;
623 // try to map it in shared
624 int r
= _shared_region_map_file_np(fd
, mappingTableCount
, mappingTable
, slidep
);
626 if(NULL
!= slidep
&& 0 != *slidep
) {
627 // update with actual load addresses
629 if ( context
.verboseMapping
) {
630 fprintf(stderr
, "dyld: Mapping split-seg shared %s\n", this->getPath());
631 for(unsigned int segIndex
=0,entryIndex
=0; segIndex
< segmentCount
; ++segIndex
, ++entryIndex
){
632 Segment
* seg
= fSegments
[segIndex
];
633 const _shared_region_mapping_np
* entry
= &mappingTable
[entryIndex
];
634 if ( (entry
->init_prot
& VM_PROT_ZF
) == 0 )
635 fprintf(stderr
, "%18s at 0x%08lX->0x%08lX\n",
636 seg
->getName(), seg
->getActualLoadAddress(), seg
->getActualLoadAddress()+seg
->getFileSize()-1);
637 if ( entryIndex
< (mappingTableCount
-1) ) {
638 const _shared_region_mapping_np
* nextEntry
= &mappingTable
[entryIndex
+1];
639 if ( (nextEntry
->init_prot
& VM_PROT_ZF
) != 0 ) {
640 uint64_t segOffset
= nextEntry
->address
- entry
->address
;
641 fprintf(stderr
, "%18s at 0x%08lX->0x%08lX\n",
642 seg
->getName(), (uintptr_t)(seg
->getActualLoadAddress() + segOffset
), (uintptr_t)(seg
->getActualLoadAddress() + segOffset
+ nextEntry
->size
- 1));
654 ImageLoaderMachO::sharedRegionMapFilePrivate(int fd
,
655 uint64_t offsetInFat
,
658 const LinkContext
& context
,
661 const unsigned int segmentCount
= fSegments
.size();
663 // adjust base address of segments to pack next to last dylib
664 if ( context
.slideAndPackDylibs
) {
665 uintptr_t lowestReadOnly
= (uintptr_t)(-1);
666 uintptr_t lowestWritable
= (uintptr_t)(-1);
667 for(unsigned int segIndex
=0; segIndex
< segmentCount
; ++segIndex
){
668 Segment
* seg
= fSegments
[segIndex
];
669 uintptr_t segEnd
= seg
->getActualLoadAddress();
670 if ( seg
->writeable() ) {
671 if ( segEnd
< lowestWritable
)
672 lowestWritable
= segEnd
;
675 if ( segEnd
< lowestReadOnly
)
676 lowestReadOnly
= segEnd
;
679 uintptr_t baseAddress
;
680 if ( lowestWritable
- 256*1024*1024 < lowestReadOnly
)
681 baseAddress
= lowestWritable
- 256*1024*1024;
683 baseAddress
= lowestReadOnly
;
684 // record that we want dylb slid to fgNextSplitSegAddress
685 this->setSlide(fgNextSplitSegAddress
- baseAddress
);
688 // build table of segments to map
689 const unsigned int extraZeroFillEntries
= getExtraZeroFillEntriesCount();
690 const unsigned int mappingTableCount
= segmentCount
+extraZeroFillEntries
;
691 _shared_region_mapping_np mappingTable
[mappingTableCount
];
692 initMappingTable(offsetInFat
, mappingTable
);
695 // try map it in privately (don't allow sliding if we pre-calculated the load address to pack dylibs)
698 r
= _shared_region_map_file_with_mmap(fd
, mappingTableCount
, mappingTable
);
700 r
= _shared_region_map_file_np(fd
, mappingTableCount
, mappingTable
, context
.slideAndPackDylibs
? NULL
: &slide
);
703 slide
= (slide
) & (-4096); // round down to page boundary
704 this->setSlide(slide
);
706 if ( context
.verboseMapping
) {
708 fprintf(stderr
, "dyld: Mapping split-seg un-shared %s\n", this->getPath());
710 fprintf(stderr
, "dyld: Mapping split-seg un-shared slid by 0x%08llX %s\n", slide
, this->getPath());
711 for(unsigned int segIndex
=0,entryIndex
=0; segIndex
< segmentCount
; ++segIndex
, ++entryIndex
){
712 Segment
* seg
= fSegments
[segIndex
];
713 const _shared_region_mapping_np
* entry
= &mappingTable
[entryIndex
];
714 if ( (entry
->init_prot
& VM_PROT_ZF
) == 0 )
715 fprintf(stderr
, "%18s at 0x%08lX->0x%08lX\n",
716 seg
->getName(), seg
->getActualLoadAddress(), seg
->getActualLoadAddress()+seg
->getFileSize()-1);
717 if ( entryIndex
< (mappingTableCount
-1) ) {
718 const _shared_region_mapping_np
* nextEntry
= &mappingTable
[entryIndex
+1];
719 if ( (nextEntry
->init_prot
& VM_PROT_ZF
) != 0 ) {
720 uint64_t segOffset
= nextEntry
->address
- entry
->address
;
721 fprintf(stderr
, "%18s at 0x%08lX->0x%08lX (zerofill)\n",
722 seg
->getName(), (uintptr_t)(seg
->getActualLoadAddress() + segOffset
), (uintptr_t)(seg
->getActualLoadAddress() + segOffset
+ nextEntry
->size
- 1));
728 if ( context
.slideAndPackDylibs
) {
729 // calculate where next split-seg dylib can load
730 uintptr_t largestReadOnly
= 0;
731 uintptr_t largestWritable
= 0;
732 for (unsigned int segIndex
=0; segIndex
< segmentCount
; ++segIndex
) {
733 Segment
* seg
= fSegments
[segIndex
];
734 uintptr_t segEnd
= seg
->getActualLoadAddress()+seg
->getSize();
735 segEnd
= (segEnd
+4095) & (-4096); // page align
736 if ( seg
->writeable() ) {
737 if ( segEnd
> largestWritable
)
738 largestWritable
= segEnd
;
741 if ( segEnd
> largestReadOnly
)
742 largestReadOnly
= segEnd
;
745 if ( largestWritable
- 256*1024*1024 > largestReadOnly
)
746 fgNextSplitSegAddress
= largestWritable
- 256*1024*1024;
748 fgNextSplitSegAddress
= largestReadOnly
;
751 if ( context
.slideAndPackDylibs
&& (r
!= 0) )
752 throwf("can't rebase split-seg dylib %s because shared_region_map_file_np() returned %d", this->getPath(), r
);
759 ImageLoaderMachO::sharedRegionLoadFile(int fd
, uint64_t offsetInFat
, uint64_t lenInFat
, uint64_t fileLen
, const LinkContext
& context
)
762 // map in split segment file at random address, then tell kernel to share it
763 void* loadAddress
= 0;
764 loadAddress
= mmap(NULL
, fileLen
, PROT_READ
, MAP_FILE
, fd
, 0);
765 if ( loadAddress
== ((void*)(-1)) )
768 // calculate mapping entries
769 const unsigned int segmentCount
= fSegments
.size();
770 unsigned int extraZeroFillEntries
= getExtraZeroFillEntriesCount();
772 // build table of segments to map
773 const unsigned int mappingTableCount
= segmentCount
+extraZeroFillEntries
;
774 const uintptr_t baseAddress
= fSegments
[0]->getPreferredLoadAddress();
775 sf_mapping mappingTable
[mappingTableCount
];
776 initMappingTable(offsetInFat
, mappingTable
, baseAddress
);
779 // use load_shared_file() to map all segments at once
780 int flags
= 0; // might need to set NEW_LOCAL_SHARED_REGIONS on first use
781 static bool firstTime
= true;
783 // when NEW_LOCAL_SHARED_REGIONS bit is set, this process will get is own shared region
784 // this is used by Xcode to prevent development libraries from polluting the global shared segment
785 if ( context
.sharedRegionMode
== kUsePrivateSharedRegion
)
786 flags
|= NEW_LOCAL_SHARED_REGIONS
;
790 caddr_t base_address
= (caddr_t
)baseAddress
;
792 r
= load_shared_file( (char*)fPath
, // path of file to map shared
793 (char*)loadAddress
, // beginning of local copy of sharable pages in file
794 fileLen
, // end of shareable pages in file
795 &base_address
, // beginning of address range to map
796 mappingTableCount
, // number of entres in array of sf_mapping
797 mappingTable
, // the array of sf_mapping
798 &flags
); // in/out flags
800 // try again but tell kernel it is ok to slide
801 flags
|= ALTERNATE_LOAD_SITE
;
802 r
= load_shared_file((char*)fPath
,(char*)loadAddress
, fileLen
, &base_address
,
803 mappingTableCount
, mappingTable
, &flags
);
806 // unmap file from random address now that they are (hopefully) mapped into the shared region
807 munmap(loadAddress
, fileLen
);
810 if ( base_address
!= (caddr_t
)baseAddress
)
811 this->setSlide((uintptr_t)base_address
- baseAddress
);
812 if ( context
.verboseMapping
) {
813 if ( base_address
!= (caddr_t
)baseAddress
)
814 fprintf(stderr
, "dyld: Mapping split-seg load_shared_alt_region %s\n", this->getPath());
816 fprintf(stderr
, "dyld: Mapping split-seg load_shared %s\n", this->getPath());
817 for(unsigned int segIndex
=0,entryIndex
=0; segIndex
< segmentCount
; ++segIndex
, ++entryIndex
){
818 Segment
* seg
= fSegments
[segIndex
];
819 const sf_mapping
* entry
= &mappingTable
[entryIndex
];
820 if ( (entry
->protection
& VM_PROT_ZF
) == 0 )
821 fprintf(stderr
, "%18s at 0x%08lX->0x%08lX\n",
822 seg
->getName(), seg
->getActualLoadAddress(), seg
->getActualLoadAddress()+seg
->getFileSize()-1);
823 if ( entryIndex
< (mappingTableCount
-1) ) {
824 const sf_mapping
* nextEntry
= &mappingTable
[entryIndex
+1];
825 if ( (nextEntry
->protection
& VM_PROT_ZF
) != 0 ) {
826 fprintf(stderr
, "%18s at 0x%08lX->0x%08lX\n",
827 seg
->getName(), (uintptr_t)(nextEntry
->mapping_offset
+ base_address
), (uintptr_t)(nextEntry
->mapping_offset
+ base_address
+ nextEntry
->size
- 1));
837 ImageLoaderMachO::initMappingTable(uint64_t offsetInFat
,
838 sf_mapping
*mappingTable
,
839 uintptr_t baseAddress
)
841 unsigned int segmentCount
= fSegments
.size();
842 for(unsigned int segIndex
=0,entryIndex
=0; segIndex
< segmentCount
; ++segIndex
, ++entryIndex
){
843 Segment
* seg
= fSegments
[segIndex
];
844 sf_mapping
* entry
= &mappingTable
[entryIndex
];
845 entry
->mapping_offset
= seg
->getPreferredLoadAddress() - baseAddress
;
846 entry
->size
= seg
->getFileSize();
847 entry
->file_offset
= seg
->getFileOffset() + offsetInFat
;
848 entry
->protection
= VM_PROT_NONE
;
849 if ( !seg
->unaccessible() ) {
850 if ( seg
->executable() )
851 entry
->protection
|= VM_PROT_EXECUTE
;
852 if ( seg
->readable() )
853 entry
->protection
|= VM_PROT_READ
;
854 if ( seg
->writeable() )
855 entry
->protection
|= VM_PROT_WRITE
| VM_PROT_COW
;
859 if ( seg
->hasTrailingZeroFill() ) {
860 sf_mapping
* zfentry
= &mappingTable
[++entryIndex
];
861 zfentry
->mapping_offset
= entry
->mapping_offset
+ seg
->getFileSize();
862 zfentry
->size
= seg
->getSize() - seg
->getFileSize();
863 zfentry
->file_offset
= 0;
864 zfentry
->protection
= entry
->protection
| VM_PROT_COW
| VM_PROT_ZF
;
870 #endif // !__LP64__ split segs not supported for 64-bits
873 void ImageLoaderMachO::setSlide(intptr_t slide
)
878 void ImageLoaderMachO::parseLoadCmds()
880 // now that segments are mapped in, get real fMachOData, fLinkEditBase, and fSlide
881 const unsigned int segmentCount
= fSegments
.size();
882 for(unsigned int i
=0; i
< segmentCount
; ++i
){
883 Segment
* seg
= fSegments
[i
];
884 // set up pointer to __LINKEDIT segment
885 if ( strcmp(seg
->getName(),"__LINKEDIT") == 0 )
886 fLinkEditBase
= (uint8_t*)(seg
->getActualLoadAddress() - seg
->getFileOffset());
887 // __TEXT segment always starts at beginning of file and contains mach_header and load commands
888 if ( strcmp(seg
->getName(),"__TEXT") == 0 ) {
889 if ( seg
->hasFixUps() )
890 fTextSegmentWithFixups
= (SegmentMachO
*)seg
;
892 // some segment always starts at beginning of file and contains mach_header and load commands
893 if ( (seg
->getFileOffset() == 0) && (seg
->getFileSize() != 0) ) {
894 fMachOData
= (uint8_t*)(seg
->getActualLoadAddress());
898 // walk load commands (mapped in at start of __TEXT segment)
899 const uint32_t cmd_count
= ((macho_header
*)fMachOData
)->ncmds
;
900 const struct load_command
* const cmds
= (struct load_command
*)&fMachOData
[sizeof(macho_header
)];
901 const struct load_command
* cmd
= cmds
;
902 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
906 const struct symtab_command
* symtab
= (struct symtab_command
*)cmd
;
907 fStrings
= (const char*)&fLinkEditBase
[symtab
->stroff
];
908 fSymbolTable
= (struct macho_nlist
*)(&fLinkEditBase
[symtab
->symoff
]);
912 fDynamicInfo
= (struct dysymtab_command
*)cmd
;
914 case LC_SUB_UMBRELLA
:
915 fHasSubUmbrella
= true;
917 case LC_SUB_FRAMEWORK
:
919 const struct sub_framework_command
* subf
= (struct sub_framework_command
*)cmd
;
920 fReExportThruFramework
= (char*)cmd
+ subf
->umbrella
.offset
;
924 fHasSubLibraries
= true;
926 case LC_ROUTINES_COMMAND
:
927 fDashInit
= (struct macho_routines_command
*)cmd
;
929 case LC_SEGMENT_COMMAND
:
931 const struct macho_segment_command
* seg
= (struct macho_segment_command
*)cmd
;
932 const bool isDataSeg
= (strcmp(seg
->segname
, "__DATA") == 0);
933 const struct macho_section
* const sectionsStart
= (struct macho_section
*)((char*)seg
+ sizeof(struct macho_segment_command
));
934 const struct macho_section
* const sectionsEnd
= §ionsStart
[seg
->nsects
];
935 for (const struct macho_section
* sect
=sectionsStart
; sect
< sectionsEnd
; ++sect
) {
936 const uint8_t type
= sect
->flags
& SECTION_TYPE
;
937 if ( type
== S_MOD_INIT_FUNC_POINTERS
)
938 fModInitSection
= sect
;
939 else if ( type
== S_MOD_TERM_FUNC_POINTERS
)
940 fModTermSection
= sect
;
941 else if ( isDataSeg
&& (strcmp(sect
->sectname
, "__dyld") == 0) ) {
944 else if ( isDataSeg
&& (strcmp(sect
->sectname
, "__image_notify") == 0) )
945 fImageNotifySection
= sect
;
949 case LC_TWOLEVEL_HINTS
:
950 fTwoLevelHints
= (struct twolevel_hints_command
*)cmd
;
954 fDylibID
= (struct dylib_command
*)cmd
;
957 case LC_LOAD_WEAK_DYLIB
:
958 // do nothing, just prevent LC_REQ_DYLD exception from occuring
961 if ( (cmd
->cmd
& LC_REQ_DYLD
) != 0 )
962 throwf("unknown required load command 0x%08X", cmd
->cmd
);
964 cmd
= (const struct load_command
*)(((char*)cmd
)+cmd
->cmdsize
);
971 const char* ImageLoaderMachO::getInstallPath() const
973 if ( fDylibID
!= NULL
) {
974 return (char*)fDylibID
+ fDylibID
->dylib
.name
.offset
;
979 // test if this image is re-exported through parent (the image that loaded this one)
980 bool ImageLoaderMachO::isSubframeworkOf(const LinkContext
& context
, const ImageLoader
* parent
) const
982 if ( fReExportThruFramework
!= NULL
) {
983 // need to match LC_SUB_FRAMEWORK string against the leaf name of the install location of parent...
984 const char* parentInstallPath
= parent
->getInstallPath();
985 if ( parentInstallPath
!= NULL
) {
986 const char* lastSlash
= strrchr(parentInstallPath
, '/');
987 if ( lastSlash
!= NULL
) {
988 if ( strcmp(&lastSlash
[1], fReExportThruFramework
) == 0 )
990 if ( context
.imageSuffix
!= NULL
) {
991 // when DYLD_IMAGE_SUFFIX is used, lastSlash string needs imageSuffix removed from end
992 char reexportAndSuffix
[strlen(context
.imageSuffix
)+strlen(fReExportThruFramework
)+1];
993 strcpy(reexportAndSuffix
, fReExportThruFramework
);
994 strcat(reexportAndSuffix
, context
.imageSuffix
);
995 if ( strcmp(&lastSlash
[1], reexportAndSuffix
) == 0 )
1004 // test if child is re-exported
1005 bool ImageLoaderMachO::hasSubLibrary(const LinkContext
& context
, const ImageLoader
* child
) const
1007 if ( fHasSubLibraries
) {
1008 // need to match LC_SUB_LIBRARY string against the leaf name (without extension) of the install location of child...
1009 const char* childInstallPath
= child
->getInstallPath();
1010 if ( childInstallPath
!= NULL
) {
1011 const char* lastSlash
= strrchr(childInstallPath
, '/');
1012 if ( lastSlash
!= NULL
) {
1013 const char* firstDot
= strchr(lastSlash
, '.');
1015 if ( firstDot
== NULL
)
1016 len
= strlen(lastSlash
);
1018 len
= firstDot
-lastSlash
-1;
1019 char childLeafName
[len
+1];
1020 strncpy(childLeafName
, &lastSlash
[1], len
);
1021 childLeafName
[len
] = '\0';
1022 const uint32_t cmd_count
= ((macho_header
*)fMachOData
)->ncmds
;
1023 const struct load_command
* const cmds
= (struct load_command
*)&fMachOData
[sizeof(macho_header
)];
1024 const struct load_command
* cmd
= cmds
;
1025 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
1027 case LC_SUB_LIBRARY
:
1029 const struct sub_library_command
* lib
= (struct sub_library_command
*)cmd
;
1030 const char* aSubLibName
= (char*)cmd
+ lib
->sub_library
.offset
;
1031 if ( strcmp(aSubLibName
, childLeafName
) == 0 )
1033 if ( context
.imageSuffix
!= NULL
) {
1034 // when DYLD_IMAGE_SUFFIX is used, childLeafName string needs imageSuffix removed from end
1035 char aSubLibNameAndSuffix
[strlen(context
.imageSuffix
)+strlen(aSubLibName
)+1];
1036 strcpy(aSubLibNameAndSuffix
, aSubLibName
);
1037 strcat(aSubLibNameAndSuffix
, context
.imageSuffix
);
1038 if ( strcmp(aSubLibNameAndSuffix
, childLeafName
) == 0 )
1044 cmd
= (const struct load_command
*)(((char*)cmd
)+cmd
->cmdsize
);
1049 if ( fHasSubUmbrella
) {
1050 // need to match LC_SUB_UMBRELLA string against the leaf name of install location of child...
1051 const char* childInstallPath
= child
->getInstallPath();
1052 if ( childInstallPath
!= NULL
) {
1053 const char* lastSlash
= strrchr(childInstallPath
, '/');
1054 if ( lastSlash
!= NULL
) {
1055 const uint32_t cmd_count
= ((macho_header
*)fMachOData
)->ncmds
;
1056 const struct load_command
* const cmds
= (struct load_command
*)&fMachOData
[sizeof(macho_header
)];
1057 const struct load_command
* cmd
= cmds
;
1058 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
1060 case LC_SUB_UMBRELLA
:
1062 const struct sub_umbrella_command
* um
= (struct sub_umbrella_command
*)cmd
;
1063 const char* aSubUmbrellaName
= (char*)cmd
+ um
->sub_umbrella
.offset
;
1064 if ( strcmp(aSubUmbrellaName
, &lastSlash
[1]) == 0 )
1066 if ( context
.imageSuffix
!= NULL
) {
1067 // when DYLD_IMAGE_SUFFIX is used, lastSlash string needs imageSuffix removed from end
1068 char umbrellaAndSuffix
[strlen(context
.imageSuffix
)+strlen(aSubUmbrellaName
)+1];
1069 strcpy(umbrellaAndSuffix
, aSubUmbrellaName
);
1070 strcat(umbrellaAndSuffix
, context
.imageSuffix
);
1071 if ( strcmp(umbrellaAndSuffix
, &lastSlash
[1]) == 0 )
1077 cmd
= (const struct load_command
*)(((char*)cmd
)+cmd
->cmdsize
);
1086 void* ImageLoaderMachO::getMain() const
1088 const uint32_t cmd_count
= ((macho_header
*)fMachOData
)->ncmds
;
1089 const struct load_command
* const cmds
= (struct load_command
*)&fMachOData
[sizeof(macho_header
)];
1090 const struct load_command
* cmd
= cmds
;
1091 for (unsigned long i
= 0; i
< cmd_count
; ++i
) {
1096 const ppc_thread_state_t
* registers
= (ppc_thread_state_t
*)(((char*)cmd
) + 16);
1097 return (void*)registers
->srr0
;
1099 const ppc_thread_state64_t
* registers
= (ppc_thread_state64_t
*)(((char*)cmd
) + 16);
1100 return (void*)registers
->srr0
;
1102 const i386_thread_state_t
* registers
= (i386_thread_state_t
*)(((char*)cmd
) + 16);
1103 return (void*)registers
->eip
;
1105 #warning need processor specific code
1110 cmd
= (const struct load_command
*)(((char*)cmd
)+cmd
->cmdsize
);
1116 uint32_t ImageLoaderMachO::doGetDependentLibraryCount()
1118 const uint32_t cmd_count
= ((macho_header
*)fMachOData
)->ncmds
;
1119 const struct load_command
* const cmds
= (struct load_command
*)&fMachOData
[sizeof(macho_header
)];
1121 const struct load_command
* cmd
= cmds
;
1122 for (unsigned long i
= 0; i
< cmd_count
; ++i
) {
1125 case LC_LOAD_WEAK_DYLIB
:
1129 cmd
= (const struct load_command
*)(((char*)cmd
)+cmd
->cmdsize
);
1134 void ImageLoaderMachO::doGetDependentLibraries(DependentLibrary libs
[])
1137 const uint32_t cmd_count
= ((macho_header
*)fMachOData
)->ncmds
;
1138 const struct load_command
* const cmds
= (struct load_command
*)&fMachOData
[sizeof(macho_header
)];
1139 const struct load_command
* cmd
= cmds
;
1140 for (unsigned long i
= 0; i
< cmd_count
; ++i
) {
1143 case LC_LOAD_WEAK_DYLIB
:
1145 const struct dylib_command
* dylib
= (struct dylib_command
*)cmd
;
1146 DependentLibrary
* lib
= &libs
[index
++];
1147 lib
->name
= (char*)cmd
+ dylib
->dylib
.name
.offset
;
1148 //lib->name = strdup((char*)cmd + dylib->dylib.name.offset);
1150 lib
->info
.checksum
= dylib
->dylib
.timestamp
;
1151 lib
->info
.minVersion
= dylib
->dylib
.compatibility_version
;
1152 lib
->info
.maxVersion
= dylib
->dylib
.current_version
;
1153 lib
->required
= (cmd
->cmd
== LC_LOAD_DYLIB
);
1154 lib
->checksumMatches
= false;
1155 lib
->isReExported
= false;
1159 cmd
= (const struct load_command
*)(((char*)cmd
)+cmd
->cmdsize
);
1163 ImageLoader::LibraryInfo
ImageLoaderMachO::doGetLibraryInfo()
1166 if ( fDylibID
!= NULL
) {
1167 info
.minVersion
= fDylibID
->dylib
.compatibility_version
;
1168 info
.maxVersion
= fDylibID
->dylib
.current_version
;
1169 info
.checksum
= fDylibID
->dylib
.timestamp
;
1172 info
.minVersion
= 0;
1173 info
.maxVersion
= 0;
1180 uintptr_t ImageLoaderMachO::getRelocBase()
1182 if ( fIsSplitSeg
) {
1183 // in split segment libraries r_address is offset from first writable segment
1184 const unsigned int segmentCount
= fSegments
.size();
1185 for(unsigned int i
=0; i
< segmentCount
; ++i
){
1186 Segment
* seg
= fSegments
[i
];
1187 if ( seg
->writeable() ) {
1188 return seg
->getActualLoadAddress();
1193 // in non-split segment libraries r_address is offset from first segment
1194 return fSegments
[0]->getActualLoadAddress();
1198 static inline void otherRelocsPPC(uintptr_t* locationToFix
, uint8_t relocationType
, uint16_t otherHalf
, uintptr_t slide
)
1200 // low 16 bits of 32-bit ppc instructions need fixing
1201 struct ppcInstruction
{ uint16_t opcode
; int16_t immediateValue
; };
1202 ppcInstruction
* instruction
= (ppcInstruction
*)locationToFix
;
1203 //uint32_t before = *((uint32_t*)locationToFix);
1204 switch ( relocationType
)
1206 case PPC_RELOC_LO16
:
1207 instruction
->immediateValue
= ((otherHalf
<< 16) | instruction
->immediateValue
) + slide
;
1209 case PPC_RELOC_HI16
:
1210 instruction
->immediateValue
= ((((instruction
->immediateValue
<< 16) | otherHalf
) + slide
) >> 16);
1212 case PPC_RELOC_HA16
:
1213 int16_t signedOtherHalf
= (int16_t)(otherHalf
& 0xffff);
1214 uint32_t temp
= (instruction
->immediateValue
<< 16) + signedOtherHalf
+ slide
;
1215 if ( (temp
& 0x00008000) != 0 )
1217 instruction
->immediateValue
= temp
>> 16;
1219 //uint32_t after = *((uint32_t*)locationToFix);
1220 //fprintf(stderr, "dyld: ppc fixup %0p type %d from 0x%08X to 0x%08X\n", locationToFix, relocationType, before, after);
1224 void ImageLoaderMachO::doRebase(const LinkContext
& context
)
1226 // if prebound and loaded at prebound address, then no need to rebase
1227 // Note: you might think that the check for allDependentLibrariesAsWhenPreBound() is not needed
1228 // but it is. If a dependent library changed, this image's lazy pointers into that library
1229 // need to be updated (reset back to lazy binding handler). That work is done most easily
1230 // here because there is a PPC_RELOC_PB_LA_PTR reloc record for each lazy pointer.
1231 if ( this->usablePrebinding(context
) && this->usesTwoLevelNameSpace() ) {
1232 // skip rebasing cause prebound and prebinding not disabled
1233 ++fgImagesWithUsedPrebinding
; // bump totals for statistics
1237 // print why prebinding was not used
1238 if ( context
.verbosePrebinding
) {
1239 if ( !this->isPrebindable() ) {
1240 fprintf(stderr
, "dyld: image not prebound, so could not use prebinding in %s\n", this->getPath());
1242 else if ( fSlide
!= 0 ) {
1243 fprintf(stderr
, "dyld: image slid, so could not use prebinding in %s\n", this->getPath());
1245 else if ( !this->allDependentLibrariesAsWhenPreBound() ) {
1246 fprintf(stderr
, "dyld: dependent libraries changed, so could not use prebinding in %s\n", this->getPath());
1248 else if ( !this->usesTwoLevelNameSpace() ){
1249 fprintf(stderr
, "dyld: image uses flat-namespace so, parts of prebinding ignored %s\n", this->getPath());
1252 fprintf(stderr
, "dyld: environment variable disabled use of prebinding in %s\n", this->getPath());
1256 // if there are __TEXT fixups, temporarily make __TEXT writable
1257 if ( fTextSegmentWithFixups
!= NULL
)
1258 fTextSegmentWithFixups
->tempWritable();
1260 // cache this value that is used in the following loop
1261 register const uintptr_t slide
= this->fSlide
;
1263 // loop through all local (internal) relocation records
1264 const uintptr_t relocBase
= this->getRelocBase();
1265 const relocation_info
* const relocsStart
= (struct relocation_info
*)(&fLinkEditBase
[fDynamicInfo
->locreloff
]);
1266 const relocation_info
* const relocsEnd
= &relocsStart
[fDynamicInfo
->nlocrel
];
1267 for (const relocation_info
* reloc
=relocsStart
; reloc
< relocsEnd
; ++reloc
) {
1268 if ( (reloc
->r_address
& R_SCATTERED
) == 0 ) {
1269 if ( reloc
->r_symbolnum
== R_ABS
) {
1270 // ignore absolute relocations
1272 else if (reloc
->r_length
== RELOC_SIZE
) {
1273 switch(reloc
->r_type
) {
1274 case GENERIC_RELOC_VANILLA
:
1275 *((uintptr_t*)(reloc
->r_address
+ relocBase
)) += slide
;
1278 case PPC_RELOC_HI16
:
1279 case PPC_RELOC_LO16
:
1280 case PPC_RELOC_HA16
:
1281 // some tools leave object file relocations in linked images
1282 otherRelocsPPC((uintptr_t*)(reloc
->r_address
+ relocBase
), reloc
->r_type
, reloc
[1].r_address
, slide
);
1283 ++reloc
; // these relocations come in pairs, skip next
1287 throw "unknown local relocation type";
1291 throw "bad local relocation length";
1295 const struct scattered_relocation_info
* sreloc
= (struct scattered_relocation_info
*)reloc
;
1296 if (sreloc
->r_length
== RELOC_SIZE
) {
1297 uintptr_t* locationToFix
= (uintptr_t*)(sreloc
->r_address
+ relocBase
);
1298 switch(sreloc
->r_type
) {
1299 case GENERIC_RELOC_VANILLA
:
1300 *locationToFix
+= slide
;
1302 #if __ppc__ || __ppc64__
1303 case PPC_RELOC_PB_LA_PTR
:
1304 // should only see these in prebound images, and we got here so prebinding is being ignored
1305 *locationToFix
= sreloc
->r_value
+ slide
;
1309 case PPC_RELOC_HI16
:
1310 case PPC_RELOC_LO16
:
1311 case PPC_RELOC_HA16
:
1312 // Metrowerks compiler sometimes leaves object file relocations in linked images???
1313 ++reloc
; // these relocations come in pairs, get next one
1314 otherRelocsPPC(locationToFix
, sreloc
->r_type
, reloc
->r_address
, slide
);
1318 case GENERIC_RELOC_PB_LA_PTR
:
1319 // should only see these in prebound images, and we got here so prebinding is being ignored
1320 *locationToFix
= sreloc
->r_value
+ slide
;
1324 throw "unknown local scattered relocation type";
1328 throw "bad local scattered relocation length";
1333 // if there were __TEXT fixups, restore write protection
1334 if ( fTextSegmentWithFixups
!= NULL
) {
1335 fTextSegmentWithFixups
->setPermissions();
1336 sys_icache_invalidate((void*)fTextSegmentWithFixups
->getActualLoadAddress(), fTextSegmentWithFixups
->getSize());
1340 fgTotalRebaseFixups
+= fDynamicInfo
->nlocrel
;
1344 const struct macho_nlist
* ImageLoaderMachO::binarySearchWithToc(const char* key
, const char stringPool
[], const struct macho_nlist symbols
[],
1345 const struct dylib_table_of_contents toc
[], uint32_t symbolCount
, uint32_t hintIndex
)
1347 int32_t high
= symbolCount
-1;
1348 int32_t mid
= hintIndex
;
1350 // handle out of range hint
1351 if ( mid
>= (int32_t)symbolCount
) {
1352 mid
= symbolCount
/2;
1353 ++ImageLoaderMachO::fgUnhintedBinaryTreeSearchs
;
1356 ++ImageLoaderMachO::fgHintedBinaryTreeSearchs
;
1359 for (int32_t low
= 0; low
<= high
; mid
= (low
+high
)/2) {
1360 const uint32_t index
= toc
[mid
].symbol_index
;
1361 const struct macho_nlist
* pivot
= &symbols
[index
];
1362 const char* pivotStr
= &stringPool
[pivot
->n_un
.n_strx
];
1363 #if LINKEDIT_USAGE_DEBUG
1364 noteAccessedLinkEditAddress(&toc
[mid
]);
1365 noteAccessedLinkEditAddress(pivot
);
1366 noteAccessedLinkEditAddress(pivotStr
);
1368 int cmp
= astrcmp(key
, pivotStr
);
1383 const struct macho_nlist
* ImageLoaderMachO::binarySearch(const char* key
, const char stringPool
[], const struct macho_nlist symbols
[], uint32_t symbolCount
)
1385 ++ImageLoaderMachO::fgUnhintedBinaryTreeSearchs
;
1386 const struct macho_nlist
* base
= symbols
;
1387 for (uint32_t n
= symbolCount
; n
> 0; n
/= 2) {
1388 const struct macho_nlist
* pivot
= &base
[n
/2];
1389 const char* pivotStr
= &stringPool
[pivot
->n_un
.n_strx
];
1390 #if LINKEDIT_USAGE_DEBUG
1391 noteAccessedLinkEditAddress(pivot
);
1392 noteAccessedLinkEditAddress(pivotStr
);
1394 int cmp
= astrcmp(key
, pivotStr
);
1399 // move base to symbol after pivot
1411 const ImageLoader::Symbol
* ImageLoaderMachO::findExportedSymbol(const char* name
, const void* hint
, bool searchReExports
, ImageLoader
** foundIn
) const
1413 const struct macho_nlist
* sym
= NULL
;
1414 const struct twolevel_hint
* theHint
= (struct twolevel_hint
*)hint
;
1415 if ( fDynamicInfo
->tocoff
== 0 )
1416 sym
= binarySearch(name
, fStrings
, &fSymbolTable
[fDynamicInfo
->iextdefsym
], fDynamicInfo
->nextdefsym
);
1418 uint32_t start
= fDynamicInfo
->nextdefsym
;
1419 if ( theHint
!= NULL
)
1420 start
= theHint
->itoc
;
1421 if ( (theHint
== NULL
) || (theHint
->isub_image
== 0) ) {
1422 sym
= binarySearchWithToc(name
, fStrings
, fSymbolTable
, (dylib_table_of_contents
*)&fLinkEditBase
[fDynamicInfo
->tocoff
],
1423 fDynamicInfo
->ntoc
, start
);
1426 if ( sym
!= NULL
) {
1427 if ( foundIn
!= NULL
)
1428 *foundIn
= (ImageLoader
*)this;
1430 return (const Symbol
*)sym
;
1433 if ( searchReExports
) {
1434 // hint might tell us to try a particular subimage
1435 if ( (theHint
!= NULL
) && (theHint
->isub_image
> 0) && (theHint
->isub_image
<= fLibrariesCount
) ) {
1436 // isub_image is an index into a list that is sorted non-rexported images first
1438 ImageLoader
* target
= NULL
;
1439 // pass one, only look at sub-frameworks
1440 for (uint32_t i
=0; i
< fLibrariesCount
; ++i
) {
1441 DependentLibrary
& libInfo
= fLibraries
[i
];
1442 if ( libInfo
.isSubFramework
&& (libInfo
.image
!= NULL
)) {
1443 if ( ++index
== theHint
->isub_image
) {
1444 target
= libInfo
.image
;
1449 if (target
!= NULL
) {
1450 // pass two, only look at non-sub-framework-reexports
1451 for (uint32_t i
=0; i
< fLibrariesCount
; ++i
) {
1452 DependentLibrary
& libInfo
= fLibraries
[i
];
1453 if ( libInfo
.isReExported
&& !libInfo
.isSubFramework
&& (libInfo
.image
!= NULL
) ) {
1454 if ( ++index
== theHint
->isub_image
) {
1455 target
= libInfo
.image
;
1461 if (target
!= NULL
) {
1462 const Symbol
* result
= target
->findExportedSymbol(name
, NULL
, searchReExports
, foundIn
);
1463 if ( result
!= NULL
)
1468 // hint failed, try all sub images
1469 // pass one, only look at sub-frameworks
1470 for(unsigned int i
=0; i
< fLibrariesCount
; ++i
){
1471 DependentLibrary
& libInfo
= fLibraries
[i
];
1472 if ( (libInfo
.image
!= NULL
) && libInfo
.isSubFramework
) {
1473 const Symbol
* result
= libInfo
.image
->findExportedSymbol(name
, NULL
, searchReExports
, foundIn
);
1474 if ( result
!= NULL
)
1478 // pass two, only look at non-sub-framework-reexports
1479 for(unsigned int i
=0; i
< fLibrariesCount
; ++i
){
1480 DependentLibrary
& libInfo
= fLibraries
[i
];
1481 if ( (libInfo
.image
!= NULL
) && libInfo
.isReExported
&& !libInfo
.isSubFramework
) {
1482 const Symbol
* result
= libInfo
.image
->findExportedSymbol(name
, NULL
, searchReExports
, foundIn
);
1483 if ( result
!= NULL
)
1489 // last change: the hint is wrong (non-zero but actually in this image)
1490 if ( (theHint
!= NULL
) && (theHint
->isub_image
!= 0) ) {
1491 sym
= binarySearchWithToc(name
, fStrings
, fSymbolTable
, (dylib_table_of_contents
*)&fLinkEditBase
[fDynamicInfo
->tocoff
],
1492 fDynamicInfo
->ntoc
, fDynamicInfo
->nextdefsym
);
1493 if ( sym
!= NULL
) {
1494 if ( foundIn
!= NULL
)
1495 *foundIn
= (ImageLoader
*)this;
1496 return (const Symbol
*)sym
;
1504 uintptr_t ImageLoaderMachO::getExportedSymbolAddress(const Symbol
* sym
) const
1506 const struct macho_nlist
* nlistSym
= (const struct macho_nlist
*)sym
;
1507 return nlistSym
->n_value
+ fSlide
;
1510 ImageLoader::DefinitionFlags
ImageLoaderMachO::getExportedSymbolInfo(const Symbol
* sym
) const
1512 const struct macho_nlist
* nlistSym
= (const struct macho_nlist
*)sym
;
1513 if ( (nlistSym
->n_desc
& N_WEAK_DEF
) != 0 )
1514 return kWeakDefinition
;
1515 return kNoDefinitionOptions
;
1518 const char* ImageLoaderMachO::getExportedSymbolName(const Symbol
* sym
) const
1520 const struct macho_nlist
* nlistSym
= (const struct macho_nlist
*)sym
;
1521 return &fStrings
[nlistSym
->n_un
.n_strx
];
1524 uint32_t ImageLoaderMachO::getExportedSymbolCount() const
1526 return fDynamicInfo
->nextdefsym
;
1530 const ImageLoader::Symbol
* ImageLoaderMachO::getIndexedExportedSymbol(uint32_t index
) const
1532 if ( index
< fDynamicInfo
->nextdefsym
) {
1533 const struct macho_nlist
* sym
= &fSymbolTable
[fDynamicInfo
->iextdefsym
+ index
];
1534 return (const ImageLoader::Symbol
*)sym
;
1540 uint32_t ImageLoaderMachO::getImportedSymbolCount() const
1542 return fDynamicInfo
->nundefsym
;
1546 const ImageLoader::Symbol
* ImageLoaderMachO::getIndexedImportedSymbol(uint32_t index
) const
1548 if ( index
< fDynamicInfo
->nundefsym
) {
1549 const struct macho_nlist
* sym
= &fSymbolTable
[fDynamicInfo
->iundefsym
+ index
];
1550 return (const ImageLoader::Symbol
*)sym
;
1556 ImageLoader::ReferenceFlags
ImageLoaderMachO::geImportedSymbolInfo(const ImageLoader::Symbol
* sym
) const
1558 const struct macho_nlist
* nlistSym
= (const struct macho_nlist
*)sym
;
1559 ImageLoader::ReferenceFlags flags
= kNoReferenceOptions
;
1560 if ( ((nlistSym
->n_type
& N_TYPE
) == N_UNDF
) && (nlistSym
->n_value
!= 0) )
1561 flags
|= ImageLoader::kTentativeDefinition
;
1562 if ( (nlistSym
->n_desc
& N_WEAK_REF
) != 0 )
1563 flags
|= ImageLoader::kWeakReference
;
1568 const char* ImageLoaderMachO::getImportedSymbolName(const ImageLoader::Symbol
* sym
) const
1570 const struct macho_nlist
* nlistSym
= (const struct macho_nlist
*)sym
;
1571 return &fStrings
[nlistSym
->n_un
.n_strx
];
1575 bool ImageLoaderMachO::getSectionContent(const char* segmentName
, const char* sectionName
, void** start
, size_t* length
)
1577 const uint32_t cmd_count
= ((macho_header
*)fMachOData
)->ncmds
;
1578 const struct load_command
* const cmds
= (struct load_command
*)&fMachOData
[sizeof(macho_header
)];
1579 const struct load_command
* cmd
= cmds
;
1580 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
1582 case LC_SEGMENT_COMMAND
:
1584 const struct macho_segment_command
* seg
= (struct macho_segment_command
*)cmd
;
1585 const struct macho_section
* const sectionsStart
= (struct macho_section
*)((char*)seg
+ sizeof(struct macho_segment_command
));
1586 const struct macho_section
* const sectionsEnd
= §ionsStart
[seg
->nsects
];
1587 for (const struct macho_section
* sect
=sectionsStart
; sect
< sectionsEnd
; ++sect
) {
1588 if ( (strcmp(sect
->segname
, segmentName
) == 0) && (strcmp(sect
->sectname
, sectionName
) == 0) ) {
1589 *start
= (uintptr_t*)(sect
->addr
+ fSlide
);
1590 *length
= sect
->size
;
1597 cmd
= (const struct load_command
*)(((char*)cmd
)+cmd
->cmdsize
);
1603 bool ImageLoaderMachO::findSection(const void* imageInterior
, const char** segmentName
, const char** sectionName
, size_t* sectionOffset
)
1605 const uint32_t cmd_count
= ((macho_header
*)fMachOData
)->ncmds
;
1606 const struct load_command
* const cmds
= (struct load_command
*)&fMachOData
[sizeof(macho_header
)];
1607 const struct load_command
* cmd
= cmds
;
1608 const uintptr_t unslidInteriorAddress
= (uintptr_t)imageInterior
- this->getSlide();
1609 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
1611 case LC_SEGMENT_COMMAND
:
1613 const struct macho_segment_command
* seg
= (struct macho_segment_command
*)cmd
;
1614 if ( (unslidInteriorAddress
>= seg
->vmaddr
) && (unslidInteriorAddress
< (seg
->vmaddr
+seg
->vmsize
)) ) {
1615 const struct macho_section
* const sectionsStart
= (struct macho_section
*)((char*)seg
+ sizeof(struct macho_segment_command
));
1616 const struct macho_section
* const sectionsEnd
= §ionsStart
[seg
->nsects
];
1617 for (const struct macho_section
* sect
=sectionsStart
; sect
< sectionsEnd
; ++sect
) {
1618 if ((sect
->addr
<= unslidInteriorAddress
) && (unslidInteriorAddress
< (sect
->addr
+sect
->size
))) {
1619 if ( segmentName
!= NULL
)
1620 *segmentName
= sect
->segname
;
1621 if ( sectionName
!= NULL
)
1622 *sectionName
= sect
->sectname
;
1623 if ( sectionOffset
!= NULL
)
1624 *sectionOffset
= unslidInteriorAddress
- sect
->addr
;
1632 cmd
= (const struct load_command
*)(((char*)cmd
)+cmd
->cmdsize
);
1638 bool ImageLoaderMachO::symbolRequiresCoalescing(const struct macho_nlist
* symbol
)
1640 // if a define and weak ==> coalesced
1641 if ( ((symbol
->n_type
& N_TYPE
) == N_SECT
) && ((symbol
->n_desc
& N_WEAK_DEF
) != 0) )
1643 // if an undefine and not referencing a weak symbol ==> coalesced
1644 if ( ((symbol
->n_type
& N_TYPE
) != N_SECT
) && ((symbol
->n_desc
& N_REF_TO_WEAK
) != 0) )
1652 static void __attribute__((noreturn
)) throwSymbolNotFound(const char* symbol
, const char* referencedFrom
, const char* expectedIn
)
1654 const char* formatString
= "Symbol not found: %s\n Referenced from: %s\n Expected in: %s\n";
1655 char buf
[strlen(symbol
)+strlen(referencedFrom
)+strlen(expectedIn
)+strlen(formatString
)];
1656 sprintf(buf
, formatString
, symbol
, referencedFrom
, expectedIn
);
1657 throw strdup(buf
); // this is a leak if exception doesn't halt program
1660 uintptr_t ImageLoaderMachO::resolveUndefined(const LinkContext
& context
, const struct macho_nlist
* undefinedSymbol
, bool twoLevel
, ImageLoader
** foundIn
)
1662 const char* symbolName
= &fStrings
[undefinedSymbol
->n_un
.n_strx
];
1664 if ( context
.bindFlat
|| !twoLevel
) {
1667 if ( context
.flatExportFinder(symbolName
, &sym
, foundIn
) )
1668 return (*foundIn
)->getExportedSymbolAddress(sym
);
1669 // if a bundle is loaded privately the above will not find its exports
1670 if ( this->isBundle() && this->hasHiddenExports() ) {
1671 // look in self for needed symbol
1672 sym
= this->findExportedSymbol(symbolName
, NULL
, false, foundIn
);
1674 return (*foundIn
)->getExportedSymbolAddress(sym
);
1676 if ( ((undefinedSymbol
->n_type
& N_PEXT
) != 0) || ((undefinedSymbol
->n_type
& N_TYPE
) == N_SECT
) ) {
1677 // could be a multi-module private_extern internal reference
1678 // the static linker squirrels away the target address in n_value
1679 uintptr_t addr
= undefinedSymbol
->n_value
+ this->fSlide
;
1683 if ( (undefinedSymbol
->n_desc
& N_WEAK_REF
) != 0 ) {
1684 // definition can't be found anywhere
1685 // if reference is weak_import, then it is ok, just return 0
1688 throwSymbolNotFound(symbolName
, this->getPath(), "flat namespace");
1691 // symbol requires searching images with coalesced symbols
1692 if ( this->needsCoalescing() && symbolRequiresCoalescing(undefinedSymbol
) ) {
1694 if ( context
.coalescedExportFinder(symbolName
, &sym
, foundIn
) )
1695 return (*foundIn
)->getExportedSymbolAddress(sym
);
1696 //throwSymbolNotFound(symbolName, this->getPath(), "coalesced namespace");
1697 //fprintf(stderr, "dyld: coalesced symbol %s not found in any coalesced image, falling back to two-level lookup", symbolName);
1702 ImageLoader
* target
= NULL
;
1703 uint8_t ord
= GET_LIBRARY_ORDINAL(undefinedSymbol
->n_desc
);
1704 if ( ord
== EXECUTABLE_ORDINAL
) {
1705 target
= context
.mainExecutable
;
1707 else if ( ord
== SELF_LIBRARY_ORDINAL
) {
1710 else if ( ord
== DYNAMIC_LOOKUP_ORDINAL
) {
1711 // rnielsen: HACKHACK
1714 if ( context
.flatExportFinder(symbolName
, &sym
, foundIn
) )
1715 return (*foundIn
)->getExportedSymbolAddress(sym
);
1716 // no image has exports this symbol
1717 // either report error or hope ZeroLink can just-in-time load an image
1718 context
.undefinedHandler(symbolName
);
1719 // try looking again
1720 if ( context
.flatExportFinder(symbolName
, &sym
, foundIn
) )
1721 return (*foundIn
)->getExportedSymbolAddress(sym
);
1723 throwSymbolNotFound(symbolName
, this->getPath(), "dynamic lookup");
1725 else if ( ord
<= fLibrariesCount
) {
1726 DependentLibrary
& libInfo
= fLibraries
[ord
-1];
1727 target
= libInfo
.image
;
1728 if ( (target
== NULL
) && (((undefinedSymbol
->n_desc
& N_WEAK_REF
) != 0) || !libInfo
.required
) ) {
1729 // if target library not loaded and reference is weak or library is weak return 0
1734 throw "corrupt binary, library ordinal too big";
1737 if ( target
== NULL
) {
1738 fprintf(stderr
, "resolveUndefined(%s) in %s\n", symbolName
, this->getPath());
1739 throw "symbol not found";
1743 if ( fTwoLevelHints
!= NULL
) {
1744 uint32_t symIndex
= undefinedSymbol
- fSymbolTable
;
1745 int32_t undefinedIndex
= symIndex
- fDynamicInfo
->iundefsym
;
1746 if ( (undefinedIndex
>= 0) && ((uint32_t)undefinedIndex
< fDynamicInfo
->nundefsym
) ) {
1747 const struct twolevel_hint
* hints
= (struct twolevel_hint
*)(&fLinkEditBase
[fTwoLevelHints
->offset
]);
1748 const struct twolevel_hint
* theHint
= &hints
[undefinedIndex
];
1749 hint
= (void*)theHint
;
1753 const Symbol
* sym
= target
->findExportedSymbol(symbolName
, hint
, true, foundIn
);
1755 return (*foundIn
)->getExportedSymbolAddress(sym
);
1757 else if ( (undefinedSymbol
->n_type
& N_PEXT
) != 0 ) {
1758 // don't know why the static linker did not eliminate the internal reference to a private extern definition
1760 return undefinedSymbol
->n_value
+ fSlide
;
1762 else if ( (undefinedSymbol
->n_desc
& N_WEAK_REF
) != 0 ) {
1763 // if definition not found and reference is weak return 0
1767 // nowhere to be found
1768 throwSymbolNotFound(symbolName
, this->getPath(), target
->getPath());
1772 // returns if 'addr' is within the address range of section 'sectionIndex'
1773 // fSlide is not used. 'addr' is assumed to be a prebound address in this image
1774 bool ImageLoaderMachO::isAddrInSection(uintptr_t addr
, uint8_t sectionIndex
)
1776 uint8_t currentSectionIndex
= 1;
1777 const uint32_t cmd_count
= ((macho_header
*)fMachOData
)->ncmds
;
1778 const struct load_command
* const cmds
= (struct load_command
*)&fMachOData
[sizeof(macho_header
)];
1779 const struct load_command
* cmd
= cmds
;
1780 for (unsigned long i
= 0; i
< cmd_count
; ++i
) {
1781 if ( cmd
->cmd
== LC_SEGMENT_COMMAND
) {
1782 const struct macho_segment_command
* seg
= (struct macho_segment_command
*)cmd
;
1783 if ( (currentSectionIndex
<= sectionIndex
) && (sectionIndex
< currentSectionIndex
+seg
->nsects
) ) {
1784 // 'sectionIndex' is in this segment, get section info
1785 const struct macho_section
* const sectionsStart
= (struct macho_section
*)((char*)seg
+ sizeof(struct macho_segment_command
));
1786 const struct macho_section
* const section
= §ionsStart
[sectionIndex
-currentSectionIndex
];
1787 return ( (section
->addr
<= addr
) && (addr
< section
->addr
+section
->size
) );
1790 // 'sectionIndex' not in this segment, skip to next segment
1791 currentSectionIndex
+= seg
->nsects
;
1794 cmd
= (const struct load_command
*)(((char*)cmd
)+cmd
->cmdsize
);
1800 void ImageLoaderMachO::doBindExternalRelocations(const LinkContext
& context
, bool onlyCoalescedSymbols
)
1802 const uintptr_t relocBase
= this->getRelocBase();
1803 const bool twoLevel
= this->usesTwoLevelNameSpace();
1804 const bool prebound
= this->isPrebindable();
1806 // if there are __TEXT fixups, temporarily make __TEXT writable
1807 if ( fTextSegmentWithFixups
!= NULL
)
1808 fTextSegmentWithFixups
->tempWritable();
1810 // cache last lookup
1811 const struct macho_nlist
* lastUndefinedSymbol
= 0;
1812 uintptr_t symbolAddr
= 0;
1813 ImageLoader
* image
= NULL
;
1815 // loop through all external relocation records and bind each
1816 const relocation_info
* const relocsStart
= (struct relocation_info
*)(&fLinkEditBase
[fDynamicInfo
->extreloff
]);
1817 const relocation_info
* const relocsEnd
= &relocsStart
[fDynamicInfo
->nextrel
];
1818 for (const relocation_info
* reloc
=relocsStart
; reloc
< relocsEnd
; ++reloc
) {
1819 if (reloc
->r_length
== RELOC_SIZE
) {
1820 switch(reloc
->r_type
) {
1821 case GENERIC_RELOC_VANILLA
:
1823 const struct macho_nlist
* undefinedSymbol
= &fSymbolTable
[reloc
->r_symbolnum
];
1824 // if only processing coalesced symbols and this one does not require coalesceing, skip to next
1825 if ( onlyCoalescedSymbols
&& !symbolRequiresCoalescing(undefinedSymbol
) )
1827 uintptr_t* location
= ((uintptr_t*)(reloc
->r_address
+ relocBase
));
1828 uintptr_t value
= *location
;
1830 if ( reloc
->r_pcrel
) {
1831 value
+= (uintptr_t)location
+ 4 - fSlide
;
1835 // we are doing relocations, so prebinding was not usable
1836 // in a prebound executable, the n_value field of an undefined symbol is set to the address where the symbol was found when prebound
1837 // so, subtracting that gives the initial displacement which we need to add to the newly found symbol address
1838 // if mach-o relocation structs had an "addend" field this complication would not be necessary.
1839 if ( ((undefinedSymbol
->n_type
& N_TYPE
) == N_SECT
) && ((undefinedSymbol
->n_desc
& N_WEAK_DEF
) != 0) ) {
1840 // weak symbols need special casing, since *location may have been prebound to a definition in another image.
1841 // If *location is currently prebound to somewhere in the same section as the weak definition, we assume
1842 // that we can subtract off the weak symbol address to get the addend.
1843 // If prebound elsewhere, we've lost the addend and have to assume it is zero.
1844 // The prebinding to elsewhere only happens with 10.4+ update_prebinding which only operates on a small set of Apple dylibs
1845 if ( (value
== undefinedSymbol
->n_value
) || this->isAddrInSection(value
, undefinedSymbol
->n_sect
) )
1846 value
-= undefinedSymbol
->n_value
;
1851 // is undefined or non-weak symbol, so do subtraction to get addend
1852 value
-= undefinedSymbol
->n_value
;
1855 // if undefinedSymbol is same as last time, then symbolAddr and image will resolve to the same too
1856 if ( undefinedSymbol
!= lastUndefinedSymbol
) {
1857 symbolAddr
= this->resolveUndefined(context
, undefinedSymbol
, twoLevel
, &image
);
1858 lastUndefinedSymbol
= undefinedSymbol
;
1860 if ( context
.verboseBind
) {
1861 const char *path
= NULL
;
1863 path
= image
->getShortName();
1866 fprintf(stderr
, "dyld: bind: %s:0x%08lx = %s:%s, *0x%08lx = 0x%08lx\n",
1867 this->getShortName(), (uintptr_t)location
,
1868 path
, &fStrings
[undefinedSymbol
->n_un
.n_strx
], (uintptr_t)location
, symbolAddr
);
1871 fprintf(stderr
, "dyld: bind: %s:0x%08lx = %s:%s, *0x%08lx = 0x%08lx + %ld\n",
1872 this->getShortName(), (uintptr_t)location
,
1873 path
, &fStrings
[undefinedSymbol
->n_un
.n_strx
], (uintptr_t)location
, symbolAddr
, value
);
1876 value
+= symbolAddr
;
1878 if ( reloc
->r_pcrel
) {
1879 *location
= value
- ((uintptr_t)location
+ 4);
1882 // don't dirty page if prebound value was correct
1883 if ( !prebound
|| (*location
!= value
) )
1887 // don't dirty page if prebound value was correct
1888 if ( !prebound
|| (*location
!= value
) )
1894 throw "unknown external relocation type";
1898 throw "bad external relocation length";
1902 // if there were __TEXT fixups, restore write protection
1903 if ( fTextSegmentWithFixups
!= NULL
) {
1904 fTextSegmentWithFixups
->setPermissions();
1905 sys_icache_invalidate((void*)fTextSegmentWithFixups
->getActualLoadAddress(), fTextSegmentWithFixups
->getSize());
1909 fgTotalBindFixups
+= fDynamicInfo
->nextrel
;
1912 const mach_header
* ImageLoaderMachO::machHeader() const
1914 return (mach_header
*)fMachOData
;
1917 uintptr_t ImageLoaderMachO::getSlide() const
1922 // hmm. maybe this should be up in ImageLoader??
1923 const void* ImageLoaderMachO::getBaseAddress() const
1925 Segment
* seg
= fSegments
[0];
1926 return (const void*)seg
->getActualLoadAddress();
1930 uintptr_t ImageLoaderMachO::doBindLazySymbol(uintptr_t* lazyPointer
, const LinkContext
& context
)
1932 // scan for all non-lazy-pointer sections
1933 const bool twoLevel
= this->usesTwoLevelNameSpace();
1934 const uint32_t cmd_count
= ((macho_header
*)fMachOData
)->ncmds
;
1935 const struct load_command
* const cmds
= (struct load_command
*)&fMachOData
[sizeof(macho_header
)];
1936 const struct load_command
* cmd
= cmds
;
1937 const uint32_t* const indirectTable
= (uint32_t*)&fLinkEditBase
[fDynamicInfo
->indirectsymoff
];
1938 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
1940 case LC_SEGMENT_COMMAND
:
1942 const struct macho_segment_command
* seg
= (struct macho_segment_command
*)cmd
;
1943 const struct macho_section
* const sectionsStart
= (struct macho_section
*)((char*)seg
+ sizeof(struct macho_segment_command
));
1944 const struct macho_section
* const sectionsEnd
= §ionsStart
[seg
->nsects
];
1945 for (const struct macho_section
* sect
=sectionsStart
; sect
< sectionsEnd
; ++sect
) {
1946 const uint8_t type
= sect
->flags
& SECTION_TYPE
;
1947 if ( type
== S_LAZY_SYMBOL_POINTERS
) {
1948 const uint32_t pointerCount
= sect
->size
/ sizeof(uintptr_t);
1949 uintptr_t* const symbolPointers
= (uintptr_t*)(sect
->addr
+ fSlide
);
1950 if ( (lazyPointer
>= symbolPointers
) && (lazyPointer
< &symbolPointers
[pointerCount
]) ) {
1951 const uint32_t indirectTableOffset
= sect
->reserved1
;
1952 const uint32_t lazyIndex
= lazyPointer
- symbolPointers
;
1953 uint32_t symbolIndex
= indirectTable
[indirectTableOffset
+ lazyIndex
];
1954 if ( symbolIndex
!= INDIRECT_SYMBOL_ABS
&& symbolIndex
!= INDIRECT_SYMBOL_LOCAL
) {
1955 ImageLoader
*image
= NULL
;
1956 const char *path
= NULL
;
1957 uintptr_t symbolAddr
= this->resolveUndefined(context
, &fSymbolTable
[symbolIndex
], twoLevel
, &image
);
1958 if ( context
.verboseBind
) {
1959 if(NULL
== path
&& NULL
!= image
) {
1960 path
= image
->getShortName();
1962 fprintf(stderr
, "dyld: bind: %s:%s$%s = %s:%s, *0x%08lx = 0x%08lx\n",
1963 this->getShortName(), &fStrings
[fSymbolTable
[symbolIndex
].n_un
.n_strx
], "lazy_ptr",
1964 path
, &fStrings
[fSymbolTable
[symbolIndex
].n_un
.n_strx
], (uintptr_t)&symbolPointers
[lazyIndex
], symbolAddr
);
1966 if ( NULL
!= context
.bindingHandler
) {
1967 if(NULL
== path
&& NULL
!= image
) {
1968 path
= image
->getPath();
1970 symbolAddr
= (uintptr_t)context
.bindingHandler(path
, &fStrings
[fSymbolTable
[symbolIndex
].n_un
.n_strx
], (void *)symbolAddr
);
1972 symbolPointers
[lazyIndex
] = symbolAddr
;
1974 fgTotalLazyBindFixups
++;
1975 return symbolPointers
[lazyIndex
];
1983 cmd
= (const struct load_command
*)(((char*)cmd
)+cmd
->cmdsize
);
1985 throw "lazy pointer not found";
1990 void ImageLoaderMachO::doBindIndirectSymbolPointers(const LinkContext
& context
, BindingLaziness bindness
, bool onlyCoalescedSymbols
)
1992 // scan for all non-lazy-pointer sections
1993 const bool twoLevel
= this->usesTwoLevelNameSpace();
1994 const uint32_t cmd_count
= ((macho_header
*)fMachOData
)->ncmds
;
1995 const struct load_command
* const cmds
= (struct load_command
*)&fMachOData
[sizeof(macho_header
)];
1996 const struct load_command
* cmd
= cmds
;
1997 const uint32_t* const indirectTable
= (uint32_t*)&fLinkEditBase
[fDynamicInfo
->indirectsymoff
];
1998 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
2000 case LC_SEGMENT_COMMAND
:
2002 const struct macho_segment_command
* seg
= (struct macho_segment_command
*)cmd
;
2003 const struct macho_section
* const sectionsStart
= (struct macho_section
*)((char*)seg
+ sizeof(struct macho_segment_command
));
2004 const struct macho_section
* const sectionsEnd
= §ionsStart
[seg
->nsects
];
2005 for (const struct macho_section
* sect
=sectionsStart
; sect
< sectionsEnd
; ++sect
) {
2006 const uint8_t type
= sect
->flags
& SECTION_TYPE
;
2007 const uint32_t pointerCount
= sect
->size
/ sizeof(uintptr_t);
2008 if ( type
== S_NON_LAZY_SYMBOL_POINTERS
) {
2009 if ( (bindness
== kLazyOnly
) || (bindness
== kLazyOnlyNoDependents
) )
2012 else if ( type
== S_LAZY_SYMBOL_POINTERS
) {
2013 // process each symbol pointer in this section
2014 fgTotalPossibleLazyBindFixups
+= pointerCount
;
2015 if ( bindness
== kNonLazyOnly
)
2021 const uint32_t indirectTableOffset
= sect
->reserved1
;
2022 uintptr_t* const symbolPointers
= (uintptr_t*)(sect
->addr
+ fSlide
);
2023 for (uint32_t j
=0; j
< pointerCount
; ++j
) {
2024 uint32_t symbolIndex
= indirectTable
[indirectTableOffset
+ j
];
2025 if ( symbolIndex
== INDIRECT_SYMBOL_LOCAL
) {
2026 symbolPointers
[j
] += this->fSlide
;
2028 else if ( symbolIndex
== INDIRECT_SYMBOL_ABS
) {
2029 // do nothing since already has absolute address
2032 const struct macho_nlist
* sym
= &fSymbolTable
[symbolIndex
];
2033 if ( symbolIndex
== 0 ) {
2034 // This could be rdar://problem/3534709
2035 if ( ((const macho_header
*)fMachOData
)->filetype
== MH_EXECUTE
) {
2036 static bool alreadyWarned
= false;
2037 if ( (sym
->n_type
& N_TYPE
) != N_UNDF
) {
2038 // The indirect table parallels the (non)lazy pointer sections. For
2039 // instance, to find info about the fifth lazy pointer you look at the
2040 // fifth entry in the indirect table. (try otool -Iv on a file).
2041 // The entry in the indirect table contains an index into the symbol table.
2043 // The bug in ld caused the entry in the indirect table to be zero
2044 // (instead of a magic value that means a local symbol). So, if the
2045 // symbolIndex == 0, we may be encountering the bug, or 0 may be a valid
2046 // symbol table index. The check I put in place is to see if the zero'th
2047 // symbol table entry is an import entry (usually it is a local symbol
2049 if ( context
.verboseWarnings
&& !alreadyWarned
) {
2050 fprintf(stderr
, "dyld: malformed executable '%s', skipping indirect symbol to %s\n",
2051 this->getPath(), &fStrings
[sym
->n_un
.n_strx
]);
2052 alreadyWarned
= true;
2058 ImageLoader
*image
= NULL
;
2059 // if only processing coalesced symbols and this one does not require coalesceing, skip to next
2060 if ( onlyCoalescedSymbols
&& !symbolRequiresCoalescing(sym
) )
2062 uintptr_t symbolAddr
;
2063 symbolAddr
= resolveUndefined(context
, sym
, twoLevel
, &image
);
2064 if ( context
.verboseBind
) {
2065 const char *path
= NULL
;
2067 path
= image
->getShortName();
2069 const char *typeName
;
2070 if ( type
== S_LAZY_SYMBOL_POINTERS
) {
2071 typeName
= "lazy_ptr";
2074 typeName
= "non_lazy_ptr";
2076 fprintf(stderr
, "dyld: bind: %s:%s$%s = %s:%s, *0x%08lx = 0x%08lx\n",
2077 this->getShortName(), &fStrings
[sym
->n_un
.n_strx
], typeName
,
2078 path
, &fStrings
[sym
->n_un
.n_strx
], (uintptr_t)&symbolPointers
[j
], symbolAddr
);
2080 symbolPointers
[j
] = symbolAddr
;
2084 fgTotalBindFixups
+= pointerCount
;
2089 cmd
= (const struct load_command
*)(((char*)cmd
)+cmd
->cmdsize
);
2094 * The address of these symbols are written in to the (__DATA,__dyld) section
2095 * at the following offsets:
2096 * at offset 0 stub_binding_helper_interface
2097 * at offset 4 _dyld_func_lookup
2098 * at offset 8 start_debug_thread
2099 * The 'C' types (if any) for these symbols are ignored here and all are
2100 * declared as longs so the assignment of their address in to the section will
2101 * not require a cast. stub_binding_helper_interface is really a label in the
2102 * assembly code interface for the stub binding. It does not have a meaningful
2103 * 'C' type. _dyld_func_lookup is the routine in dyld_libfuncs.c.
2104 * start_debug_thread is the routine in debug.c.
2106 * For ppc the image's stub_binding_binding_helper is read from:
2107 * at offset 20 the image's stub_binding_binding_helper address
2108 * and saved into to the image structure.
2111 void* dyldLazyBinder
; // filled in at launch by dyld to point into dyld to &stub_binding_helper_interface
2112 void* dyldFuncLookup
; // filled in at launch by dyld to point into dyld to &_dyld_func_lookup
2113 void* startDebugThread
; // debugger interface ???
2114 void* debugPort
; // debugger interface ???
2115 void* debugThread
; // debugger interface ???
2116 void* stubBindHelper
; // filled in at static link time to point to stub helper in image
2117 void* coreDebug
; // ???
2120 // These are defined in dyldStartup.s
2121 extern "C" void stub_binding_helper();
2122 extern "C" bool dyld_func_lookup(const char* name
, uintptr_t* address
);
2125 void ImageLoaderMachO::setupLazyPointerHandler()
2127 if ( fDATAdyld
!= NULL
) {
2128 struct DATAdyld
* dd
= (struct DATAdyld
*)(fDATAdyld
->addr
+ fSlide
);
2129 if ( fDATAdyld
->size
> offsetof(DATAdyld
, dyldLazyBinder
) ) {
2130 if ( dd
->dyldLazyBinder
!= (void*)&stub_binding_helper
)
2131 dd
->dyldLazyBinder
= (void*)&stub_binding_helper
;
2133 if ( fDATAdyld
->size
> offsetof(DATAdyld
, dyldFuncLookup
) ) {
2134 if ( dd
->dyldFuncLookup
!= (void*)&dyld_func_lookup
)
2135 dd
->dyldFuncLookup
= (void*)&dyld_func_lookup
;
2137 //if ( fDATAdyld->size > offsetof(DATAdyld, startDebugThread) )
2138 // dd->startDebugThread = &start_debug_thread;
2140 //if ( fDATAdyld->size > offsetof(DATAdyld, stubBindHelper) )
2141 // save = dd->stubBindHelper;
2146 bool ImageLoaderMachO::usablePrebinding(const LinkContext
& context
) const
2148 // if prebound and loaded at prebound address, and all libraries are same as when this was prebound, then no need to bind
2149 if ( this->isPrebindable() && this->allDependentLibrariesAsWhenPreBound() && (this->getSlide() == 0) ) {
2150 // allow environment variables to disable prebinding
2151 if ( context
.bindFlat
)
2153 switch ( context
.prebindUsage
) {
2154 case kUseAllPrebinding
:
2156 case kUseSplitSegPrebinding
:
2157 return this->fIsSplitSeg
;
2158 case kUseAllButAppPredbinding
:
2159 return (this != context
.mainExecutable
);
2160 case kUseNoPrebinding
:
2167 void ImageLoaderMachO::doBind(const LinkContext
& context
, BindingLaziness bindness
)
2169 // set dyld entry points in image
2170 this->setupLazyPointerHandler();
2172 // if prebound and loaded at prebound address, and all libraries are same as when this was prebound, then no need to bind
2173 // note: flat-namespace binaries need to be imports rebound (even if correctly prebound)
2174 if ( this->usablePrebinding(context
) && this->usesTwoLevelNameSpace() ) {
2175 // if image has coalesced symbols, then these need to be rebound
2176 if ( this->needsCoalescing() ) {
2177 this->doBindExternalRelocations(context
, true);
2178 this->doBindIndirectSymbolPointers(context
, kLazyAndNonLazy
, true);
2180 // skip binding because prebound and prebinding not disabled
2184 // values bound by name are stored two different ways in mach-o
2187 case kLazyAndNonLazy
:
2188 // external relocations are used for data initialized to external symbols
2189 this->doBindExternalRelocations(context
, false);
2192 case kLazyOnlyNoDependents
:
2195 // "indirect symbols" are used for code references to external symbols
2196 this->doBindIndirectSymbolPointers(context
, bindness
, false);
2201 void ImageLoaderMachO::doImageInit(const LinkContext
& context
)
2203 if ( fDashInit
!= NULL
) {
2204 Initializer func
= (Initializer
)(fDashInit
->init_address
+ fSlide
);
2205 if ( context
.verboseInit
)
2206 fprintf(stderr
, "dyld: calling -init function 0x%p in %s\n", func
, this->getPath());
2207 func(context
.argc
, context
.argv
, context
.envp
, context
.apple
);
2211 void ImageLoaderMachO::doModInitFunctions(const LinkContext
& context
)
2213 if ( fModInitSection
!= NULL
) {
2214 Initializer
* inits
= (Initializer
*)(fModInitSection
->addr
+ fSlide
);
2215 const uint32_t count
= fModInitSection
->size
/ sizeof(uintptr_t);
2216 for (uint32_t i
=0; i
< count
; ++i
) {
2217 Initializer func
= inits
[i
];
2218 if ( context
.verboseInit
)
2219 fprintf(stderr
, "dyld: calling initializer function %p in %s\n", func
, this->getPath());
2220 func(context
.argc
, context
.argv
, context
.envp
, context
.apple
);
2226 void ImageLoaderMachO::doInitialization(const LinkContext
& context
)
2228 // mach-o has -init and static initializers
2229 doImageInit(context
);
2230 doModInitFunctions(context
);
2233 bool ImageLoaderMachO::needsInitialization()
2235 return ( (fDashInit
!= NULL
) || (fModInitSection
!= NULL
) );
2239 bool ImageLoaderMachO::needsTermination()
2241 return ( fModTermSection
!= NULL
);
2244 bool ImageLoaderMachO::hasImageNotification()
2246 return ( fImageNotifySection
!= NULL
);
2250 void ImageLoaderMachO::doTermination(const LinkContext
& context
)
2252 if ( fModTermSection
!= NULL
) {
2253 Terminator
* terms
= (Terminator
*)(fModTermSection
->addr
+ fSlide
);
2254 const uint32_t count
= fModTermSection
->size
/ sizeof(uintptr_t);
2255 for (uint32_t i
=count
; i
> 0; --i
) {
2256 Terminator func
= terms
[i
-1];
2257 if ( context
.verboseInit
)
2258 fprintf(stderr
, "dyld: calling terminaton function %p in %s\n", func
, this->getPath());
2264 void ImageLoaderMachO::doNotification(enum dyld_image_mode mode
, uint32_t infoCount
, const struct dyld_image_info info
[])
2266 if ( fImageNotifySection
!= NULL
) {
2267 dyld_image_notifier
* notes
= (dyld_image_notifier
*)(fImageNotifySection
->addr
+ fSlide
);
2268 const uint32_t count
= fImageNotifySection
->size
/ sizeof(uintptr_t);
2269 for (uint32_t i
=count
; i
> 0; --i
) {
2270 dyld_image_notifier func
= notes
[i
-1];
2271 func(mode
, infoCount
, info
);
2276 void ImageLoaderMachO::printStatistics(unsigned int imageCount
)
2278 ImageLoader::printStatistics(imageCount
);
2279 fprintf(stderr
, "total hinted binary tree searches: %d\n", fgHintedBinaryTreeSearchs
);
2280 fprintf(stderr
, "total unhinted binary tree searches: %d\n", fgUnhintedBinaryTreeSearchs
);
2282 #if LINKEDIT_USAGE_DEBUG
2283 fprintf(stderr
, "linkedit pages accessed (%lu):\n", sLinkEditPageBuckets
.size());
2287 void ImageLoaderMachO::doPrebinding(const LinkContext
& context
, time_t timestamp
, uint8_t* fileToPrebind
)
2289 // update __DATA segment
2290 this->applyPrebindingToDATA(fileToPrebind
);
2292 // update load commands
2293 this->applyPrebindingToLoadCommands(context
, fileToPrebind
, timestamp
);
2295 // update symbol table
2296 this->applyPrebindingToLinkEdit(context
, fileToPrebind
);
2299 void ImageLoaderMachO::applyPrebindingToDATA(uint8_t* fileToPrebind
)
2301 const unsigned int segmentCount
= fSegments
.size();
2302 for(unsigned int i
=0; i
< segmentCount
; ++i
) {
2303 SegmentMachO
* seg
= (SegmentMachO
*)fSegments
[i
];
2304 if ( seg
->writeable() ) {
2305 memcpy(&fileToPrebind
[seg
->fFileOffset
], (void*)seg
->getActualLoadAddress(), seg
->fFileSize
);
2310 void ImageLoaderMachO::applyPrebindingToLoadCommands(const LinkContext
& context
, uint8_t* fileToPrebind
, time_t timestamp
)
2312 macho_header
* mh
= (macho_header
*)fileToPrebind
;
2313 const uint32_t cmd_count
= mh
->ncmds
;
2314 const struct load_command
* const cmds
= (struct load_command
*)&fileToPrebind
[sizeof(macho_header
)];
2315 const struct load_command
* cmd
= cmds
;
2316 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
2319 case LC_LOAD_WEAK_DYLIB
:
2321 // update each dylib load command with the timestamp of the target dylib
2322 struct dylib_command
* dylib
= (struct dylib_command
*)cmd
;
2323 const char* name
= (char*)cmd
+ dylib
->dylib
.name
.offset
;
2324 for (const DependentLibrary
* dl
=fLibraries
; dl
< &fLibraries
[fLibrariesCount
]; dl
++) {
2325 if (strcmp(dl
->name
, name
) == 0 ) {
2326 // found matching DependentLibrary for this load command
2327 ImageLoaderMachO
* targetImage
= (ImageLoaderMachO
*)(dl
->image
); // !!! assume only mach-o images are prebound
2328 if ( ! targetImage
->isPrebindable() )
2329 throw "dependent dylib is not prebound";
2330 // if the target is currently being re-prebound then its timestamp will be the same as this one
2331 if ( ! targetImage
->usablePrebinding(context
) ) {
2332 dylib
->dylib
.timestamp
= timestamp
;
2335 // otherwise dependent library is already correctly prebound, so use its checksum
2336 dylib
->dylib
.timestamp
= targetImage
->doGetLibraryInfo().checksum
;
2345 // update the ID of this library with the new timestamp
2346 struct dylib_command
* dylib
= (struct dylib_command
*)cmd
;
2347 dylib
->dylib
.timestamp
= timestamp
;
2350 case LC_SEGMENT_COMMAND
:
2351 // if dylib was rebased, update segment commands
2352 if ( fSlide
!= 0 ) {
2353 struct macho_segment_command
* seg
= (struct macho_segment_command
*)cmd
;
2354 seg
->vmaddr
+= fSlide
;
2355 struct macho_section
* const sectionsStart
= (struct macho_section
*)((char*)seg
+ sizeof(struct macho_segment_command
));
2356 struct macho_section
* const sectionsEnd
= §ionsStart
[seg
->nsects
];
2357 for (struct macho_section
* sect
=sectionsStart
; sect
< sectionsEnd
; ++sect
) {
2358 sect
->addr
+= fSlide
;
2362 case LC_ROUTINES_COMMAND
:
2363 // if dylib was rebased, update -init command
2364 if ( fSlide
!= 0 ) {
2365 struct macho_routines_command
* routines
= (struct macho_routines_command
*)cmd
;
2366 routines
->init_address
+= fSlide
;
2370 cmd
= (const struct load_command
*)(((char*)cmd
)+cmd
->cmdsize
);
2374 void ImageLoaderMachO::applyPrebindingToLinkEdit(const LinkContext
& context
, uint8_t* fileToPrebind
)
2376 // In prebound images, the n_value of the symbol table entry for is the prebound address
2377 // This is needed when prebinding can't be used, to back solve for any possible addend in non-lazy pointers
2378 const char* stringPool
= NULL
;
2379 struct macho_nlist
* symbolTable
= NULL
;
2380 const struct dysymtab_command
* dysymtab
= NULL
;
2382 // get symbol table info
2383 macho_header
* mh
= (macho_header
*)fileToPrebind
;
2384 const uint32_t cmd_count
= mh
->ncmds
;
2385 const struct load_command
* const cmds
= (struct load_command
*)&fileToPrebind
[sizeof(macho_header
)];
2386 const struct load_command
* cmd
= cmds
;
2387 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
2391 const struct symtab_command
* symtab
= (struct symtab_command
*)cmd
;
2392 stringPool
= (const char*)&fileToPrebind
[symtab
->stroff
];
2393 symbolTable
= (struct macho_nlist
*)(&fileToPrebind
[symtab
->symoff
]);
2397 dysymtab
= (struct dysymtab_command
*)cmd
;
2400 cmd
= (const struct load_command
*)(((char*)cmd
)+cmd
->cmdsize
);
2403 // walk all imports and re-resolve their n_value (needed incase prebinding is invalid)
2404 struct macho_nlist
* lastImport
= &symbolTable
[dysymtab
->iundefsym
+dysymtab
->nundefsym
];
2405 for (struct macho_nlist
* entry
= &symbolTable
[dysymtab
->iundefsym
]; entry
< lastImport
; ++entry
) {
2407 entry
->n_value
= this->resolveUndefined(context
, entry
, this->usesTwoLevelNameSpace(), &dummy
);
2410 // walk all exports and slide their n_value
2411 struct macho_nlist
* lastExport
= &symbolTable
[dysymtab
->iextdefsym
+dysymtab
->nextdefsym
];
2412 for (struct macho_nlist
* entry
= &symbolTable
[dysymtab
->iextdefsym
]; entry
< lastExport
; ++entry
) {
2413 if ( (entry
->n_type
& N_TYPE
) == N_SECT
)
2414 entry
->n_value
+= fSlide
;
2417 // walk all local symbols and slide their n_value
2418 struct macho_nlist
* lastLocal
= &symbolTable
[dysymtab
->ilocalsym
+dysymtab
->nlocalsym
];
2419 for (struct macho_nlist
* entry
= &symbolTable
[dysymtab
->ilocalsym
]; entry
< lastLocal
; ++entry
) {
2420 if ( entry
->n_sect
!= NO_SECT
)
2421 entry
->n_value
+= fSlide
;
2424 // walk all local relocations and reset every PPC_RELOC_PB_LA_PTR r_value
2425 relocation_info
* const relocsStart
= (struct relocation_info
*)(&fileToPrebind
[dysymtab
->locreloff
]);
2426 relocation_info
* const relocsEnd
= &relocsStart
[dysymtab
->nlocrel
];
2427 for (relocation_info
* reloc
=relocsStart
; reloc
< relocsEnd
; ++reloc
) {
2428 if ( (reloc
->r_address
& R_SCATTERED
) != 0 ) {
2429 struct scattered_relocation_info
* sreloc
= (struct scattered_relocation_info
*)reloc
;
2430 if (sreloc
->r_length
== RELOC_SIZE
) {
2431 switch(sreloc
->r_type
) {
2432 #if __ppc__ || __ppc64__
2433 case PPC_RELOC_PB_LA_PTR
:
2435 case GENERIC_RELOC_PB_LA_PTR
:
2437 #error unknown architecture
2439 sreloc
->r_value
+= fSlide
;
2446 // if multi-module, fix up objc_addr (10.4 and later runtime does not use this, but we want to keep file checksum consistent)
2447 if ( dysymtab
->nmodtab
!= 0 ) {
2448 dylib_module
* const modulesStart
= (struct dylib_module
*)(&fileToPrebind
[dysymtab
->modtaboff
]);
2449 dylib_module
* const modulesEnd
= &modulesStart
[dysymtab
->nmodtab
];
2450 for (dylib_module
* module=modulesStart
; module < modulesEnd
; ++module) {
2451 if ( module->objc_module_info_size
!= 0 ) {
2452 module->objc_module_info_addr
+= fSlide
;
2458 // file on disk has been reprebound, but we are still mapped to old file
2459 void ImageLoaderMachO::prebindUnmap(const LinkContext
& context
)
2461 // this removes all mappings to the old file, so the kernel will unlink (delete) it.
2462 // We need to leave the load commands and __LINKEDIT in place
2463 for (std::vector
<class Segment
*>::iterator it
=fSegments
.begin(); it
!= fSegments
.end(); ++it
) {
2464 void* segmentAddress
= (void*)((*it
)->getActualLoadAddress());
2465 uintptr_t segmentSize
= (*it
)->getSize();
2466 //fprintf(stderr, "unmapping segment %s at %p for %s\n", (*it)->getName(), segmentAddress, this->getPath());
2467 // save load commands at beginning of __TEXT segment
2468 if ( segmentAddress
== fMachOData
) {
2469 // typically load commands are one or two pages in size, so ok to alloc on stack
2470 uint32_t loadCmdSize
= sizeof(macho_header
) + ((macho_header
*)fMachOData
)->sizeofcmds
;
2471 uint32_t loadCmdPages
= (loadCmdSize
+4095) & (-4096);
2472 uint8_t loadcommands
[loadCmdPages
];
2473 memcpy(loadcommands
, fMachOData
, loadCmdPages
);
2474 // unmap whole __TEXT segment
2475 munmap((void*)(fMachOData
), segmentSize
);
2476 // allocate and copy back mach_header and load commands
2477 vm_address_t addr
= (vm_address_t
)fMachOData
;
2478 int r2
= vm_allocate(mach_task_self(), &addr
, loadCmdPages
, false /*at this address*/);
2480 fprintf(stderr
, "prebindUnmap() vm_allocate for __TEXT %d failed\n", loadCmdPages
);
2481 memcpy((void*)fMachOData
, loadcommands
, loadCmdPages
);
2482 //fprintf(stderr, "copying back load commands to %p size=%u for %s\n", segmentAddress, loadCmdPages, this->getPath());
2484 else if ( strcmp((*it
)->getName(), "__LINKEDIT") == 0 ) {
2485 uint32_t linkEditSize
= segmentSize
;
2486 uint32_t linkEditPages
= (linkEditSize
+4095) & (-4096);
2487 void* linkEditTmp
= malloc(linkEditPages
);
2488 memcpy(linkEditTmp
, segmentAddress
, linkEditPages
);
2489 // unmap whole __LINKEDIT segment
2490 munmap(segmentAddress
, segmentSize
);
2491 vm_address_t addr
= (vm_address_t
)segmentAddress
;
2492 int r2
= vm_allocate(mach_task_self(), &addr
, linkEditPages
, false /*at this address*/);
2494 fprintf(stderr
, "prebindUnmap() vm_allocate for __LINKEDIT %d failed\n", linkEditPages
);
2495 memcpy(segmentAddress
, linkEditTmp
, linkEditPages
);
2496 //fprintf(stderr, "copying back __LINKEDIT to %p size=%u for %s\n", segmentAddress, linkEditPages, this->getPath());
2500 // unmap any other segment
2501 munmap((void*)(segmentAddress
), (*it
)->getSize());
2508 SegmentMachO::SegmentMachO(const struct macho_segment_command
* cmd
, ImageLoaderMachO
* image
, const uint8_t* fileData
)
2509 : fImage(image
), fSize(cmd
->vmsize
), fFileSize(cmd
->filesize
), fFileOffset(cmd
->fileoff
), fPreferredLoadAddress(cmd
->vmaddr
),
2510 fVMProtection(cmd
->initprot
), fHasFixUps(false), fUnMapOnDestruction(false)
2512 strncpy(fName
, cmd
->segname
, 16);
2514 // scan sections for fix-up bit
2515 const struct macho_section
* const sectionsStart
= (struct macho_section
*)((char*)cmd
+ sizeof(struct macho_segment_command
));
2516 const struct macho_section
* const sectionsEnd
= §ionsStart
[cmd
->nsects
];
2517 for (const struct macho_section
* sect
=sectionsStart
; sect
< sectionsEnd
; ++sect
) {
2518 if ( (sect
->flags
& (S_ATTR_EXT_RELOC
| S_ATTR_LOC_RELOC
)) != 0 )
2523 SegmentMachO::~SegmentMachO()
2525 if ( fUnMapOnDestruction
) {
2526 //fprintf(stderr, "unmapping segment %s at 0x%08lX\n", getName(), getActualLoadAddress());
2527 munmap((void*)(this->getActualLoadAddress()), this->getSize());
2531 const ImageLoader
* SegmentMachO::getImage()
2536 const char* SegmentMachO::getName()
2541 uintptr_t SegmentMachO::getSize()
2546 uintptr_t SegmentMachO::getFileSize()
2551 uintptr_t SegmentMachO::getFileOffset()
2556 bool SegmentMachO::readable()
2558 return ( (fVMProtection
& VM_PROT_READ
) != 0);
2561 bool SegmentMachO::writeable()
2563 return ((fVMProtection
& VM_PROT_WRITE
) != 0);
2566 bool SegmentMachO::executable()
2568 return ((fVMProtection
& VM_PROT_EXECUTE
) != 0);
2571 bool SegmentMachO::unaccessible()
2573 return (fVMProtection
== 0);
2576 bool SegmentMachO::hasFixUps()
2581 uintptr_t SegmentMachO::getActualLoadAddress()
2583 return fPreferredLoadAddress
+ fImage
->fSlide
;
2586 uintptr_t SegmentMachO::getPreferredLoadAddress()
2588 return fPreferredLoadAddress
;
2591 bool SegmentMachO::hasPreferredLoadAddress()
2593 return (fPreferredLoadAddress
!= 0);
2596 void SegmentMachO::setUnMapWhenDestructed(bool unmap
)
2598 fUnMapOnDestruction
= unmap
;
2601 static uint32_t *buildCRCTable(void)
2603 uint32_t *table
= new uint32_t[256];
2604 uint32_t p
= 0xedb88320UL
; // standard CRC-32 polynomial
2606 for (unsigned int i
= 0; i
< 256; i
++) {
2608 for (unsigned int j
= 0; j
< 8; j
++) {
2609 if ( c
& 1 ) c
= p
^ (c
>> 1);
2618 uint32_t SegmentMachO::crc32()
2620 if ( !readable() ) return 0;
2622 static uint32_t *crcTable
= NULL
;
2623 if ( !crcTable
) crcTable
= buildCRCTable();
2625 uint32_t crc
= ~(uint32_t)0;
2626 uint8_t *p
= (uint8_t *)getActualLoadAddress();
2627 uint8_t *end
= p
+ getSize();
2629 crc
= crcTable
[(crc
& 0xff) ^ (*p
++)] ^ (crc
>> 8);
2631 return crc
^ ~(uint32_t)0;