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
322 hasSharedRegionMapFile(void)
324 int mib
[CTL_MAXNAME
];
329 mib
[1] = KERN_SHREG_PRIVATIZABLE
;
331 if (sysctl(mib
, 2, &value
, &size
, NULL
, 0) != 0) {
339 ImageLoaderMachO::sharedRegionMapFilePrivateOutside(int fd
,
340 uint64_t offsetInFat
,
343 const LinkContext
& context
)
345 const unsigned int segmentCount
= fSegments
.size();
346 const unsigned int extraZeroFillEntries
= getExtraZeroFillEntriesCount();
347 const unsigned int regionCount
= segmentCount
+extraZeroFillEntries
;
348 _shared_region_mapping_np regions
[regionCount
];
349 initMappingTable(offsetInFat
, regions
);
351 // find space somewhere to allocate split seg
352 bool foundRoom
= false;
353 vm_size_t biggestDiff
= 0;
354 while ( ! foundRoom
) {
356 for(unsigned int i
=0; i
< regionCount
; ++i
) {
357 vm_address_t addr
= sNextAltLoadAddress
+ regions
[i
].address
- regions
[0].address
;
358 vm_size_t size
= regions
[i
].size
;
359 r
= vm_allocate(mach_task_self(), &addr
, size
, false /*only this range*/);
361 // no room here, deallocate what has succeeded so far
362 for(unsigned int j
=0; j
< i
; ++j
) {
363 vm_address_t addr
= sNextAltLoadAddress
+ regions
[j
].address
- regions
[0].address
;
364 vm_size_t size
= regions
[j
].size
;
365 (void)vm_deallocate(mach_task_self(), addr
, size
);
367 sNextAltLoadAddress
+= 0x00100000; // skip ahead 1MB and try again
368 if ( (sNextAltLoadAddress
& 0xF0000000) == 0x90000000 )
369 throw "can't map split seg anywhere";
373 vm_size_t high
= (regions
[i
].address
+ size
- regions
[0].address
) & 0x0FFFFFFF;
374 if ( high
> biggestDiff
)
379 // map in each region
380 uintptr_t slide
= sNextAltLoadAddress
- regions
[0].address
;
381 this->setSlide(slide
);
382 for(unsigned int i
=0; i
< regionCount
; ++i
) {
383 if ( (regions
[i
].init_prot
& VM_PROT_ZF
) != 0 ) {
384 // do nothing vm_allocate() zero-fills by default
387 void* mmapAddress
= (void*)(uintptr_t)(regions
[i
].address
+ slide
);
388 size_t size
= regions
[i
].size
;
390 if ( regions
[i
].init_prot
& VM_PROT_EXECUTE
)
391 protection
|= PROT_EXEC
;
392 if ( regions
[i
].init_prot
& VM_PROT_READ
)
393 protection
|= PROT_READ
;
394 if ( regions
[i
].init_prot
& VM_PROT_WRITE
)
395 protection
|= PROT_WRITE
;
396 off_t offset
= regions
[i
].file_offset
;
397 //fprintf(stderr, "mmap(%p, 0x%08lX, block=0x%08X, %s\n", mmapAddress, size, biggestDiff, fPath);
398 mmapAddress
= mmap(mmapAddress
, size
, protection
, MAP_FILE
| MAP_FIXED
| MAP_PRIVATE
, fd
, offset
);
399 if ( mmapAddress
== ((void*)(-1)) )
403 // set so next maps right after this one
404 sNextAltLoadAddress
+= biggestDiff
;
405 sNextAltLoadAddress
= (sNextAltLoadAddress
+ 4095) & (-4096);
408 if ( context
.verboseMapping
) {
409 fprintf(stderr
, "dyld: Mapping split-seg outside shared region, slid by 0x%08lX %s\n", this->fSlide
, this->getPath());
410 for(unsigned int segIndex
=0,entryIndex
=0; segIndex
< segmentCount
; ++segIndex
, ++entryIndex
){
411 Segment
* seg
= fSegments
[segIndex
];
412 const _shared_region_mapping_np
* entry
= ®ions
[entryIndex
];
413 if ( (entry
->init_prot
& VM_PROT_ZF
) == 0 )
414 fprintf(stderr
, "%18s at 0x%08lX->0x%08lX\n",
415 seg
->getName(), seg
->getActualLoadAddress(), seg
->getActualLoadAddress()+seg
->getFileSize()-1);
416 if ( entryIndex
< (regionCount
-1) ) {
417 const _shared_region_mapping_np
* nextEntry
= ®ions
[entryIndex
+1];
418 if ( (nextEntry
->init_prot
& VM_PROT_ZF
) != 0 ) {
419 uint64_t segOffset
= nextEntry
->address
- entry
->address
;
420 fprintf(stderr
, "%18s at 0x%08lX->0x%08lX (zerofill)\n",
421 seg
->getName(), (uintptr_t)(seg
->getActualLoadAddress() + segOffset
), (uintptr_t)(seg
->getActualLoadAddress() + segOffset
+ nextEntry
->size
- 1));
432 void ImageLoaderMachO::mapSegments(int fd
, uint64_t offsetInFat
, uint64_t lenInFat
, uint64_t fileLen
, const LinkContext
& context
)
434 enum SharedRegionState
436 kSharedRegionStartState
= 0,
437 kSharedRegionLoadFileState
,
438 kSharedRegionMapFileState
,
439 kSharedRegionMapFilePrivateState
,
440 kSharedRegionMapFilePrivateOutsideState
,
442 static SharedRegionState sSharedRegionState
= kSharedRegionStartState
;
444 // non-split segment libraries handled by super class
446 return ImageLoader::mapSegments(fd
, offsetInFat
, lenInFat
, fileLen
, context
);
448 if ( kSharedRegionStartState
== sSharedRegionState
) {
449 if ( hasSharedRegionMapFile() ) {
450 if ( (context
.sharedRegionMode
== kUsePrivateSharedRegion
) || context
.slideAndPackDylibs
) {
451 sharedRegionMakePrivate(context
);
452 sSharedRegionState
= kSharedRegionMapFilePrivateState
;
454 else if ( context
.sharedRegionMode
== kDontUseSharedRegion
) {
455 sSharedRegionState
= kSharedRegionMapFilePrivateOutsideState
;
458 sSharedRegionState
= kSharedRegionMapFileState
;
462 sSharedRegionState
= kSharedRegionLoadFileState
;
466 if ( kSharedRegionLoadFileState
== sSharedRegionState
) {
467 if ( 0 != sharedRegionLoadFile(fd
, offsetInFat
, lenInFat
, fileLen
, context
) ) {
468 sSharedRegionState
= kSharedRegionMapFilePrivateOutsideState
;
472 if ( kSharedRegionMapFileState
== sSharedRegionState
) {
473 if ( 0 != sharedRegionMapFile(fd
, offsetInFat
, lenInFat
, fileLen
, context
) ) {
474 sharedRegionMakePrivate(context
);
475 sSharedRegionState
= kSharedRegionMapFilePrivateState
;
479 if ( kSharedRegionMapFilePrivateState
== sSharedRegionState
) {
480 if ( 0 != sharedRegionMapFilePrivate(fd
, offsetInFat
, lenInFat
, fileLen
, context
) ) {
481 sSharedRegionState
= kSharedRegionMapFilePrivateOutsideState
;
485 if ( kSharedRegionMapFilePrivateOutsideState
== sSharedRegionState
) {
486 if ( 0 != sharedRegionMapFilePrivateOutside(fd
, offsetInFat
, lenInFat
, fileLen
, context
) ) {
487 throw "mapping error";
493 ImageLoaderMachO::getExtraZeroFillEntriesCount()
495 // calculate mapping entries
496 const unsigned int segmentCount
= fSegments
.size();
497 unsigned int extraZeroFillEntries
= 0;
498 for(unsigned int i
=0; i
< segmentCount
; ++i
){
499 Segment
* seg
= fSegments
[i
];
500 if ( seg
->hasTrailingZeroFill() )
501 ++extraZeroFillEntries
;
504 return extraZeroFillEntries
;
508 ImageLoaderMachO::initMappingTable(uint64_t offsetInFat
,
509 _shared_region_mapping_np
*mappingTable
)
511 unsigned int segmentCount
= fSegments
.size();
512 for(unsigned int segIndex
=0,entryIndex
=0; segIndex
< segmentCount
; ++segIndex
, ++entryIndex
){
513 Segment
* seg
= fSegments
[segIndex
];
514 _shared_region_mapping_np
* entry
= &mappingTable
[entryIndex
];
515 entry
->address
= seg
->getActualLoadAddress();
516 entry
->size
= seg
->getFileSize();
517 entry
->file_offset
= seg
->getFileOffset() + offsetInFat
;
518 entry
->init_prot
= VM_PROT_NONE
;
519 if ( !seg
->unaccessible() ) {
520 if ( seg
->executable() )
521 entry
->init_prot
|= VM_PROT_EXECUTE
;
522 if ( seg
->readable() )
523 entry
->init_prot
|= VM_PROT_READ
;
524 if ( seg
->writeable() )
525 entry
->init_prot
|= VM_PROT_WRITE
| VM_PROT_COW
;
527 entry
->max_prot
= entry
->init_prot
;
528 if ( seg
->hasTrailingZeroFill() ) {
529 _shared_region_mapping_np
* zfentry
= &mappingTable
[++entryIndex
];
530 zfentry
->address
= entry
->address
+ seg
->getFileSize();
531 zfentry
->size
= seg
->getSize() - seg
->getFileSize();
532 zfentry
->file_offset
= 0;
533 zfentry
->init_prot
= entry
->init_prot
| VM_PROT_COW
| VM_PROT_ZF
;
534 zfentry
->max_prot
= zfentry
->init_prot
;
540 ImageLoaderMachO::sharedRegionMakePrivate(const LinkContext
& context
)
542 if ( context
.verboseMapping
)
543 fprintf(stderr
, "dyld: making shared regions private\n");
545 // shared mapping failed, so make private copy of shared region and try mapping private
546 RegionsVector allRegions
;
547 context
.getAllMappedRegions(allRegions
);
548 std::vector
<_shared_region_range_np
> splitSegRegions
;
549 const unsigned int allRegiontCount
= allRegions
.size();
550 for(unsigned int i
=0; i
< allRegiontCount
; ++i
){
551 MappedRegion region
= allRegions
[i
];
552 uint8_t highByte
= region
.address
>> 28;
553 if ( (highByte
== 9) || (highByte
== 0xA) ) {
554 _shared_region_range_np splitRegion
;
555 splitRegion
.address
= region
.address
;
556 splitRegion
.size
= region
.size
;
557 splitSegRegions
.push_back(splitRegion
);
560 int result
= _shared_region_make_private_np(splitSegRegions
.size(), &splitSegRegions
[0]);
561 // notify gdb or other lurkers that this process is no longer using the shared region
562 dyld_all_image_infos
.processDetachedFromSharedRegion
= true;
567 ImageLoaderMachO::sharedRegionMapFile(int fd
,
568 uint64_t offsetInFat
,
571 const LinkContext
& context
)
573 // build table of segments to map
574 const unsigned int segmentCount
= fSegments
.size();
575 const unsigned int extraZeroFillEntries
= getExtraZeroFillEntriesCount();
576 const unsigned int mappingTableCount
= segmentCount
+extraZeroFillEntries
;
577 _shared_region_mapping_np mappingTable
[mappingTableCount
];
578 initMappingTable(offsetInFat
, mappingTable
);
580 uint64_t *slidep
= NULL
;
582 // try to map it in shared
583 int r
= _shared_region_map_file_np(fd
, mappingTableCount
, mappingTable
, slidep
);
585 if(NULL
!= slidep
&& 0 != *slidep
) {
586 // update with actual load addresses
588 if ( context
.verboseMapping
) {
589 fprintf(stderr
, "dyld: Mapping split-seg shared %s\n", this->getPath());
590 for(unsigned int segIndex
=0,entryIndex
=0; segIndex
< segmentCount
; ++segIndex
, ++entryIndex
){
591 Segment
* seg
= fSegments
[segIndex
];
592 const _shared_region_mapping_np
* entry
= &mappingTable
[entryIndex
];
593 if ( (entry
->init_prot
& VM_PROT_ZF
) == 0 )
594 fprintf(stderr
, "%18s at 0x%08lX->0x%08lX\n",
595 seg
->getName(), seg
->getActualLoadAddress(), seg
->getActualLoadAddress()+seg
->getFileSize()-1);
596 if ( entryIndex
< (mappingTableCount
-1) ) {
597 const _shared_region_mapping_np
* nextEntry
= &mappingTable
[entryIndex
+1];
598 if ( (nextEntry
->init_prot
& VM_PROT_ZF
) != 0 ) {
599 uint64_t segOffset
= nextEntry
->address
- entry
->address
;
600 fprintf(stderr
, "%18s at 0x%08lX->0x%08lX\n",
601 seg
->getName(), (uintptr_t)(seg
->getActualLoadAddress() + segOffset
), (uintptr_t)(seg
->getActualLoadAddress() + segOffset
+ nextEntry
->size
- 1));
612 ImageLoaderMachO::sharedRegionMapFilePrivate(int fd
,
613 uint64_t offsetInFat
,
616 const LinkContext
& context
)
618 const unsigned int segmentCount
= fSegments
.size();
620 // adjust base address of segments to pack next to last dylib
621 if ( context
.slideAndPackDylibs
) {
622 uintptr_t lowestReadOnly
= (uintptr_t)(-1);
623 uintptr_t lowestWritable
= (uintptr_t)(-1);
624 for(unsigned int segIndex
=0; segIndex
< segmentCount
; ++segIndex
){
625 Segment
* seg
= fSegments
[segIndex
];
626 uintptr_t segEnd
= seg
->getActualLoadAddress();
627 if ( seg
->writeable() ) {
628 if ( segEnd
< lowestWritable
)
629 lowestWritable
= segEnd
;
632 if ( segEnd
< lowestReadOnly
)
633 lowestReadOnly
= segEnd
;
636 uintptr_t baseAddress
;
637 if ( lowestWritable
- 256*1024*1024 < lowestReadOnly
)
638 baseAddress
= lowestWritable
- 256*1024*1024;
640 baseAddress
= lowestReadOnly
;
641 // record that we want dylb slid to fgNextSplitSegAddress
642 this->setSlide(fgNextSplitSegAddress
- baseAddress
);
645 // build table of segments to map
646 const unsigned int extraZeroFillEntries
= getExtraZeroFillEntriesCount();
647 const unsigned int mappingTableCount
= segmentCount
+extraZeroFillEntries
;
648 _shared_region_mapping_np mappingTable
[mappingTableCount
];
649 initMappingTable(offsetInFat
, mappingTable
);
652 // try map it in privately (don't allow sliding if we pre-calculated the load address to pack dylibs)
653 int r
= _shared_region_map_file_np(fd
, mappingTableCount
, mappingTable
, context
.slideAndPackDylibs
? NULL
: &slide
);
656 slide
= (slide
) & (-4096); // round down to page boundary
657 this->setSlide(slide
);
659 if ( context
.verboseMapping
) {
661 fprintf(stderr
, "dyld: Mapping split-seg un-shared %s\n", this->getPath());
663 fprintf(stderr
, "dyld: Mapping split-seg un-shared slid by 0x%08llX %s\n", slide
, this->getPath());
664 for(unsigned int segIndex
=0,entryIndex
=0; segIndex
< segmentCount
; ++segIndex
, ++entryIndex
){
665 Segment
* seg
= fSegments
[segIndex
];
666 const _shared_region_mapping_np
* entry
= &mappingTable
[entryIndex
];
667 if ( (entry
->init_prot
& VM_PROT_ZF
) == 0 )
668 fprintf(stderr
, "%18s at 0x%08lX->0x%08lX\n",
669 seg
->getName(), seg
->getActualLoadAddress(), seg
->getActualLoadAddress()+seg
->getFileSize()-1);
670 if ( entryIndex
< (mappingTableCount
-1) ) {
671 const _shared_region_mapping_np
* nextEntry
= &mappingTable
[entryIndex
+1];
672 if ( (nextEntry
->init_prot
& VM_PROT_ZF
) != 0 ) {
673 uint64_t segOffset
= nextEntry
->address
- entry
->address
;
674 fprintf(stderr
, "%18s at 0x%08lX->0x%08lX (zerofill)\n",
675 seg
->getName(), (uintptr_t)(seg
->getActualLoadAddress() + segOffset
), (uintptr_t)(seg
->getActualLoadAddress() + segOffset
+ nextEntry
->size
- 1));
681 if ( context
.slideAndPackDylibs
) {
682 // calculate where next split-seg dylib can load
683 uintptr_t largestReadOnly
= 0;
684 uintptr_t largestWritable
= 0;
685 for (unsigned int segIndex
=0; segIndex
< segmentCount
; ++segIndex
) {
686 Segment
* seg
= fSegments
[segIndex
];
687 uintptr_t segEnd
= seg
->getActualLoadAddress()+seg
->getSize();
688 segEnd
= (segEnd
+4095) & (-4096); // page align
689 if ( seg
->writeable() ) {
690 if ( segEnd
> largestWritable
)
691 largestWritable
= segEnd
;
694 if ( segEnd
> largestReadOnly
)
695 largestReadOnly
= segEnd
;
698 if ( largestWritable
- 256*1024*1024 > largestReadOnly
)
699 fgNextSplitSegAddress
= largestWritable
- 256*1024*1024;
701 fgNextSplitSegAddress
= largestReadOnly
;
704 if ( context
.slideAndPackDylibs
&& (r
!= 0) )
705 throw "can't rebase split-seg dylib";
712 ImageLoaderMachO::sharedRegionLoadFile(int fd
, uint64_t offsetInFat
, uint64_t lenInFat
, uint64_t fileLen
, const LinkContext
& context
)
715 // map in split segment file at random address, then tell kernel to share it
716 void* loadAddress
= 0;
717 loadAddress
= mmap(NULL
, fileLen
, PROT_READ
, MAP_FILE
, fd
, 0);
718 if ( loadAddress
== ((void*)(-1)) )
721 // calculate mapping entries
722 const unsigned int segmentCount
= fSegments
.size();
723 unsigned int extraZeroFillEntries
= getExtraZeroFillEntriesCount();
725 // build table of segments to map
726 const unsigned int mappingTableCount
= segmentCount
+extraZeroFillEntries
;
727 const uintptr_t baseAddress
= fSegments
[0]->getPreferredLoadAddress();
728 sf_mapping mappingTable
[mappingTableCount
];
729 initMappingTable(offsetInFat
, mappingTable
, baseAddress
);
732 // use load_shared_file() to map all segments at once
733 int flags
= 0; // might need to set NEW_LOCAL_SHARED_REGIONS on first use
734 static bool firstTime
= true;
736 // when NEW_LOCAL_SHARED_REGIONS bit is set, this process will get is own shared region
737 // this is used by Xcode to prevent development libraries from polluting the global shared segment
738 if ( context
.sharedRegionMode
== kUsePrivateSharedRegion
)
739 flags
|= NEW_LOCAL_SHARED_REGIONS
;
743 caddr_t base_address
= (caddr_t
)baseAddress
;
745 r
= load_shared_file( (char*)fPath
, // path of file to map shared
746 (char*)loadAddress
, // beginning of local copy of sharable pages in file
747 fileLen
, // end of shareable pages in file
748 &base_address
, // beginning of address range to map
749 mappingTableCount
, // number of entres in array of sf_mapping
750 mappingTable
, // the array of sf_mapping
751 &flags
); // in/out flags
753 // try again but tell kernel it is ok to slide
754 flags
|= ALTERNATE_LOAD_SITE
;
755 r
= load_shared_file((char*)fPath
,(char*)loadAddress
, fileLen
, &base_address
,
756 mappingTableCount
, mappingTable
, &flags
);
759 // unmap file from random address now that they are (hopefully) mapped into the shared region
760 munmap(loadAddress
, fileLen
);
763 if ( base_address
!= (caddr_t
)baseAddress
)
764 this->setSlide((uintptr_t)base_address
- baseAddress
);
765 if ( context
.verboseMapping
) {
766 if ( base_address
!= (caddr_t
)baseAddress
)
767 fprintf(stderr
, "dyld: Mapping split-seg load_shared_alt_region %s\n", this->getPath());
769 fprintf(stderr
, "dyld: Mapping split-seg load_shared %s\n", this->getPath());
770 for(unsigned int segIndex
=0,entryIndex
=0; segIndex
< segmentCount
; ++segIndex
, ++entryIndex
){
771 Segment
* seg
= fSegments
[segIndex
];
772 const sf_mapping
* entry
= &mappingTable
[entryIndex
];
773 if ( (entry
->protection
& VM_PROT_ZF
) == 0 )
774 fprintf(stderr
, "%18s at 0x%08lX->0x%08lX\n",
775 seg
->getName(), seg
->getActualLoadAddress(), seg
->getActualLoadAddress()+seg
->getFileSize()-1);
776 if ( entryIndex
< (mappingTableCount
-1) ) {
777 const sf_mapping
* nextEntry
= &mappingTable
[entryIndex
+1];
778 if ( (nextEntry
->protection
& VM_PROT_ZF
) != 0 ) {
779 fprintf(stderr
, "%18s at 0x%08lX->0x%08lX\n",
780 seg
->getName(), (uintptr_t)(nextEntry
->mapping_offset
+ base_address
), (uintptr_t)(nextEntry
->mapping_offset
+ base_address
+ nextEntry
->size
- 1));
790 ImageLoaderMachO::initMappingTable(uint64_t offsetInFat
,
791 sf_mapping
*mappingTable
,
792 uintptr_t baseAddress
)
794 unsigned int segmentCount
= fSegments
.size();
795 for(unsigned int segIndex
=0,entryIndex
=0; segIndex
< segmentCount
; ++segIndex
, ++entryIndex
){
796 Segment
* seg
= fSegments
[segIndex
];
797 sf_mapping
* entry
= &mappingTable
[entryIndex
];
798 entry
->mapping_offset
= seg
->getPreferredLoadAddress() - baseAddress
;
799 entry
->size
= seg
->getFileSize();
800 entry
->file_offset
= seg
->getFileOffset() + offsetInFat
;
801 entry
->protection
= VM_PROT_NONE
;
802 if ( !seg
->unaccessible() ) {
803 if ( seg
->executable() )
804 entry
->protection
|= VM_PROT_EXECUTE
;
805 if ( seg
->readable() )
806 entry
->protection
|= VM_PROT_READ
;
807 if ( seg
->writeable() )
808 entry
->protection
|= VM_PROT_WRITE
| VM_PROT_COW
;
812 if ( seg
->hasTrailingZeroFill() ) {
813 sf_mapping
* zfentry
= &mappingTable
[++entryIndex
];
814 zfentry
->mapping_offset
= entry
->mapping_offset
+ seg
->getFileSize();
815 zfentry
->size
= seg
->getSize() - seg
->getFileSize();
816 zfentry
->file_offset
= 0;
817 zfentry
->protection
= entry
->protection
| VM_PROT_COW
| VM_PROT_ZF
;
823 #endif // !__LP64__ split segs not supported for 64-bits
826 void ImageLoaderMachO::setSlide(intptr_t slide
)
831 void ImageLoaderMachO::parseLoadCmds()
833 // now that segments are mapped in, get real fMachOData, fLinkEditBase, and fSlide
834 const unsigned int segmentCount
= fSegments
.size();
835 for(unsigned int i
=0; i
< segmentCount
; ++i
){
836 Segment
* seg
= fSegments
[i
];
837 // set up pointer to __LINKEDIT segment
838 if ( strcmp(seg
->getName(),"__LINKEDIT") == 0 )
839 fLinkEditBase
= (uint8_t*)(seg
->getActualLoadAddress() - seg
->getFileOffset());
840 // __TEXT segment always starts at beginning of file and contains mach_header and load commands
841 if ( strcmp(seg
->getName(),"__TEXT") == 0 ) {
842 if ( seg
->hasFixUps() )
843 fTextSegmentWithFixups
= (SegmentMachO
*)seg
;
845 // some segment always starts at beginning of file and contains mach_header and load commands
846 if ( (seg
->getFileOffset() == 0) && (seg
->getFileSize() != 0) ) {
847 fMachOData
= (uint8_t*)(seg
->getActualLoadAddress());
851 // walk load commands (mapped in at start of __TEXT segment)
852 const uint32_t cmd_count
= ((macho_header
*)fMachOData
)->ncmds
;
853 const struct load_command
* const cmds
= (struct load_command
*)&fMachOData
[sizeof(macho_header
)];
854 const struct load_command
* cmd
= cmds
;
855 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
859 const struct symtab_command
* symtab
= (struct symtab_command
*)cmd
;
860 fStrings
= (const char*)&fLinkEditBase
[symtab
->stroff
];
861 fSymbolTable
= (struct macho_nlist
*)(&fLinkEditBase
[symtab
->symoff
]);
865 fDynamicInfo
= (struct dysymtab_command
*)cmd
;
867 case LC_SUB_UMBRELLA
:
868 fHasSubUmbrella
= true;
870 case LC_SUB_FRAMEWORK
:
872 const struct sub_framework_command
* subf
= (struct sub_framework_command
*)cmd
;
873 fReExportThruFramework
= (char*)cmd
+ subf
->umbrella
.offset
;
877 fHasSubLibraries
= true;
879 case LC_ROUTINES_COMMAND
:
880 fDashInit
= (struct macho_routines_command
*)cmd
;
882 case LC_SEGMENT_COMMAND
:
884 const struct macho_segment_command
* seg
= (struct macho_segment_command
*)cmd
;
885 const bool isDataSeg
= (strcmp(seg
->segname
, "__DATA") == 0);
886 const struct macho_section
* const sectionsStart
= (struct macho_section
*)((char*)seg
+ sizeof(struct macho_segment_command
));
887 const struct macho_section
* const sectionsEnd
= §ionsStart
[seg
->nsects
];
888 for (const struct macho_section
* sect
=sectionsStart
; sect
< sectionsEnd
; ++sect
) {
889 const uint8_t type
= sect
->flags
& SECTION_TYPE
;
890 if ( type
== S_MOD_INIT_FUNC_POINTERS
)
891 fModInitSection
= sect
;
892 else if ( type
== S_MOD_TERM_FUNC_POINTERS
)
893 fModTermSection
= sect
;
894 else if ( isDataSeg
&& (strcmp(sect
->sectname
, "__dyld") == 0) )
896 else if ( isDataSeg
&& (strcmp(sect
->sectname
, "__image_notify") == 0) )
897 fImageNotifySection
= sect
;
901 case LC_TWOLEVEL_HINTS
:
902 fTwoLevelHints
= (struct twolevel_hints_command
*)cmd
;
906 fDylibID
= (struct dylib_command
*)cmd
;
910 cmd
= (const struct load_command
*)(((char*)cmd
)+cmd
->cmdsize
);
917 const char* ImageLoaderMachO::getInstallPath() const
919 if ( fDylibID
!= NULL
) {
920 return (char*)fDylibID
+ fDylibID
->dylib
.name
.offset
;
925 // test if this image is re-exported through parent (the image that loaded this one)
926 bool ImageLoaderMachO::isSubframeworkOf(const LinkContext
& context
, const ImageLoader
* parent
) const
928 if ( fReExportThruFramework
!= NULL
) {
929 // need to match LC_SUB_FRAMEWORK string against the leaf name of the install location of parent...
930 const char* parentInstallPath
= parent
->getInstallPath();
931 if ( parentInstallPath
!= NULL
) {
932 const char* lastSlash
= strrchr(parentInstallPath
, '/');
933 if ( lastSlash
!= NULL
) {
934 if ( strcmp(&lastSlash
[1], fReExportThruFramework
) == 0 )
936 if ( context
.imageSuffix
!= NULL
) {
937 // when DYLD_IMAGE_SUFFIX is used, lastSlash string needs imageSuffix removed from end
938 char reexportAndSuffix
[strlen(context
.imageSuffix
)+strlen(fReExportThruFramework
)+1];
939 strcpy(reexportAndSuffix
, fReExportThruFramework
);
940 strcat(reexportAndSuffix
, context
.imageSuffix
);
941 if ( strcmp(&lastSlash
[1], reexportAndSuffix
) == 0 )
950 // test if child is re-exported
951 bool ImageLoaderMachO::hasSubLibrary(const LinkContext
& context
, const ImageLoader
* child
) const
953 if ( fHasSubLibraries
) {
954 // need to match LC_SUB_LIBRARY string against the leaf name (without extension) of the install location of child...
955 const char* childInstallPath
= child
->getInstallPath();
956 if ( childInstallPath
!= NULL
) {
957 const char* lastSlash
= strrchr(childInstallPath
, '/');
958 if ( lastSlash
!= NULL
) {
959 const char* firstDot
= strchr(lastSlash
, '.');
961 if ( firstDot
== NULL
)
962 len
= strlen(lastSlash
);
964 len
= firstDot
-lastSlash
-1;
965 char childLeafName
[len
+1];
966 strncpy(childLeafName
, &lastSlash
[1], len
);
967 childLeafName
[len
] = '\0';
968 const uint32_t cmd_count
= ((macho_header
*)fMachOData
)->ncmds
;
969 const struct load_command
* const cmds
= (struct load_command
*)&fMachOData
[sizeof(macho_header
)];
970 const struct load_command
* cmd
= cmds
;
971 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
975 const struct sub_library_command
* lib
= (struct sub_library_command
*)cmd
;
976 const char* aSubLibName
= (char*)cmd
+ lib
->sub_library
.offset
;
977 if ( strcmp(aSubLibName
, childLeafName
) == 0 )
979 if ( context
.imageSuffix
!= NULL
) {
980 // when DYLD_IMAGE_SUFFIX is used, childLeafName string needs imageSuffix removed from end
981 char aSubLibNameAndSuffix
[strlen(context
.imageSuffix
)+strlen(aSubLibName
)+1];
982 strcpy(aSubLibNameAndSuffix
, aSubLibName
);
983 strcat(aSubLibNameAndSuffix
, context
.imageSuffix
);
984 if ( strcmp(aSubLibNameAndSuffix
, childLeafName
) == 0 )
990 cmd
= (const struct load_command
*)(((char*)cmd
)+cmd
->cmdsize
);
995 if ( fHasSubUmbrella
) {
996 // need to match LC_SUB_UMBRELLA string against the leaf name of install location of child...
997 const char* childInstallPath
= child
->getInstallPath();
998 if ( childInstallPath
!= NULL
) {
999 const char* lastSlash
= strrchr(childInstallPath
, '/');
1000 if ( lastSlash
!= NULL
) {
1001 const uint32_t cmd_count
= ((macho_header
*)fMachOData
)->ncmds
;
1002 const struct load_command
* const cmds
= (struct load_command
*)&fMachOData
[sizeof(macho_header
)];
1003 const struct load_command
* cmd
= cmds
;
1004 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
1006 case LC_SUB_UMBRELLA
:
1008 const struct sub_umbrella_command
* um
= (struct sub_umbrella_command
*)cmd
;
1009 const char* aSubUmbrellaName
= (char*)cmd
+ um
->sub_umbrella
.offset
;
1010 if ( strcmp(aSubUmbrellaName
, &lastSlash
[1]) == 0 )
1012 if ( context
.imageSuffix
!= NULL
) {
1013 // when DYLD_IMAGE_SUFFIX is used, lastSlash string needs imageSuffix removed from end
1014 char umbrellaAndSuffix
[strlen(context
.imageSuffix
)+strlen(aSubUmbrellaName
)+1];
1015 strcpy(umbrellaAndSuffix
, aSubUmbrellaName
);
1016 strcat(umbrellaAndSuffix
, context
.imageSuffix
);
1017 if ( strcmp(umbrellaAndSuffix
, &lastSlash
[1]) == 0 )
1023 cmd
= (const struct load_command
*)(((char*)cmd
)+cmd
->cmdsize
);
1032 void* ImageLoaderMachO::getMain() const
1034 const uint32_t cmd_count
= ((macho_header
*)fMachOData
)->ncmds
;
1035 const struct load_command
* const cmds
= (struct load_command
*)&fMachOData
[sizeof(macho_header
)];
1036 const struct load_command
* cmd
= cmds
;
1037 for (unsigned long i
= 0; i
< cmd_count
; ++i
) {
1042 const ppc_thread_state_t
* registers
= (ppc_thread_state_t
*)(((char*)cmd
) + 16);
1043 return (void*)registers
->srr0
;
1045 const ppc_thread_state64_t
* registers
= (ppc_thread_state64_t
*)(((char*)cmd
) + 16);
1046 return (void*)registers
->srr0
;
1048 const i386_thread_state_t
* registers
= (i386_thread_state_t
*)(((char*)cmd
) + 16);
1049 return (void*)registers
->eip
;
1051 #warning need processor specific code
1056 cmd
= (const struct load_command
*)(((char*)cmd
)+cmd
->cmdsize
);
1062 uint32_t ImageLoaderMachO::doGetDependentLibraryCount()
1064 const uint32_t cmd_count
= ((macho_header
*)fMachOData
)->ncmds
;
1065 const struct load_command
* const cmds
= (struct load_command
*)&fMachOData
[sizeof(macho_header
)];
1067 const struct load_command
* cmd
= cmds
;
1068 for (unsigned long i
= 0; i
< cmd_count
; ++i
) {
1071 case LC_LOAD_WEAK_DYLIB
:
1075 cmd
= (const struct load_command
*)(((char*)cmd
)+cmd
->cmdsize
);
1080 void ImageLoaderMachO::doGetDependentLibraries(DependentLibrary libs
[])
1083 const uint32_t cmd_count
= ((macho_header
*)fMachOData
)->ncmds
;
1084 const struct load_command
* const cmds
= (struct load_command
*)&fMachOData
[sizeof(macho_header
)];
1085 const struct load_command
* cmd
= cmds
;
1086 for (unsigned long i
= 0; i
< cmd_count
; ++i
) {
1089 case LC_LOAD_WEAK_DYLIB
:
1091 const struct dylib_command
* dylib
= (struct dylib_command
*)cmd
;
1092 DependentLibrary
* lib
= &libs
[index
++];
1093 lib
->name
= (char*)cmd
+ dylib
->dylib
.name
.offset
;
1094 //lib->name = strdup((char*)cmd + dylib->dylib.name.offset);
1096 lib
->info
.checksum
= dylib
->dylib
.timestamp
;
1097 lib
->info
.minVersion
= dylib
->dylib
.compatibility_version
;
1098 lib
->info
.maxVersion
= dylib
->dylib
.current_version
;
1099 lib
->required
= (cmd
->cmd
== LC_LOAD_DYLIB
);
1100 lib
->checksumMatches
= false;
1101 lib
->isReExported
= false;
1105 cmd
= (const struct load_command
*)(((char*)cmd
)+cmd
->cmdsize
);
1109 ImageLoader::LibraryInfo
ImageLoaderMachO::doGetLibraryInfo()
1112 if ( fDylibID
!= NULL
) {
1113 info
.minVersion
= fDylibID
->dylib
.compatibility_version
;
1114 info
.maxVersion
= fDylibID
->dylib
.current_version
;
1115 info
.checksum
= fDylibID
->dylib
.timestamp
;
1118 info
.minVersion
= 0;
1119 info
.maxVersion
= 0;
1126 uintptr_t ImageLoaderMachO::getRelocBase()
1128 if ( fIsSplitSeg
) {
1129 // in split segment libraries r_address is offset from first writable segment
1130 const unsigned int segmentCount
= fSegments
.size();
1131 for(unsigned int i
=0; i
< segmentCount
; ++i
){
1132 Segment
* seg
= fSegments
[i
];
1133 if ( seg
->writeable() ) {
1134 return seg
->getActualLoadAddress();
1139 // in non-split segment libraries r_address is offset from first segment
1140 return fSegments
[0]->getActualLoadAddress();
1144 static inline void otherRelocsPPC(uintptr_t* locationToFix
, uint8_t relocationType
, uint16_t otherHalf
, uintptr_t slide
)
1146 // low 16 bits of 32-bit ppc instructions need fixing
1147 struct ppcInstruction
{ uint16_t opcode
; int16_t immediateValue
; };
1148 ppcInstruction
* instruction
= (ppcInstruction
*)locationToFix
;
1149 //uint32_t before = *((uint32_t*)locationToFix);
1150 switch ( relocationType
)
1152 case PPC_RELOC_LO16
:
1153 instruction
->immediateValue
= ((otherHalf
<< 16) | instruction
->immediateValue
) + slide
;
1155 case PPC_RELOC_HI16
:
1156 instruction
->immediateValue
= ((((instruction
->immediateValue
<< 16) | otherHalf
) + slide
) >> 16);
1158 case PPC_RELOC_HA16
:
1159 int16_t signedOtherHalf
= (int16_t)(otherHalf
& 0xffff);
1160 uint32_t temp
= (instruction
->immediateValue
<< 16) + signedOtherHalf
+ slide
;
1161 if ( (temp
& 0x00008000) != 0 )
1163 instruction
->immediateValue
= temp
>> 16;
1165 //uint32_t after = *((uint32_t*)locationToFix);
1166 //fprintf(stderr, "dyld: ppc fixup %0p type %d from 0x%08X to 0x%08X\n", locationToFix, relocationType, before, after);
1170 void ImageLoaderMachO::doRebase(const LinkContext
& context
)
1172 // if prebound and loaded at prebound address, then no need to rebase
1173 // Note: you might think that the check for allDependentLibrariesAsWhenPreBound() is not needed
1174 // but it is. If a dependent library changed, this image's lazy pointers into that library
1175 // need to be updated (reset back to lazy binding handler). That work is done most easily
1176 // here because there is a PPC_RELOC_PB_LA_PTR reloc record for each lazy pointer.
1177 if ( this->usablePrebinding(context
) && this->usesTwoLevelNameSpace() ) {
1178 // skip rebasing cause prebound and prebinding not disabled
1179 ++fgImagesWithUsedPrebinding
; // bump totals for statistics
1183 // print why prebinding was not used
1184 if ( context
.verbosePrebinding
) {
1185 if ( !this->isPrebindable() ) {
1186 fprintf(stderr
, "dyld: image not prebound, so could not use prebinding in %s\n", this->getPath());
1188 else if ( fSlide
!= 0 ) {
1189 fprintf(stderr
, "dyld: image slid, so could not use prebinding in %s\n", this->getPath());
1191 else if ( !this->allDependentLibrariesAsWhenPreBound() ) {
1192 fprintf(stderr
, "dyld: dependent libraries changed, so could not use prebinding in %s\n", this->getPath());
1194 else if ( !this->usesTwoLevelNameSpace() ){
1195 fprintf(stderr
, "dyld: image uses flat-namespace so, parts of prebinding ignored %s\n", this->getPath());
1198 fprintf(stderr
, "dyld: environment variable disabled use of prebinding in %s\n", this->getPath());
1202 // if there are __TEXT fixups, temporarily make __TEXT writable
1203 if ( fTextSegmentWithFixups
!= NULL
)
1204 fTextSegmentWithFixups
->tempWritable();
1206 // cache this value that is used in the following loop
1207 register const uintptr_t slide
= this->fSlide
;
1209 // loop through all local (internal) relocation records
1210 const uintptr_t relocBase
= this->getRelocBase();
1211 const relocation_info
* const relocsStart
= (struct relocation_info
*)(&fLinkEditBase
[fDynamicInfo
->locreloff
]);
1212 const relocation_info
* const relocsEnd
= &relocsStart
[fDynamicInfo
->nlocrel
];
1213 for (const relocation_info
* reloc
=relocsStart
; reloc
< relocsEnd
; ++reloc
) {
1214 if ( (reloc
->r_address
& R_SCATTERED
) == 0 ) {
1215 if ( reloc
->r_symbolnum
== R_ABS
) {
1216 // ignore absolute relocations
1218 else if (reloc
->r_length
== RELOC_SIZE
) {
1219 switch(reloc
->r_type
) {
1220 case GENERIC_RELOC_VANILLA
:
1221 *((uintptr_t*)(reloc
->r_address
+ relocBase
)) += slide
;
1224 case PPC_RELOC_HI16
:
1225 case PPC_RELOC_LO16
:
1226 case PPC_RELOC_HA16
:
1227 // some tools leave object file relocations in linked images
1228 otherRelocsPPC((uintptr_t*)(reloc
->r_address
+ relocBase
), reloc
->r_type
, reloc
[1].r_address
, slide
);
1229 ++reloc
; // these relocations come in pairs, skip next
1233 throw "unknown local relocation type";
1237 throw "bad local relocation length";
1241 const struct scattered_relocation_info
* sreloc
= (struct scattered_relocation_info
*)reloc
;
1242 if (sreloc
->r_length
== RELOC_SIZE
) {
1243 uintptr_t* locationToFix
= (uintptr_t*)(sreloc
->r_address
+ relocBase
);
1244 switch(sreloc
->r_type
) {
1245 case GENERIC_RELOC_VANILLA
:
1246 *locationToFix
+= slide
;
1248 #if __ppc__ || __ppc64__
1249 case PPC_RELOC_PB_LA_PTR
:
1250 // should only see these in prebound images, and we got here so prebinding is being ignored
1251 *locationToFix
= sreloc
->r_value
+ slide
;
1255 case PPC_RELOC_HI16
:
1256 case PPC_RELOC_LO16
:
1257 case PPC_RELOC_HA16
:
1258 // Metrowerks compiler sometimes leaves object file relocations in linked images???
1259 ++reloc
; // these relocations come in pairs, get next one
1260 otherRelocsPPC(locationToFix
, sreloc
->r_type
, reloc
->r_address
, slide
);
1264 case GENERIC_RELOC_PB_LA_PTR
:
1265 // should only see these in prebound images, and we got here so prebinding is being ignored
1266 *locationToFix
= sreloc
->r_value
+ slide
;
1270 throw "unknown local scattered relocation type";
1274 throw "bad local scattered relocation length";
1279 // if there were __TEXT fixups, restore write protection
1280 if ( fTextSegmentWithFixups
!= NULL
) {
1281 fTextSegmentWithFixups
->setPermissions();
1282 sys_icache_invalidate((void*)fTextSegmentWithFixups
->getActualLoadAddress(), fTextSegmentWithFixups
->getSize());
1286 fgTotalRebaseFixups
+= fDynamicInfo
->nlocrel
;
1290 const struct macho_nlist
* ImageLoaderMachO::binarySearchWithToc(const char* key
, const char stringPool
[], const struct macho_nlist symbols
[],
1291 const struct dylib_table_of_contents toc
[], uint32_t symbolCount
, uint32_t hintIndex
)
1293 int32_t high
= symbolCount
-1;
1294 int32_t mid
= hintIndex
;
1296 // handle out of range hint
1297 if ( mid
>= (int32_t)symbolCount
) {
1298 mid
= symbolCount
/2;
1299 ++ImageLoaderMachO::fgUnhintedBinaryTreeSearchs
;
1302 ++ImageLoaderMachO::fgHintedBinaryTreeSearchs
;
1305 for (int32_t low
= 0; low
<= high
; mid
= (low
+high
)/2) {
1306 const uint32_t index
= toc
[mid
].symbol_index
;
1307 const struct macho_nlist
* pivot
= &symbols
[index
];
1308 const char* pivotStr
= &stringPool
[pivot
->n_un
.n_strx
];
1309 #if LINKEDIT_USAGE_DEBUG
1310 noteAccessedLinkEditAddress(&toc
[mid
]);
1311 noteAccessedLinkEditAddress(pivot
);
1312 noteAccessedLinkEditAddress(pivotStr
);
1314 int cmp
= astrcmp(key
, pivotStr
);
1329 const struct macho_nlist
* ImageLoaderMachO::binarySearch(const char* key
, const char stringPool
[], const struct macho_nlist symbols
[], uint32_t symbolCount
)
1331 ++ImageLoaderMachO::fgUnhintedBinaryTreeSearchs
;
1332 const struct macho_nlist
* base
= symbols
;
1333 for (uint32_t n
= symbolCount
; n
> 0; n
/= 2) {
1334 const struct macho_nlist
* pivot
= &base
[n
/2];
1335 const char* pivotStr
= &stringPool
[pivot
->n_un
.n_strx
];
1336 #if LINKEDIT_USAGE_DEBUG
1337 noteAccessedLinkEditAddress(pivot
);
1338 noteAccessedLinkEditAddress(pivotStr
);
1340 int cmp
= astrcmp(key
, pivotStr
);
1345 // move base to symbol after pivot
1357 const ImageLoader::Symbol
* ImageLoaderMachO::findExportedSymbol(const char* name
, const void* hint
, bool searchReExports
, ImageLoader
** foundIn
) const
1359 const struct macho_nlist
* sym
= NULL
;
1360 const struct twolevel_hint
* theHint
= (struct twolevel_hint
*)hint
;
1361 if ( fDynamicInfo
->tocoff
== 0 )
1362 sym
= binarySearch(name
, fStrings
, &fSymbolTable
[fDynamicInfo
->iextdefsym
], fDynamicInfo
->nextdefsym
);
1364 uint32_t start
= fDynamicInfo
->nextdefsym
;
1365 if ( theHint
!= NULL
)
1366 start
= theHint
->itoc
;
1367 if ( (theHint
== NULL
) || (theHint
->isub_image
== 0) ) {
1368 sym
= binarySearchWithToc(name
, fStrings
, fSymbolTable
, (dylib_table_of_contents
*)&fLinkEditBase
[fDynamicInfo
->tocoff
],
1369 fDynamicInfo
->ntoc
, start
);
1372 if ( sym
!= NULL
) {
1373 if ( foundIn
!= NULL
)
1374 *foundIn
= (ImageLoader
*)this;
1376 return (const Symbol
*)sym
;
1379 if ( searchReExports
) {
1380 // hint might tell us to try a particular subimage
1381 if ( (theHint
!= NULL
) && (theHint
->isub_image
> 0) && (theHint
->isub_image
<= fLibrariesCount
) ) {
1382 // isub_image is an index into a list that is sorted non-rexported images first
1384 ImageLoader
* target
= NULL
;
1385 // pass one, only look at sub-frameworks
1386 for (uint32_t i
=0; i
< fLibrariesCount
; ++i
) {
1387 DependentLibrary
& libInfo
= fLibraries
[i
];
1388 if ( libInfo
.isSubFramework
&& (libInfo
.image
!= NULL
)) {
1389 if ( ++index
== theHint
->isub_image
) {
1390 target
= libInfo
.image
;
1395 if (target
!= NULL
) {
1396 // pass two, only look at non-sub-framework-reexports
1397 for (uint32_t i
=0; i
< fLibrariesCount
; ++i
) {
1398 DependentLibrary
& libInfo
= fLibraries
[i
];
1399 if ( libInfo
.isReExported
&& !libInfo
.isSubFramework
&& (libInfo
.image
!= NULL
) ) {
1400 if ( ++index
== theHint
->isub_image
) {
1401 target
= libInfo
.image
;
1407 if (target
!= NULL
) {
1408 const Symbol
* result
= target
->findExportedSymbol(name
, NULL
, searchReExports
, foundIn
);
1409 if ( result
!= NULL
)
1414 // hint failed, try all sub images
1415 // pass one, only look at sub-frameworks
1416 for(unsigned int i
=0; i
< fLibrariesCount
; ++i
){
1417 DependentLibrary
& libInfo
= fLibraries
[i
];
1418 if ( (libInfo
.image
!= NULL
) && libInfo
.isSubFramework
) {
1419 const Symbol
* result
= libInfo
.image
->findExportedSymbol(name
, NULL
, searchReExports
, foundIn
);
1420 if ( result
!= NULL
)
1424 // pass two, only look at non-sub-framework-reexports
1425 for(unsigned int i
=0; i
< fLibrariesCount
; ++i
){
1426 DependentLibrary
& libInfo
= fLibraries
[i
];
1427 if ( (libInfo
.image
!= NULL
) && libInfo
.isReExported
&& !libInfo
.isSubFramework
) {
1428 const Symbol
* result
= libInfo
.image
->findExportedSymbol(name
, NULL
, searchReExports
, foundIn
);
1429 if ( result
!= NULL
)
1435 // last change: the hint is wrong (non-zero but actually in this image)
1436 if ( (theHint
!= NULL
) && (theHint
->isub_image
!= 0) ) {
1437 sym
= binarySearchWithToc(name
, fStrings
, fSymbolTable
, (dylib_table_of_contents
*)&fLinkEditBase
[fDynamicInfo
->tocoff
],
1438 fDynamicInfo
->ntoc
, fDynamicInfo
->nextdefsym
);
1439 if ( sym
!= NULL
) {
1440 if ( foundIn
!= NULL
)
1441 *foundIn
= (ImageLoader
*)this;
1442 return (const Symbol
*)sym
;
1450 uintptr_t ImageLoaderMachO::getExportedSymbolAddress(const Symbol
* sym
) const
1452 const struct macho_nlist
* nlistSym
= (const struct macho_nlist
*)sym
;
1453 return nlistSym
->n_value
+ fSlide
;
1456 ImageLoader::DefinitionFlags
ImageLoaderMachO::getExportedSymbolInfo(const Symbol
* sym
) const
1458 const struct macho_nlist
* nlistSym
= (const struct macho_nlist
*)sym
;
1459 if ( (nlistSym
->n_desc
& N_WEAK_DEF
) != 0 )
1460 return kWeakDefinition
;
1461 return kNoDefinitionOptions
;
1464 const char* ImageLoaderMachO::getExportedSymbolName(const Symbol
* sym
) const
1466 const struct macho_nlist
* nlistSym
= (const struct macho_nlist
*)sym
;
1467 return &fStrings
[nlistSym
->n_un
.n_strx
];
1470 uint32_t ImageLoaderMachO::getExportedSymbolCount() const
1472 return fDynamicInfo
->nextdefsym
;
1476 const ImageLoader::Symbol
* ImageLoaderMachO::getIndexedExportedSymbol(uint32_t index
) const
1478 if ( index
< fDynamicInfo
->nextdefsym
) {
1479 const struct macho_nlist
* sym
= &fSymbolTable
[fDynamicInfo
->iextdefsym
+ index
];
1480 return (const ImageLoader::Symbol
*)sym
;
1486 uint32_t ImageLoaderMachO::getImportedSymbolCount() const
1488 return fDynamicInfo
->nundefsym
;
1492 const ImageLoader::Symbol
* ImageLoaderMachO::getIndexedImportedSymbol(uint32_t index
) const
1494 if ( index
< fDynamicInfo
->nundefsym
) {
1495 const struct macho_nlist
* sym
= &fSymbolTable
[fDynamicInfo
->iundefsym
+ index
];
1496 return (const ImageLoader::Symbol
*)sym
;
1502 ImageLoader::ReferenceFlags
ImageLoaderMachO::geImportedSymbolInfo(const ImageLoader::Symbol
* sym
) const
1504 const struct macho_nlist
* nlistSym
= (const struct macho_nlist
*)sym
;
1505 ImageLoader::ReferenceFlags flags
= kNoReferenceOptions
;
1506 if ( ((nlistSym
->n_type
& N_TYPE
) == N_UNDF
) && (nlistSym
->n_value
!= 0) )
1507 flags
|= ImageLoader::kTentativeDefinition
;
1508 if ( (nlistSym
->n_desc
& N_WEAK_REF
) != 0 )
1509 flags
|= ImageLoader::kWeakReference
;
1514 const char* ImageLoaderMachO::getImportedSymbolName(const ImageLoader::Symbol
* sym
) const
1516 const struct macho_nlist
* nlistSym
= (const struct macho_nlist
*)sym
;
1517 return &fStrings
[nlistSym
->n_un
.n_strx
];
1521 bool ImageLoaderMachO::getSectionContent(const char* segmentName
, const char* sectionName
, void** start
, size_t* length
)
1523 const uint32_t cmd_count
= ((macho_header
*)fMachOData
)->ncmds
;
1524 const struct load_command
* const cmds
= (struct load_command
*)&fMachOData
[sizeof(macho_header
)];
1525 const struct load_command
* cmd
= cmds
;
1526 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
1528 case LC_SEGMENT_COMMAND
:
1530 const struct macho_segment_command
* seg
= (struct macho_segment_command
*)cmd
;
1531 const struct macho_section
* const sectionsStart
= (struct macho_section
*)((char*)seg
+ sizeof(struct macho_segment_command
));
1532 const struct macho_section
* const sectionsEnd
= §ionsStart
[seg
->nsects
];
1533 for (const struct macho_section
* sect
=sectionsStart
; sect
< sectionsEnd
; ++sect
) {
1534 if ( (strcmp(sect
->segname
, segmentName
) == 0) && (strcmp(sect
->sectname
, sectionName
) == 0) ) {
1535 *start
= (uintptr_t*)(sect
->addr
+ fSlide
);
1536 *length
= sect
->size
;
1543 cmd
= (const struct load_command
*)(((char*)cmd
)+cmd
->cmdsize
);
1549 bool ImageLoaderMachO::findSection(const void* imageInterior
, const char** segmentName
, const char** sectionName
, size_t* sectionOffset
)
1551 const uint32_t cmd_count
= ((macho_header
*)fMachOData
)->ncmds
;
1552 const struct load_command
* const cmds
= (struct load_command
*)&fMachOData
[sizeof(macho_header
)];
1553 const struct load_command
* cmd
= cmds
;
1554 const uintptr_t unslidInteriorAddress
= (uintptr_t)imageInterior
- this->getSlide();
1555 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
1557 case LC_SEGMENT_COMMAND
:
1559 const struct macho_segment_command
* seg
= (struct macho_segment_command
*)cmd
;
1560 if ( (unslidInteriorAddress
>= seg
->vmaddr
) && (unslidInteriorAddress
< (seg
->vmaddr
+seg
->vmsize
)) ) {
1561 const struct macho_section
* const sectionsStart
= (struct macho_section
*)((char*)seg
+ sizeof(struct macho_segment_command
));
1562 const struct macho_section
* const sectionsEnd
= §ionsStart
[seg
->nsects
];
1563 for (const struct macho_section
* sect
=sectionsStart
; sect
< sectionsEnd
; ++sect
) {
1564 if ((sect
->addr
<= unslidInteriorAddress
) && (unslidInteriorAddress
< (sect
->addr
+sect
->size
))) {
1565 if ( segmentName
!= NULL
)
1566 *segmentName
= sect
->segname
;
1567 if ( sectionName
!= NULL
)
1568 *sectionName
= sect
->sectname
;
1569 if ( sectionOffset
!= NULL
)
1570 *sectionOffset
= unslidInteriorAddress
- sect
->addr
;
1578 cmd
= (const struct load_command
*)(((char*)cmd
)+cmd
->cmdsize
);
1584 bool ImageLoaderMachO::symbolRequiresCoalescing(const struct macho_nlist
* symbol
)
1586 // if a define and weak ==> coalesced
1587 if ( ((symbol
->n_type
& N_TYPE
) == N_SECT
) && ((symbol
->n_desc
& N_WEAK_DEF
) != 0) )
1589 // if an undefine and not referencing a weak symbol ==> coalesced
1590 if ( ((symbol
->n_type
& N_TYPE
) != N_SECT
) && ((symbol
->n_desc
& N_REF_TO_WEAK
) != 0) )
1598 static void __attribute__((noreturn
)) throwSymbolNotFound(const char* symbol
, const char* referencedFrom
, const char* expectedIn
)
1600 const char* formatString
= "Symbol not found: %s\n Referenced from: %s\n Expected in: %s\n";
1601 char buf
[strlen(symbol
)+strlen(referencedFrom
)+strlen(expectedIn
)+strlen(formatString
)];
1602 sprintf(buf
, formatString
, symbol
, referencedFrom
, expectedIn
);
1603 throw strdup(buf
); // this is a leak if exception doesn't halt program
1606 uintptr_t ImageLoaderMachO::resolveUndefined(const LinkContext
& context
, const struct macho_nlist
* undefinedSymbol
, bool twoLevel
, ImageLoader
** foundIn
)
1608 const char* symbolName
= &fStrings
[undefinedSymbol
->n_un
.n_strx
];
1610 if ( context
.bindFlat
|| !twoLevel
) {
1613 if ( context
.flatExportFinder(symbolName
, &sym
, foundIn
) )
1614 return (*foundIn
)->getExportedSymbolAddress(sym
);
1615 // if a bundle is loaded privately the above will not find its exports
1616 if ( this->isBundle() && this->hasHiddenExports() ) {
1617 // look in self for needed symbol
1618 sym
= this->findExportedSymbol(symbolName
, NULL
, false, foundIn
);
1620 return (*foundIn
)->getExportedSymbolAddress(sym
);
1622 if ( ((undefinedSymbol
->n_type
& N_PEXT
) != 0) || ((undefinedSymbol
->n_type
& N_TYPE
) == N_SECT
) ) {
1623 // could be a multi-module private_extern internal reference
1624 // the static linker squirrels away the target address in n_value
1625 uintptr_t addr
= undefinedSymbol
->n_value
+ this->fSlide
;
1629 if ( (undefinedSymbol
->n_desc
& N_WEAK_REF
) != 0 ) {
1630 // definition can't be found anywhere
1631 // if reference is weak_import, then it is ok, just return 0
1634 throwSymbolNotFound(symbolName
, this->getPath(), "flat namespace");
1637 // symbol requires searching images with coalesced symbols
1638 if ( this->needsCoalescing() && symbolRequiresCoalescing(undefinedSymbol
) ) {
1640 if ( context
.coalescedExportFinder(symbolName
, &sym
, foundIn
) )
1641 return (*foundIn
)->getExportedSymbolAddress(sym
);
1642 //throwSymbolNotFound(symbolName, this->getPath(), "coalesced namespace");
1643 //fprintf(stderr, "dyld: coalesced symbol %s not found in any coalesced image, falling back to two-level lookup", symbolName);
1648 ImageLoader
* target
= NULL
;
1649 uint8_t ord
= GET_LIBRARY_ORDINAL(undefinedSymbol
->n_desc
);
1650 if ( ord
== EXECUTABLE_ORDINAL
) {
1651 target
= context
.mainExecutable
;
1653 else if ( ord
== SELF_LIBRARY_ORDINAL
) {
1656 else if ( ord
== DYNAMIC_LOOKUP_ORDINAL
) {
1657 // rnielsen: HACKHACK
1660 if ( context
.flatExportFinder(symbolName
, &sym
, foundIn
) )
1661 return (*foundIn
)->getExportedSymbolAddress(sym
);
1662 // no image has exports this symbol
1663 // either report error or hope ZeroLink can just-in-time load an image
1664 context
.undefinedHandler(symbolName
);
1665 // try looking again
1666 if ( context
.flatExportFinder(symbolName
, &sym
, foundIn
) )
1667 return (*foundIn
)->getExportedSymbolAddress(sym
);
1669 throwSymbolNotFound(symbolName
, this->getPath(), "dynamic lookup");
1671 else if ( ord
<= fLibrariesCount
) {
1672 DependentLibrary
& libInfo
= fLibraries
[ord
-1];
1673 target
= libInfo
.image
;
1674 if ( (target
== NULL
) && (((undefinedSymbol
->n_desc
& N_WEAK_REF
) != 0) || !libInfo
.required
) ) {
1675 // if target library not loaded and reference is weak or library is weak return 0
1680 throw "corrupt binary, library ordinal too big";
1683 if ( target
== NULL
) {
1684 fprintf(stderr
, "resolveUndefined(%s) in %s\n", symbolName
, this->getPath());
1685 throw "symbol not found";
1689 if ( fTwoLevelHints
!= NULL
) {
1690 uint32_t symIndex
= undefinedSymbol
- fSymbolTable
;
1691 int32_t undefinedIndex
= symIndex
- fDynamicInfo
->iundefsym
;
1692 if ( (undefinedIndex
>= 0) && ((uint32_t)undefinedIndex
< fDynamicInfo
->nundefsym
) ) {
1693 const struct twolevel_hint
* hints
= (struct twolevel_hint
*)(&fLinkEditBase
[fTwoLevelHints
->offset
]);
1694 const struct twolevel_hint
* theHint
= &hints
[undefinedIndex
];
1695 hint
= (void*)theHint
;
1699 const Symbol
* sym
= target
->findExportedSymbol(symbolName
, hint
, true, foundIn
);
1701 return (*foundIn
)->getExportedSymbolAddress(sym
);
1703 else if ( (undefinedSymbol
->n_type
& N_PEXT
) != 0 ) {
1704 // don't know why the static linker did not eliminate the internal reference to a private extern definition
1706 return undefinedSymbol
->n_value
+ fSlide
;
1708 else if ( (undefinedSymbol
->n_desc
& N_WEAK_REF
) != 0 ) {
1709 // if definition not found and reference is weak return 0
1713 // nowhere to be found
1714 throwSymbolNotFound(symbolName
, this->getPath(), target
->getPath());
1718 void ImageLoaderMachO::doBindExternalRelocations(const LinkContext
& context
, bool onlyCoalescedSymbols
)
1720 const uintptr_t relocBase
= this->getRelocBase();
1721 const bool twoLevel
= this->usesTwoLevelNameSpace();
1722 const bool prebound
= this->isPrebindable();
1724 // if there are __TEXT fixups, temporarily make __TEXT writable
1725 if ( fTextSegmentWithFixups
!= NULL
)
1726 fTextSegmentWithFixups
->tempWritable();
1728 // cache last lookup
1729 const struct macho_nlist
* lastUndefinedSymbol
= 0;
1730 uintptr_t symbolAddr
= 0;
1731 ImageLoader
* image
= NULL
;
1733 // loop through all external relocation records and bind each
1734 const relocation_info
* const relocsStart
= (struct relocation_info
*)(&fLinkEditBase
[fDynamicInfo
->extreloff
]);
1735 const relocation_info
* const relocsEnd
= &relocsStart
[fDynamicInfo
->nextrel
];
1736 for (const relocation_info
* reloc
=relocsStart
; reloc
< relocsEnd
; ++reloc
) {
1737 if (reloc
->r_length
== RELOC_SIZE
) {
1738 switch(reloc
->r_type
) {
1739 case GENERIC_RELOC_VANILLA
:
1741 const struct macho_nlist
* undefinedSymbol
= &fSymbolTable
[reloc
->r_symbolnum
];
1742 // if only processing coalesced symbols and this one does not require coalesceing, skip to next
1743 if ( onlyCoalescedSymbols
&& !symbolRequiresCoalescing(undefinedSymbol
) )
1745 uintptr_t* location
= ((uintptr_t*)(reloc
->r_address
+ relocBase
));
1746 uintptr_t value
= *location
;
1748 // we are doing relocations, so prebinding was not usable
1749 // in a prebound executable, the n_value field is set to the address where the symbol was found when prebound
1750 // so, subtracting that gives the initial displacement which we need to add to the newly found symbol address
1751 // if mach-o relocation structs had an "addend" field this would not be necessary.
1752 value
-= undefinedSymbol
->n_value
;
1754 // if undefinedSymbol is same as last time, then symbolAddr and image will resolve to the same too
1755 if ( undefinedSymbol
!= lastUndefinedSymbol
) {
1756 symbolAddr
= this->resolveUndefined(context
, undefinedSymbol
, twoLevel
, &image
);
1757 lastUndefinedSymbol
= undefinedSymbol
;
1759 if ( context
.verboseBind
) {
1760 const char *path
= NULL
;
1762 path
= image
->getShortName();
1765 fprintf(stderr
, "dyld: bind: %s:0x%08lx = %s:%s, *0x%08lx = 0x%08lx\n",
1766 this->getShortName(), (uintptr_t)location
,
1767 path
, &fStrings
[undefinedSymbol
->n_un
.n_strx
], (uintptr_t)location
, symbolAddr
);
1770 fprintf(stderr
, "dyld: bind: %s:0x%08lx = %s:%s, *0x%08lx = 0x%08lx + %ld\n",
1771 this->getShortName(), (uintptr_t)location
,
1772 path
, &fStrings
[undefinedSymbol
->n_un
.n_strx
], (uintptr_t)location
, symbolAddr
, value
);
1775 value
+= symbolAddr
;
1780 throw "unknown external relocation type";
1784 throw "bad external relocation length";
1788 // if there were __TEXT fixups, restore write protection
1789 if ( fTextSegmentWithFixups
!= NULL
) {
1790 fTextSegmentWithFixups
->setPermissions();
1791 sys_icache_invalidate((void*)fTextSegmentWithFixups
->getActualLoadAddress(), fTextSegmentWithFixups
->getSize());
1795 fgTotalBindFixups
+= fDynamicInfo
->nextrel
;
1798 const mach_header
* ImageLoaderMachO::machHeader() const
1800 return (mach_header
*)fMachOData
;
1803 uintptr_t ImageLoaderMachO::getSlide() const
1808 // hmm. maybe this should be up in ImageLoader??
1809 const void* ImageLoaderMachO::getBaseAddress() const
1811 Segment
* seg
= fSegments
[0];
1812 return (const void*)seg
->getActualLoadAddress();
1816 uintptr_t ImageLoaderMachO::doBindLazySymbol(uintptr_t* lazyPointer
, const LinkContext
& context
)
1818 // scan for all non-lazy-pointer sections
1819 const bool twoLevel
= this->usesTwoLevelNameSpace();
1820 const uint32_t cmd_count
= ((macho_header
*)fMachOData
)->ncmds
;
1821 const struct load_command
* const cmds
= (struct load_command
*)&fMachOData
[sizeof(macho_header
)];
1822 const struct load_command
* cmd
= cmds
;
1823 const uint32_t* const indirectTable
= (uint32_t*)&fLinkEditBase
[fDynamicInfo
->indirectsymoff
];
1824 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
1826 case LC_SEGMENT_COMMAND
:
1828 const struct macho_segment_command
* seg
= (struct macho_segment_command
*)cmd
;
1829 const struct macho_section
* const sectionsStart
= (struct macho_section
*)((char*)seg
+ sizeof(struct macho_segment_command
));
1830 const struct macho_section
* const sectionsEnd
= §ionsStart
[seg
->nsects
];
1831 for (const struct macho_section
* sect
=sectionsStart
; sect
< sectionsEnd
; ++sect
) {
1832 const uint8_t type
= sect
->flags
& SECTION_TYPE
;
1833 if ( type
== S_LAZY_SYMBOL_POINTERS
) {
1834 const uint32_t pointerCount
= sect
->size
/ sizeof(uintptr_t);
1835 uintptr_t* const symbolPointers
= (uintptr_t*)(sect
->addr
+ fSlide
);
1836 if ( (lazyPointer
>= symbolPointers
) && (lazyPointer
< &symbolPointers
[pointerCount
]) ) {
1837 const uint32_t indirectTableOffset
= sect
->reserved1
;
1838 const uint32_t lazyIndex
= lazyPointer
- symbolPointers
;
1839 uint32_t symbolIndex
= indirectTable
[indirectTableOffset
+ lazyIndex
];
1840 if ( symbolIndex
!= INDIRECT_SYMBOL_ABS
&& symbolIndex
!= INDIRECT_SYMBOL_LOCAL
) {
1841 ImageLoader
*image
= NULL
;
1842 const char *path
= NULL
;
1843 uintptr_t symbolAddr
= this->resolveUndefined(context
, &fSymbolTable
[symbolIndex
], twoLevel
, &image
);
1844 if ( context
.verboseBind
) {
1845 if(NULL
== path
&& NULL
!= image
) {
1846 path
= image
->getShortName();
1848 fprintf(stderr
, "dyld: bind: %s:%s$%s = %s:%s, *0x%08lx = 0x%08lx\n",
1849 this->getShortName(), &fStrings
[fSymbolTable
[symbolIndex
].n_un
.n_strx
], "lazy_ptr",
1850 path
, &fStrings
[fSymbolTable
[symbolIndex
].n_un
.n_strx
], (uintptr_t)&symbolPointers
[lazyIndex
], symbolAddr
);
1852 if ( NULL
!= context
.bindingHandler
) {
1853 if(NULL
== path
&& NULL
!= image
) {
1854 path
= image
->getPath();
1856 symbolAddr
= (uintptr_t)context
.bindingHandler(path
, &fStrings
[fSymbolTable
[symbolIndex
].n_un
.n_strx
], (void *)symbolAddr
);
1858 symbolPointers
[lazyIndex
] = symbolAddr
;
1860 fgTotalLazyBindFixups
++;
1861 return symbolPointers
[lazyIndex
];
1869 cmd
= (const struct load_command
*)(((char*)cmd
)+cmd
->cmdsize
);
1871 throw "lazy pointer not found";
1876 void ImageLoaderMachO::doBindIndirectSymbolPointers(const LinkContext
& context
, BindingLaziness bindness
, bool onlyCoalescedSymbols
)
1878 // scan for all non-lazy-pointer sections
1879 const bool twoLevel
= this->usesTwoLevelNameSpace();
1880 const uint32_t cmd_count
= ((macho_header
*)fMachOData
)->ncmds
;
1881 const struct load_command
* const cmds
= (struct load_command
*)&fMachOData
[sizeof(macho_header
)];
1882 const struct load_command
* cmd
= cmds
;
1883 const uint32_t* const indirectTable
= (uint32_t*)&fLinkEditBase
[fDynamicInfo
->indirectsymoff
];
1884 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
1886 case LC_SEGMENT_COMMAND
:
1888 const struct macho_segment_command
* seg
= (struct macho_segment_command
*)cmd
;
1889 const struct macho_section
* const sectionsStart
= (struct macho_section
*)((char*)seg
+ sizeof(struct macho_segment_command
));
1890 const struct macho_section
* const sectionsEnd
= §ionsStart
[seg
->nsects
];
1891 for (const struct macho_section
* sect
=sectionsStart
; sect
< sectionsEnd
; ++sect
) {
1892 const uint8_t type
= sect
->flags
& SECTION_TYPE
;
1893 const uint32_t pointerCount
= sect
->size
/ sizeof(uintptr_t);
1894 if ( type
== S_NON_LAZY_SYMBOL_POINTERS
) {
1895 if ( (bindness
== kLazyOnly
) || (bindness
== kLazyOnlyNoDependents
) )
1898 else if ( type
== S_LAZY_SYMBOL_POINTERS
) {
1899 // process each symbol pointer in this section
1900 fgTotalPossibleLazyBindFixups
+= pointerCount
;
1901 if ( bindness
== kNonLazyOnly
)
1907 const uint32_t indirectTableOffset
= sect
->reserved1
;
1908 uintptr_t* const symbolPointers
= (uintptr_t*)(sect
->addr
+ fSlide
);
1909 for (uint32_t j
=0; j
< pointerCount
; ++j
) {
1910 uint32_t symbolIndex
= indirectTable
[indirectTableOffset
+ j
];
1911 if ( symbolIndex
== INDIRECT_SYMBOL_LOCAL
) {
1912 symbolPointers
[j
] += this->fSlide
;
1914 else if ( symbolIndex
== INDIRECT_SYMBOL_ABS
) {
1915 // do nothing since already has absolute address
1918 const struct macho_nlist
* sym
= &fSymbolTable
[symbolIndex
];
1919 if ( symbolIndex
== 0 ) {
1920 // This could be rdar://problem/3534709
1921 if ( ((const macho_header
*)fMachOData
)->filetype
== MH_EXECUTE
) {
1922 static bool alreadyWarned
= false;
1923 if ( (sym
->n_type
& N_TYPE
) != N_UNDF
) {
1924 // The indirect table parallels the (non)lazy pointer sections. For
1925 // instance, to find info about the fifth lazy pointer you look at the
1926 // fifth entry in the indirect table. (try otool -Iv on a file).
1927 // The entry in the indirect table contains an index into the symbol table.
1929 // The bug in ld caused the entry in the indirect table to be zero
1930 // (instead of a magic value that means a local symbol). So, if the
1931 // symbolIndex == 0, we may be encountering the bug, or 0 may be a valid
1932 // symbol table index. The check I put in place is to see if the zero'th
1933 // symbol table entry is an import entry (usually it is a local symbol
1935 if ( context
.verboseWarnings
&& !alreadyWarned
) {
1936 fprintf(stderr
, "dyld: malformed executable '%s', skipping indirect symbol to %s\n",
1937 this->getPath(), &fStrings
[sym
->n_un
.n_strx
]);
1938 alreadyWarned
= true;
1944 ImageLoader
*image
= NULL
;
1945 // if only processing coalesced symbols and this one does not require coalesceing, skip to next
1946 if ( onlyCoalescedSymbols
&& !symbolRequiresCoalescing(sym
) )
1948 uintptr_t symbolAddr
;
1949 symbolAddr
= resolveUndefined(context
, sym
, twoLevel
, &image
);
1950 if ( context
.verboseBind
) {
1951 const char *path
= NULL
;
1953 path
= image
->getShortName();
1955 const char *typeName
;
1956 if ( type
== S_LAZY_SYMBOL_POINTERS
) {
1957 typeName
= "lazy_ptr";
1960 typeName
= "non_lazy_ptr";
1962 fprintf(stderr
, "dyld: bind: %s:%s$%s = %s:%s, *0x%08lx = 0x%08lx\n",
1963 this->getShortName(), &fStrings
[sym
->n_un
.n_strx
], typeName
,
1964 path
, &fStrings
[sym
->n_un
.n_strx
], (uintptr_t)&symbolPointers
[j
], symbolAddr
);
1966 symbolPointers
[j
] = symbolAddr
;
1970 fgTotalBindFixups
+= pointerCount
;
1975 cmd
= (const struct load_command
*)(((char*)cmd
)+cmd
->cmdsize
);
1980 * The address of these symbols are written in to the (__DATA,__dyld) section
1981 * at the following offsets:
1982 * at offset 0 stub_binding_helper_interface
1983 * at offset 4 _dyld_func_lookup
1984 * at offset 8 start_debug_thread
1985 * The 'C' types (if any) for these symbols are ignored here and all are
1986 * declared as longs so the assignment of their address in to the section will
1987 * not require a cast. stub_binding_helper_interface is really a label in the
1988 * assembly code interface for the stub binding. It does not have a meaningful
1989 * 'C' type. _dyld_func_lookup is the routine in dyld_libfuncs.c.
1990 * start_debug_thread is the routine in debug.c.
1992 * For ppc the image's stub_binding_binding_helper is read from:
1993 * at offset 20 the image's stub_binding_binding_helper address
1994 * and saved into to the image structure.
1997 void* dyldLazyBinder
; // filled in at launch by dyld to point into dyld to &stub_binding_helper_interface
1998 void* dyldFuncLookup
; // filled in at launch by dyld to point into dyld to &_dyld_func_lookup
1999 void* startDebugThread
; // debugger interface ???
2000 void* debugPort
; // debugger interface ???
2001 void* debugThread
; // debugger interface ???
2002 void* stubBindHelper
; // filled in at static link time to point to stub helper in image
2003 void* coreDebug
; // ???
2006 // These are defined in dyldStartup.s
2007 extern "C" void stub_binding_helper();
2008 extern "C" bool dyld_func_lookup(const char* name
, uintptr_t* address
);
2011 void ImageLoaderMachO::setupLazyPointerHandler()
2013 if ( fDATAdyld
!= NULL
) {
2014 struct DATAdyld
* dd
= (struct DATAdyld
*)(fDATAdyld
->addr
+ fSlide
);
2015 if ( fDATAdyld
->size
> offsetof(DATAdyld
, dyldLazyBinder
) ) {
2016 if ( dd
->dyldLazyBinder
!= (void*)&stub_binding_helper
)
2017 dd
->dyldLazyBinder
= (void*)&stub_binding_helper
;
2019 if ( fDATAdyld
->size
> offsetof(DATAdyld
, dyldFuncLookup
) ) {
2020 if ( dd
->dyldFuncLookup
!= (void*)&dyld_func_lookup
)
2021 dd
->dyldFuncLookup
= (void*)&dyld_func_lookup
;
2023 //if ( fDATAdyld->size > offsetof(DATAdyld, startDebugThread) )
2024 // dd->startDebugThread = &start_debug_thread;
2026 //if ( fDATAdyld->size > offsetof(DATAdyld, stubBindHelper) )
2027 // save = dd->stubBindHelper;
2032 bool ImageLoaderMachO::usablePrebinding(const LinkContext
& context
) const
2034 // if prebound and loaded at prebound address, and all libraries are same as when this was prebound, then no need to bind
2035 if ( this->isPrebindable() && this->allDependentLibrariesAsWhenPreBound() && (this->getSlide() == 0) ) {
2036 // allow environment variables to disable prebinding
2037 if ( context
.bindFlat
)
2039 switch ( context
.prebindUsage
) {
2040 case kUseAllPrebinding
:
2042 case kUseSplitSegPrebinding
:
2043 return this->fIsSplitSeg
;
2044 case kUseAllButAppPredbinding
:
2045 return (this != context
.mainExecutable
);
2046 case kUseNoPrebinding
:
2053 void ImageLoaderMachO::doBind(const LinkContext
& context
, BindingLaziness bindness
)
2055 // set dyld entry points in image
2056 this->setupLazyPointerHandler();
2058 // if prebound and loaded at prebound address, and all libraries are same as when this was prebound, then no need to bind
2059 // note: flat-namespace binaries need to be imports rebound (even if correctly prebound)
2060 if ( this->usablePrebinding(context
) && this->usesTwoLevelNameSpace() ) {
2061 // if image has coalesced symbols, then these need to be rebound
2062 if ( this->needsCoalescing() ) {
2063 this->doBindExternalRelocations(context
, true);
2064 this->doBindIndirectSymbolPointers(context
, kLazyAndNonLazy
, true);
2066 // skip binding because prebound and prebinding not disabled
2070 // values bound by name are stored two different ways in mach-o
2073 case kLazyAndNonLazy
:
2074 // external relocations are used for data initialized to external symbols
2075 this->doBindExternalRelocations(context
, false);
2078 case kLazyOnlyNoDependents
:
2081 // "indirect symbols" are used for code references to external symbols
2082 this->doBindIndirectSymbolPointers(context
, bindness
, false);
2087 void ImageLoaderMachO::doImageInit(const LinkContext
& context
)
2089 if ( fDashInit
!= NULL
) {
2090 Initializer func
= (Initializer
)(fDashInit
->init_address
+ fSlide
);
2091 if ( context
.verboseInit
)
2092 fprintf(stderr
, "dyld: calling -init function 0x%p in %s\n", func
, this->getPath());
2093 func(context
.argc
, context
.argv
, context
.envp
, context
.apple
);
2097 void ImageLoaderMachO::doModInitFunctions(const LinkContext
& context
)
2099 if ( fModInitSection
!= NULL
) {
2100 Initializer
* inits
= (Initializer
*)(fModInitSection
->addr
+ fSlide
);
2101 const uint32_t count
= fModInitSection
->size
/ sizeof(uintptr_t);
2102 for (uint32_t i
=0; i
< count
; ++i
) {
2103 Initializer func
= inits
[i
];
2104 if ( context
.verboseInit
)
2105 fprintf(stderr
, "dyld: calling initializer function %p in %s\n", func
, this->getPath());
2106 func(context
.argc
, context
.argv
, context
.envp
, context
.apple
);
2112 void ImageLoaderMachO::doInitialization(const LinkContext
& context
)
2114 // mach-o has -init and static initializers
2115 doImageInit(context
);
2116 doModInitFunctions(context
);
2119 bool ImageLoaderMachO::needsInitialization()
2121 return ( (fDashInit
!= NULL
) || (fModInitSection
!= NULL
) );
2125 bool ImageLoaderMachO::needsTermination()
2127 return ( fModTermSection
!= NULL
);
2130 bool ImageLoaderMachO::hasImageNotification()
2132 return ( fImageNotifySection
!= NULL
);
2136 void ImageLoaderMachO::doTermination(const LinkContext
& context
)
2138 if ( fModTermSection
!= NULL
) {
2139 Terminator
* terms
= (Terminator
*)(fModTermSection
->addr
+ fSlide
);
2140 const uint32_t count
= fModTermSection
->size
/ sizeof(uintptr_t);
2141 for (uint32_t i
=count
; i
> 0; --i
) {
2142 Terminator func
= terms
[i
-1];
2143 if ( context
.verboseInit
)
2144 fprintf(stderr
, "dyld: calling terminaton function %p in %s\n", func
, this->getPath());
2150 void ImageLoaderMachO::doNotification(enum dyld_image_mode mode
, uint32_t infoCount
, const struct dyld_image_info info
[])
2152 if ( fImageNotifySection
!= NULL
) {
2153 dyld_image_notifier
* notes
= (dyld_image_notifier
*)(fImageNotifySection
->addr
+ fSlide
);
2154 const uint32_t count
= fImageNotifySection
->size
/ sizeof(uintptr_t);
2155 for (uint32_t i
=count
; i
> 0; --i
) {
2156 dyld_image_notifier func
= notes
[i
-1];
2157 func(mode
, infoCount
, info
);
2162 void ImageLoaderMachO::printStatistics(unsigned int imageCount
)
2164 ImageLoader::printStatistics(imageCount
);
2165 fprintf(stderr
, "total hinted binary tree searches: %d\n", fgHintedBinaryTreeSearchs
);
2166 fprintf(stderr
, "total unhinted binary tree searches: %d\n", fgUnhintedBinaryTreeSearchs
);
2168 #if LINKEDIT_USAGE_DEBUG
2169 fprintf(stderr
, "linkedit pages accessed (%lu):\n", sLinkEditPageBuckets
.size());
2173 void ImageLoaderMachO::doPrebinding(const LinkContext
& context
, time_t timestamp
, uint8_t* fileToPrebind
)
2175 // update __DATA segment
2176 this->applyPrebindingToDATA(fileToPrebind
);
2178 // update load commands
2179 this->applyPrebindingToLoadCommands(context
, fileToPrebind
, timestamp
);
2181 // update symbol table
2182 this->applyPrebindingToLinkEdit(context
, fileToPrebind
);
2185 void ImageLoaderMachO::applyPrebindingToDATA(uint8_t* fileToPrebind
)
2187 const unsigned int segmentCount
= fSegments
.size();
2188 for(unsigned int i
=0; i
< segmentCount
; ++i
) {
2189 SegmentMachO
* seg
= (SegmentMachO
*)fSegments
[i
];
2190 if ( seg
->writeable() ) {
2191 memcpy(&fileToPrebind
[seg
->fFileOffset
], (void*)seg
->getActualLoadAddress(), seg
->fFileSize
);
2196 void ImageLoaderMachO::applyPrebindingToLoadCommands(const LinkContext
& context
, uint8_t* fileToPrebind
, time_t timestamp
)
2198 macho_header
* mh
= (macho_header
*)fileToPrebind
;
2199 const uint32_t cmd_count
= mh
->ncmds
;
2200 const struct load_command
* const cmds
= (struct load_command
*)&fileToPrebind
[sizeof(macho_header
)];
2201 const struct load_command
* cmd
= cmds
;
2202 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
2205 case LC_LOAD_WEAK_DYLIB
:
2207 // update each dylib load command with the timestamp of the target dylib
2208 struct dylib_command
* dylib
= (struct dylib_command
*)cmd
;
2209 const char* name
= (char*)cmd
+ dylib
->dylib
.name
.offset
;
2210 for (const DependentLibrary
* dl
=fLibraries
; dl
< &fLibraries
[fLibrariesCount
]; dl
++) {
2211 if (strcmp(dl
->name
, name
) == 0 ) {
2212 // found matching DependentLibrary for this load command
2213 ImageLoaderMachO
* targetImage
= (ImageLoaderMachO
*)(dl
->image
); // !!! assume only mach-o images are prebound
2214 if ( ! targetImage
->isPrebindable() )
2215 throw "dependent dylib is not prebound";
2216 // if the target is currently being re-prebound then its timestamp will be the same as this one
2217 if ( ! targetImage
->usablePrebinding(context
) ) {
2218 dylib
->dylib
.timestamp
= timestamp
;
2221 // otherwise dependent library is already correctly prebound, so use its checksum
2222 dylib
->dylib
.timestamp
= targetImage
->doGetLibraryInfo().checksum
;
2231 // update the ID of this library with the new timestamp
2232 struct dylib_command
* dylib
= (struct dylib_command
*)cmd
;
2233 dylib
->dylib
.timestamp
= timestamp
;
2236 case LC_SEGMENT_COMMAND
:
2237 // if dylib was rebased, update segment commands
2238 if ( fSlide
!= 0 ) {
2239 struct macho_segment_command
* seg
= (struct macho_segment_command
*)cmd
;
2240 seg
->vmaddr
+= fSlide
;
2241 struct macho_section
* const sectionsStart
= (struct macho_section
*)((char*)seg
+ sizeof(struct macho_segment_command
));
2242 struct macho_section
* const sectionsEnd
= §ionsStart
[seg
->nsects
];
2243 for (struct macho_section
* sect
=sectionsStart
; sect
< sectionsEnd
; ++sect
) {
2244 sect
->addr
+= fSlide
;
2248 case LC_ROUTINES_COMMAND
:
2249 // if dylib was rebased, update -init command
2250 if ( fSlide
!= 0 ) {
2251 struct macho_routines_command
* routines
= (struct macho_routines_command
*)cmd
;
2252 routines
->init_address
+= fSlide
;
2256 cmd
= (const struct load_command
*)(((char*)cmd
)+cmd
->cmdsize
);
2260 void ImageLoaderMachO::applyPrebindingToLinkEdit(const LinkContext
& context
, uint8_t* fileToPrebind
)
2262 // In prebound images, the n_value of the symbol table entry for is the prebound address
2263 // This is needed when prebinding can't be used, to back solve for any possible addend in non-lazy pointers
2264 const char* stringPool
= NULL
;
2265 struct macho_nlist
* symbolTable
= NULL
;
2266 const struct dysymtab_command
* dysymtab
= NULL
;
2268 // get symbol table info
2269 macho_header
* mh
= (macho_header
*)fileToPrebind
;
2270 const uint32_t cmd_count
= mh
->ncmds
;
2271 const struct load_command
* const cmds
= (struct load_command
*)&fileToPrebind
[sizeof(macho_header
)];
2272 const struct load_command
* cmd
= cmds
;
2273 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
2277 const struct symtab_command
* symtab
= (struct symtab_command
*)cmd
;
2278 stringPool
= (const char*)&fileToPrebind
[symtab
->stroff
];
2279 symbolTable
= (struct macho_nlist
*)(&fileToPrebind
[symtab
->symoff
]);
2283 dysymtab
= (struct dysymtab_command
*)cmd
;
2286 cmd
= (const struct load_command
*)(((char*)cmd
)+cmd
->cmdsize
);
2289 // walk all imports and re-resolve their n_value (needed incase prebinding is invalid)
2290 struct macho_nlist
* lastImport
= &symbolTable
[dysymtab
->iundefsym
+dysymtab
->nundefsym
];
2291 for (struct macho_nlist
* entry
= &symbolTable
[dysymtab
->iundefsym
]; entry
< lastImport
; ++entry
) {
2293 entry
->n_value
= this->resolveUndefined(context
, entry
, this->usesTwoLevelNameSpace(), &dummy
);
2296 // walk all exports and slide their n_value
2297 struct macho_nlist
* lastExport
= &symbolTable
[dysymtab
->iextdefsym
+dysymtab
->nextdefsym
];
2298 for (struct macho_nlist
* entry
= &symbolTable
[dysymtab
->iextdefsym
]; entry
< lastExport
; ++entry
) {
2299 entry
->n_value
+= fSlide
;
2302 // walk all local symbols and slide their n_value
2303 struct macho_nlist
* lastLocal
= &symbolTable
[dysymtab
->ilocalsym
+dysymtab
->nlocalsym
];
2304 for (struct macho_nlist
* entry
= &symbolTable
[dysymtab
->ilocalsym
]; entry
< lastLocal
; ++entry
) {
2305 if ( (entry
->n_type
& N_TYPE
) == N_SECT
)
2306 entry
->n_value
+= fSlide
;
2309 // walk all local relocations and reset every PPC_RELOC_PB_LA_PTR r_value
2310 relocation_info
* const relocsStart
= (struct relocation_info
*)(&fileToPrebind
[dysymtab
->locreloff
]);
2311 relocation_info
* const relocsEnd
= &relocsStart
[dysymtab
->nlocrel
];
2312 for (relocation_info
* reloc
=relocsStart
; reloc
< relocsEnd
; ++reloc
) {
2313 if ( (reloc
->r_address
& R_SCATTERED
) != 0 ) {
2314 struct scattered_relocation_info
* sreloc
= (struct scattered_relocation_info
*)reloc
;
2315 if (sreloc
->r_length
== RELOC_SIZE
) {
2316 switch(sreloc
->r_type
) {
2317 #if __ppc__ || __ppc64__
2318 case PPC_RELOC_PB_LA_PTR
:
2320 case GENERIC_RELOC_PB_LA_PTR
:
2322 #error unknown architecture
2324 sreloc
->r_value
+= fSlide
;
2334 SegmentMachO::SegmentMachO(const struct macho_segment_command
* cmd
, ImageLoaderMachO
* image
, const uint8_t* fileData
)
2335 : fImage(image
), fSize(cmd
->vmsize
), fFileSize(cmd
->filesize
), fFileOffset(cmd
->fileoff
), fPreferredLoadAddress(cmd
->vmaddr
),
2336 fVMProtection(cmd
->initprot
), fHasFixUps(false), fUnMapOnDestruction(false)
2338 strncpy(fName
, cmd
->segname
, 16);
2340 // scan sections for fix-up bit
2341 const struct macho_section
* const sectionsStart
= (struct macho_section
*)((char*)cmd
+ sizeof(struct macho_segment_command
));
2342 const struct macho_section
* const sectionsEnd
= §ionsStart
[cmd
->nsects
];
2343 for (const struct macho_section
* sect
=sectionsStart
; sect
< sectionsEnd
; ++sect
) {
2344 if ( (sect
->flags
& (S_ATTR_EXT_RELOC
| S_ATTR_LOC_RELOC
)) != 0 )
2349 SegmentMachO::~SegmentMachO()
2351 if ( fUnMapOnDestruction
) {
2352 //fprintf(stderr, "unmapping segment %s at 0x%08lX\n", getName(), getActualLoadAddress());
2353 munmap((void*)(this->getActualLoadAddress()), this->getSize());
2357 const ImageLoader
* SegmentMachO::getImage()
2362 const char* SegmentMachO::getName()
2367 uintptr_t SegmentMachO::getSize()
2372 uintptr_t SegmentMachO::getFileSize()
2377 uintptr_t SegmentMachO::getFileOffset()
2382 bool SegmentMachO::readable()
2384 return ( (fVMProtection
& VM_PROT_READ
) != 0);
2387 bool SegmentMachO::writeable()
2389 return ((fVMProtection
& VM_PROT_WRITE
) != 0);
2392 bool SegmentMachO::executable()
2394 return ((fVMProtection
& VM_PROT_EXECUTE
) != 0);
2397 bool SegmentMachO::unaccessible()
2399 return (fVMProtection
== 0);
2402 bool SegmentMachO::hasFixUps()
2407 uintptr_t SegmentMachO::getActualLoadAddress()
2409 return fPreferredLoadAddress
+ fImage
->fSlide
;
2412 uintptr_t SegmentMachO::getPreferredLoadAddress()
2414 return fPreferredLoadAddress
;
2417 bool SegmentMachO::hasPreferredLoadAddress()
2419 return (fPreferredLoadAddress
!= 0);
2422 void SegmentMachO::setUnMapWhenDestructed(bool unmap
)
2424 fUnMapOnDestruction
= unmap
;
2427 static uint32_t *buildCRCTable(void)
2429 uint32_t *table
= new uint32_t[256];
2430 uint32_t p
= 0xedb88320UL
; // standard CRC-32 polynomial
2432 for (unsigned int i
= 0; i
< 256; i
++) {
2434 for (unsigned int j
= 0; j
< 8; j
++) {
2435 if ( c
& 1 ) c
= p
^ (c
>> 1);
2444 uint32_t SegmentMachO::crc32()
2446 if ( !readable() ) return 0;
2448 static uint32_t *crcTable
= NULL
;
2449 if ( !crcTable
) crcTable
= buildCRCTable();
2451 uint32_t crc
= ~(uint32_t)0;
2452 uint8_t *p
= (uint8_t *)getActualLoadAddress();
2453 uint8_t *end
= p
+ getSize();
2455 crc
= crcTable
[(crc
& 0xff) ^ (*p
++)] ^ (crc
>> 8);
2457 return crc
^ ~(uint32_t)0;