]>
Commit | Line | Data |
---|---|---|
39a8cd10 A |
1 | /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- |
2 | * | |
412ebb8e | 3 | * Copyright (c) 2004-2010 Apple Inc. All rights reserved. |
39a8cd10 A |
4 | * |
5 | * @APPLE_LICENSE_HEADER_START@ | |
6 | * | |
7 | * This file contains Original Code and/or Modifications of Original Code | |
8 | * as defined in and that are subject to the Apple Public Source License | |
9 | * Version 2.0 (the 'License'). You may not use this file except in | |
10 | * compliance with the License. Please obtain a copy of the License at | |
11 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
12 | * file. | |
13 | * | |
14 | * The Original Code and all software distributed under the License are | |
15 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
16 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
17 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
18 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
19 | * Please see the License for the specific language governing rights and | |
20 | * limitations under the License. | |
21 | * | |
22 | * @APPLE_LICENSE_HEADER_END@ | |
23 | */ | |
24 | ||
25 | // work around until conformance work is complete rdar://problem/4508801 | |
26 | #define __srr0 srr0 | |
27 | #define __eip eip | |
28 | #define __rip rip | |
29 | ||
30 | ||
31 | #include <string.h> | |
32 | #include <fcntl.h> | |
33 | #include <errno.h> | |
34 | #include <sys/types.h> | |
35 | #include <sys/fcntl.h> | |
36 | #include <sys/stat.h> | |
37 | #include <sys/mman.h> | |
38 | #include <mach/mach.h> | |
39 | #include <mach/thread_status.h> | |
40 | #include <mach-o/loader.h> | |
41 | #include <mach-o/reloc.h> | |
42 | #include <mach-o/nlist.h> | |
43 | #include <sys/sysctl.h> | |
44 | #include <libkern/OSAtomic.h> | |
45 | #include <libkern/OSCacheControl.h> | |
46 | ||
47 | #if __ppc__ || __ppc64__ | |
48 | #include <mach-o/ppc/reloc.h> | |
49 | #endif | |
50 | #if __x86_64__ | |
51 | #include <mach-o/x86_64/reloc.h> | |
52 | #endif | |
53 | #if __arm__ | |
54 | #include <mach-o/arm/reloc.h> | |
55 | #endif | |
56 | ||
57 | #include "ImageLoaderMachOClassic.h" | |
58 | #include "mach-o/dyld_images.h" | |
59 | ||
60 | // optimize strcmp for ppc | |
61 | #if __ppc__ | |
62 | #include <ppc_intrinsics.h> | |
63 | #else | |
64 | #define astrcmp(a,b) strcmp(a,b) | |
65 | #endif | |
66 | ||
67 | ||
68 | // in dyldStartup.s | |
69 | extern "C" void fast_stub_binding_helper_interface(); | |
70 | ||
71 | ||
72 | #if __x86_64__ | |
73 | #define POINTER_RELOC X86_64_RELOC_UNSIGNED | |
74 | #else | |
75 | #define POINTER_RELOC GENERIC_RELOC_VANILLA | |
76 | #endif | |
77 | ||
78 | ||
79 | // relocation_info.r_length field has value 3 for 64-bit executables and value 2 for 32-bit executables | |
80 | #if __LP64__ | |
81 | #define RELOC_SIZE 3 | |
82 | #define LC_SEGMENT_COMMAND LC_SEGMENT_64 | |
83 | #define LC_ROUTINES_COMMAND LC_ROUTINES_64 | |
84 | struct macho_segment_command : public segment_command_64 {}; | |
85 | struct macho_section : public section_64 {}; | |
86 | struct macho_routines_command : public routines_command_64 {}; | |
87 | #else | |
88 | #define RELOC_SIZE 2 | |
89 | #define LC_SEGMENT_COMMAND LC_SEGMENT | |
90 | #define LC_ROUTINES_COMMAND LC_ROUTINES | |
91 | struct macho_segment_command : public segment_command {}; | |
92 | struct macho_section : public section {}; | |
93 | struct macho_routines_command : public routines_command {}; | |
94 | #endif | |
95 | ||
96 | ||
97 | ||
98 | ||
99 | // create image for main executable | |
100 | ImageLoaderMachOClassic* ImageLoaderMachOClassic::instantiateMainExecutable(const macho_header* mh, uintptr_t slide, const char* path, | |
101 | unsigned int segCount, unsigned int libCount, const LinkContext& context) | |
102 | { | |
103 | ImageLoaderMachOClassic* image = ImageLoaderMachOClassic::instantiateStart(mh, path, segCount, libCount); | |
104 | ||
105 | // set slide for PIE programs | |
106 | image->setSlide(slide); | |
107 | ||
108 | // for PIE record end of program, to know where to start loading dylibs | |
412ebb8e | 109 | if ( slide != 0 ) |
39a8cd10 A |
110 | fgNextPIEDylibAddress = (uintptr_t)image->getEnd(); |
111 | ||
112 | image->instantiateFinish(context); | |
412ebb8e A |
113 | image->setMapped(context); |
114 | ||
39a8cd10 A |
115 | #if __i386__ |
116 | // kernel may have mapped in __IMPORT segment read-only, we need it read/write to do binding | |
117 | if ( image->fReadOnlyImportSegment ) { | |
118 | for(unsigned int i=0; i < image->fSegmentsCount; ++i) { | |
119 | if ( image->segIsReadOnlyImport(i) ) | |
120 | image->segMakeWritable(i, context); | |
121 | } | |
122 | } | |
123 | #endif | |
124 | ||
125 | if ( context.verboseMapping ) { | |
126 | dyld::log("dyld: Main executable mapped %s\n", path); | |
127 | for(unsigned int i=0, e=image->segmentCount(); i < e; ++i) { | |
128 | const char* name = image->segName(i); | |
129 | if ( (strcmp(name, "__PAGEZERO") == 0) || (strcmp(name, "__UNIXSTACK") == 0) ) | |
130 | dyld::log("%18s at 0x%08lX->0x%08lX\n", name, image->segPreferredLoadAddress(i), image->segPreferredLoadAddress(i)+image->segSize(i)); | |
131 | else | |
132 | dyld::log("%18s at 0x%08lX->0x%08lX\n", name, image->segActualLoadAddress(i), image->segActualEndAddress(i)); | |
133 | } | |
134 | } | |
135 | ||
136 | return image; | |
137 | } | |
138 | ||
139 | // create image by mapping in a mach-o file | |
140 | ImageLoaderMachOClassic* ImageLoaderMachOClassic::instantiateFromFile(const char* path, int fd, const uint8_t* fileData, | |
141 | uint64_t offsetInFat, uint64_t lenInFat, const struct stat& info, | |
412ebb8e A |
142 | unsigned int segCount, unsigned int libCount, |
143 | const struct linkedit_data_command* codeSigCmd, const LinkContext& context) | |
39a8cd10 A |
144 | { |
145 | ImageLoaderMachOClassic* image = ImageLoaderMachOClassic::instantiateStart((macho_header*)fileData, path, segCount, libCount); | |
146 | try { | |
147 | // record info about file | |
148 | image->setFileInfo(info.st_dev, info.st_ino, info.st_mtime); | |
149 | ||
39a8cd10 | 150 | #if CODESIGNING_SUPPORT |
412ebb8e A |
151 | // if this image is code signed, let kernel validate signature before mapping any pages from image |
152 | if ( codeSigCmd != NULL ) | |
153 | image->loadCodeSignature(codeSigCmd, fd, offsetInFat); | |
39a8cd10 A |
154 | #endif |
155 | ||
412ebb8e A |
156 | // mmap segments |
157 | image->mapSegmentsClassic(fd, offsetInFat, lenInFat, info.st_size, context); | |
158 | ||
159 | // finish up | |
160 | image->instantiateFinish(context); | |
161 | ||
39a8cd10 A |
162 | // if path happens to be same as in LC_DYLIB_ID load command use that, otherwise malloc a copy of the path |
163 | const char* installName = image->getInstallPath(); | |
164 | if ( (installName != NULL) && (strcmp(installName, path) == 0) && (path[0] == '/') ) | |
165 | image->setPathUnowned(installName); | |
166 | else if ( path[0] != '/' ) { | |
167 | // rdar://problem/5135363 turn relative paths into absolute paths so gdb, Symbolication can later find them | |
168 | char realPath[MAXPATHLEN]; | |
169 | if ( realpath(path, realPath) != NULL ) | |
170 | image->setPath(realPath); | |
171 | else | |
172 | image->setPath(path); | |
173 | } | |
174 | else | |
175 | image->setPath(path); | |
176 | ||
412ebb8e A |
177 | // make sure path is stable before recording in dyld_all_image_infos |
178 | image->setMapped(context); | |
179 | ||
39a8cd10 A |
180 | // pre-fetch content of __DATA segment for faster launches |
181 | // don't do this on prebound images or if prefetching is disabled | |
182 | if ( !context.preFetchDisabled && !image->isPrebindable()) | |
183 | image->preFetchDATA(fd, offsetInFat, context); | |
184 | ||
39a8cd10 A |
185 | } |
186 | catch (...) { | |
187 | // ImageLoader::setMapped() can throw an exception to block loading of image | |
188 | // <rdar://problem/6169686> Leaked fSegmentsArray and image segments during failed dlopen_preflight | |
189 | delete image; | |
190 | throw; | |
191 | } | |
192 | ||
193 | return image; | |
194 | } | |
195 | ||
196 | // create image by using cached mach-o file | |
412ebb8e | 197 | ImageLoaderMachOClassic* ImageLoaderMachOClassic::instantiateFromCache(const macho_header* mh, const char* path, long slide, const struct stat& info, |
39a8cd10 A |
198 | unsigned int segCount, unsigned int libCount, const LinkContext& context) |
199 | { | |
200 | ImageLoaderMachOClassic* image = ImageLoaderMachOClassic::instantiateStart(mh, path, segCount, libCount); | |
201 | try { | |
202 | // record info about file | |
203 | image->setFileInfo(info.st_dev, info.st_ino, info.st_mtime); | |
204 | ||
205 | // remember this is from shared cache and cannot be unloaded | |
206 | image->fInSharedCache = true; | |
207 | image->setNeverUnload(); | |
208 | ||
209 | // segments already mapped in cache | |
210 | if ( context.verboseMapping ) { | |
211 | dyld::log("dyld: Using shared cached for %s\n", path); | |
212 | for(unsigned int i=0, e=image->segmentCount(); i < e; ++i) { | |
213 | dyld::log("%18s at 0x%08lX->0x%08lX\n", image->segName(i), image->segActualLoadAddress(i), image->segActualEndAddress(i)); | |
214 | } | |
215 | } | |
216 | ||
217 | image->instantiateFinish(context); | |
412ebb8e | 218 | image->setMapped(context); |
39a8cd10 A |
219 | } |
220 | catch (...) { | |
221 | // ImageLoader::setMapped() can throw an exception to block loading of image | |
222 | // <rdar://problem/6169686> Leaked fSegmentsArray and image segments during failed dlopen_preflight | |
223 | delete image; | |
224 | throw; | |
225 | } | |
226 | ||
227 | return image; | |
228 | } | |
229 | ||
230 | // create image by copying an in-memory mach-o file | |
231 | ImageLoaderMachOClassic* ImageLoaderMachOClassic::instantiateFromMemory(const char* moduleName, const macho_header* mh, uint64_t len, | |
232 | unsigned int segCount, unsigned int libCount, const LinkContext& context) | |
233 | { | |
234 | ImageLoaderMachOClassic* image = ImageLoaderMachOClassic::instantiateStart(mh, moduleName, segCount, libCount); | |
235 | try { | |
236 | // map segments | |
237 | if ( mh->filetype == MH_EXECUTE ) | |
238 | throw "can't load another MH_EXECUTE"; | |
239 | ||
240 | // vmcopy segments | |
241 | image->ImageLoaderMachO::mapSegments((const void*)mh, len, context); | |
242 | ||
243 | // for compatibility, never unload dylibs loaded from memory | |
244 | image->setNeverUnload(); | |
245 | ||
246 | // bundle loads need path copied | |
247 | if ( moduleName != NULL ) | |
248 | image->setPath(moduleName); | |
249 | ||
250 | image->instantiateFinish(context); | |
412ebb8e | 251 | image->setMapped(context); |
39a8cd10 A |
252 | } |
253 | catch (...) { | |
254 | // ImageLoader::setMapped() can throw an exception to block loading of image | |
255 | // <rdar://problem/6169686> Leaked fSegmentsArray and image segments during failed dlopen_preflight | |
256 | delete image; | |
257 | throw; | |
258 | } | |
259 | ||
260 | return image; | |
261 | } | |
262 | ||
263 | ||
264 | ImageLoaderMachOClassic::ImageLoaderMachOClassic(const macho_header* mh, const char* path, | |
265 | unsigned int segCount, uint32_t segOffsets[], unsigned int libCount) | |
266 | : ImageLoaderMachO(mh, path, segCount, segOffsets, libCount), fStrings(NULL), fSymbolTable(NULL), fDynamicInfo(NULL) | |
267 | { | |
268 | } | |
269 | ||
270 | // construct ImageLoaderMachOClassic using "placement new" with SegmentMachO objects array at end | |
271 | ImageLoaderMachOClassic* ImageLoaderMachOClassic::instantiateStart(const macho_header* mh, const char* path, | |
272 | unsigned int segCount, unsigned int libCount) | |
273 | { | |
274 | size_t size = sizeof(ImageLoaderMachOClassic) + segCount * sizeof(uint32_t) + libCount * sizeof(ImageLoader*); | |
275 | ImageLoaderMachOClassic* allocatedSpace = static_cast<ImageLoaderMachOClassic*>(malloc(size)); | |
276 | if ( allocatedSpace == NULL ) | |
277 | throw "malloc failed"; | |
278 | uint32_t* segOffsets = ((uint32_t*)(((uint8_t*)allocatedSpace) + sizeof(ImageLoaderMachOClassic))); | |
279 | bzero(&segOffsets[segCount], libCount*sizeof(void*)); // zero out lib array | |
280 | return new (allocatedSpace) ImageLoaderMachOClassic(mh, path, segCount, segOffsets, libCount); | |
281 | } | |
282 | ||
283 | ||
284 | ||
285 | // common code to finish initializing object | |
286 | void ImageLoaderMachOClassic::instantiateFinish(const LinkContext& context) | |
287 | { | |
288 | // now that segments are mapped in, get real fMachOData, fLinkEditBase, and fSlide | |
289 | this->parseLoadCmds(); | |
39a8cd10 A |
290 | } |
291 | ||
292 | ImageLoaderMachOClassic::~ImageLoaderMachOClassic() | |
293 | { | |
294 | // don't do clean up in ~ImageLoaderMachO() because virtual call to segmentCommandOffsets() won't work | |
295 | destroy(); | |
296 | } | |
297 | ||
298 | uint32_t* ImageLoaderMachOClassic::segmentCommandOffsets() const | |
299 | { | |
300 | return ((uint32_t*)(((uint8_t*)this) + sizeof(ImageLoaderMachOClassic))); | |
301 | } | |
302 | ||
303 | ||
304 | ImageLoader* ImageLoaderMachOClassic::libImage(unsigned int libIndex) const | |
305 | { | |
306 | const uintptr_t* images = ((uintptr_t*)(((uint8_t*)this) + sizeof(ImageLoaderMachOClassic) + fSegmentsCount*sizeof(uint32_t))); | |
412ebb8e A |
307 | // mask off low bits |
308 | return (ImageLoader*)(images[libIndex] & (-4)); | |
39a8cd10 A |
309 | } |
310 | ||
311 | bool ImageLoaderMachOClassic::libReExported(unsigned int libIndex) const | |
312 | { | |
313 | const uintptr_t* images = ((uintptr_t*)(((uint8_t*)this) + sizeof(ImageLoaderMachOClassic) + fSegmentsCount*sizeof(uint32_t))); | |
314 | // re-export flag is low bit | |
315 | return ((images[libIndex] & 1) != 0); | |
316 | } | |
317 | ||
412ebb8e A |
318 | bool ImageLoaderMachOClassic::libIsUpward(unsigned int libIndex) const |
319 | { | |
320 | const uintptr_t* images = ((uintptr_t*)(((uint8_t*)this) + sizeof(ImageLoaderMachOClassic) + fSegmentsCount*sizeof(uint32_t))); | |
321 | // upward flag is second bit | |
322 | return ((images[libIndex] & 2) != 0); | |
323 | } | |
324 | ||
39a8cd10 | 325 | |
412ebb8e | 326 | void ImageLoaderMachOClassic::setLibImage(unsigned int libIndex, ImageLoader* image, bool reExported, bool upward) |
39a8cd10 A |
327 | { |
328 | uintptr_t* images = ((uintptr_t*)(((uint8_t*)this) + sizeof(ImageLoaderMachOClassic) + fSegmentsCount*sizeof(uint32_t))); | |
329 | uintptr_t value = (uintptr_t)image; | |
330 | if ( reExported ) | |
331 | value |= 1; | |
412ebb8e A |
332 | if ( upward ) |
333 | value |= 2; | |
39a8cd10 A |
334 | images[libIndex] = value; |
335 | } | |
336 | ||
337 | ||
338 | void ImageLoaderMachOClassic::setSymbolTableInfo(const macho_nlist* symbols, const char* strings, const dysymtab_command* dynSym) | |
339 | { | |
340 | fSymbolTable = symbols; | |
341 | fStrings = strings; | |
342 | fDynamicInfo = dynSym; | |
343 | } | |
344 | ||
345 | void ImageLoaderMachOClassic::prefetchLINKEDIT(const LinkContext& context) | |
346 | { | |
347 | // always prefetch a subrange of __LINKEDIT pages | |
348 | uintptr_t symbolTableStart = (uintptr_t)fSymbolTable; | |
349 | uintptr_t stringTableStart = (uintptr_t)fStrings; | |
350 | uintptr_t start; | |
351 | // if image did not load at preferred address | |
352 | if ( segPreferredLoadAddress(0) != (uintptr_t)fMachOData ) { | |
353 | // local relocations will be processed, so start pre-fetch at local symbols | |
354 | start = (uintptr_t)fMachOData + fDynamicInfo->locreloff; | |
355 | } | |
356 | else { | |
357 | // otherwise start pre-fetch at global symbols section of symbol table | |
358 | start = symbolTableStart + fDynamicInfo->iextdefsym * sizeof(macho_nlist); | |
359 | } | |
360 | // prefetch ends at end of last undefined string in string pool | |
361 | uintptr_t end = stringTableStart; | |
362 | if ( fDynamicInfo->nundefsym != 0 ) | |
363 | end += fSymbolTable[fDynamicInfo->iundefsym+fDynamicInfo->nundefsym-1].n_un.n_strx; | |
364 | else if ( fDynamicInfo->nextdefsym != 0 ) | |
365 | end += fSymbolTable[fDynamicInfo->iextdefsym+fDynamicInfo->nextdefsym-1].n_un.n_strx; | |
366 | ||
367 | // round to whole pages | |
368 | start = start & (-4096); | |
369 | end = (end + 4095) & (-4096); | |
370 | ||
371 | // skip if there is only one page | |
372 | if ( (end-start) > 4096 ) { | |
373 | madvise((void*)start, end-start, MADV_WILLNEED); | |
374 | fgTotalBytesPreFetched += (end-start); | |
375 | if ( context.verboseMapping ) { | |
376 | dyld::log("%18s prefetching 0x%0lX -> 0x%0lX\n", "__LINKEDIT", start, end-1); | |
377 | } | |
378 | } | |
379 | } | |
380 | ||
381 | ||
382 | #if SPLIT_SEG_DYLIB_SUPPORT | |
383 | unsigned int | |
384 | ImageLoaderMachOClassic::getExtraZeroFillEntriesCount() | |
385 | { | |
386 | // calculate mapping entries | |
387 | unsigned int extraZeroFillEntries = 0; | |
388 | for(unsigned int i=0; i < fSegmentsCount; ++i) { | |
389 | if ( segHasTrailingZeroFill(i) ) | |
390 | ++extraZeroFillEntries; | |
391 | } | |
392 | ||
393 | return extraZeroFillEntries; | |
394 | } | |
395 | ||
396 | void | |
397 | ImageLoaderMachOClassic::initMappingTable(uint64_t offsetInFat, | |
398 | shared_file_mapping_np *mappingTable) | |
399 | { | |
400 | for(unsigned int i=0,entryIndex=0; i < fSegmentsCount; ++i, ++entryIndex) { | |
401 | shared_file_mapping_np* entry = &mappingTable[entryIndex]; | |
402 | entry->sfm_address = segActualLoadAddress(i); | |
403 | entry->sfm_size = segFileSize(i); | |
404 | entry->sfm_file_offset = segFileOffset(i) + offsetInFat; | |
405 | entry->sfm_init_prot = VM_PROT_NONE; | |
406 | if ( !segUnaccessible(i) ) { | |
407 | if ( segExecutable(i) ) | |
408 | entry->sfm_init_prot |= VM_PROT_EXECUTE; | |
409 | if ( segReadable(i) ) | |
410 | entry->sfm_init_prot |= VM_PROT_READ; | |
411 | if ( segWriteable(i) ) | |
412 | entry->sfm_init_prot |= VM_PROT_WRITE | VM_PROT_COW; | |
413 | } | |
414 | entry->sfm_max_prot = entry->sfm_init_prot; | |
415 | if ( segHasTrailingZeroFill(i) ) { | |
416 | shared_file_mapping_np* zfentry = &mappingTable[++entryIndex]; | |
417 | zfentry->sfm_address = entry->sfm_address + segFileSize(i); | |
418 | zfentry->sfm_size = segSize(i) - segFileSize(i); | |
419 | zfentry->sfm_file_offset = 0; | |
420 | zfentry->sfm_init_prot = entry->sfm_init_prot | VM_PROT_COW | VM_PROT_ZF; | |
421 | zfentry->sfm_max_prot = zfentry->sfm_init_prot; | |
422 | } | |
423 | } | |
424 | } | |
425 | ||
426 | int | |
427 | ImageLoaderMachOClassic::mapSplitSegDylibOutsideSharedRegion(int fd, | |
428 | uint64_t offsetInFat, | |
429 | uint64_t lenInFat, | |
430 | uint64_t fileLen, | |
431 | const LinkContext& context) | |
432 | { | |
433 | uintptr_t nextAltLoadAddress = 0; | |
434 | const unsigned int segmentCount = fSegmentsCount; | |
435 | const unsigned int extraZeroFillEntries = getExtraZeroFillEntriesCount(); | |
436 | const unsigned int regionCount = segmentCount+extraZeroFillEntries; | |
437 | shared_file_mapping_np regions[regionCount]; | |
438 | initMappingTable(offsetInFat, regions); | |
439 | int r = -1; | |
440 | // find space somewhere to allocate split seg | |
441 | bool foundRoom = false; | |
442 | while ( ! foundRoom ) { | |
443 | foundRoom = true; | |
444 | for(unsigned int i=0; i < regionCount; ++i) { | |
445 | vm_address_t addr = nextAltLoadAddress + regions[i].sfm_address - regions[0].sfm_address; | |
446 | vm_size_t size = regions[i].sfm_size ; | |
447 | r = vm_allocate(mach_task_self(), &addr, size, false /*only this range*/); | |
448 | if ( 0 != r ) { | |
449 | // no room here, deallocate what has succeeded so far | |
450 | for(unsigned int j=0; j < i; ++j) { | |
451 | vm_address_t addr = nextAltLoadAddress + regions[j].sfm_address - regions[0].sfm_address; | |
452 | vm_size_t size = regions[j].sfm_size ; | |
453 | (void)vm_deallocate(mach_task_self(), addr, size); | |
454 | } | |
455 | nextAltLoadAddress += 0x00100000; // skip ahead 1MB and try again | |
456 | // skip over shared region | |
457 | if ( (SHARED_REGION_BASE <= nextAltLoadAddress) && (nextAltLoadAddress < (SHARED_REGION_BASE + SHARED_REGION_SIZE)) ) | |
458 | nextAltLoadAddress = (SHARED_REGION_BASE + SHARED_REGION_SIZE); | |
459 | if ( nextAltLoadAddress > 0xFF000000 ) | |
460 | throw "can't map split seg anywhere"; | |
461 | foundRoom = false; | |
462 | break; | |
463 | } | |
464 | } | |
465 | } | |
466 | ||
467 | // map in each region | |
468 | uintptr_t slide = nextAltLoadAddress - regions[0].sfm_address; | |
469 | this->setSlide(slide); | |
470 | for(unsigned int i=0; i < regionCount; ++i) { | |
471 | if ( ((regions[i].sfm_init_prot & VM_PROT_ZF) != 0) || (regions[i].sfm_size == 0) ) { | |
472 | // nothing to mmap for zero-fills areas, they are just vm_allocated | |
473 | } | |
474 | else { | |
475 | void* mmapAddress = (void*)(uintptr_t)(regions[i].sfm_address + slide); | |
476 | size_t size = regions[i].sfm_size; | |
477 | int protection = 0; | |
478 | if ( regions[i].sfm_init_prot & VM_PROT_EXECUTE ) | |
479 | protection |= PROT_EXEC; | |
480 | if ( regions[i].sfm_init_prot & VM_PROT_READ ) | |
481 | protection |= PROT_READ; | |
482 | if ( regions[i].sfm_init_prot & VM_PROT_WRITE ) | |
483 | protection |= PROT_WRITE; | |
484 | off_t offset = regions[i].sfm_file_offset; | |
485 | //dyld::log("mmap(%p, 0x%08lX, %s\n", mmapAddress, size, fPath); | |
486 | mmapAddress = mmap(mmapAddress, size, protection, MAP_FIXED | MAP_PRIVATE, fd, offset); | |
487 | if ( mmapAddress == ((void*)(-1)) ) | |
488 | throw "mmap error"; | |
489 | } | |
490 | } | |
491 | ||
492 | // logging | |
493 | if ( context.verboseMapping ) { | |
494 | dyld::log("dyld: Mapping split-seg outside shared region, slid by 0x%08lX %s\n", this->fSlide, this->getPath()); | |
495 | for(unsigned int segIndex=0,entryIndex=0; segIndex < segmentCount; ++segIndex, ++entryIndex){ | |
496 | const shared_file_mapping_np* entry = ®ions[entryIndex]; | |
497 | if ( (entry->sfm_init_prot & VM_PROT_ZF) == 0 ) | |
498 | dyld::log("%18s at 0x%08lX->0x%08lX\n", | |
499 | segName(segIndex), segActualLoadAddress(segIndex), segActualEndAddress(segIndex)-1); | |
500 | if ( entryIndex < (regionCount-1) ) { | |
501 | const shared_file_mapping_np* nextEntry = ®ions[entryIndex+1]; | |
502 | if ( (nextEntry->sfm_init_prot & VM_PROT_ZF) != 0 ) { | |
503 | uint64_t segOffset = nextEntry->sfm_address - entry->sfm_address; | |
504 | dyld::log("%18s at 0x%08lX->0x%08lX (zerofill)\n", | |
505 | segName(segIndex), (uintptr_t)(segActualLoadAddress(segIndex) + segOffset), (uintptr_t)(segActualLoadAddress(segIndex) + segOffset + nextEntry->sfm_size - 1)); | |
506 | ++entryIndex; | |
507 | } | |
508 | } | |
509 | } | |
510 | } | |
511 | ||
512 | return r; | |
513 | } | |
514 | #endif // SPLIT_SEG_DYLIB_SUPPORT | |
515 | ||
516 | ||
517 | void ImageLoaderMachOClassic::mapSegmentsClassic(int fd, uint64_t offsetInFat, uint64_t lenInFat, uint64_t fileLen, const LinkContext& context) | |
518 | { | |
519 | // non-split segment libraries handled by super class | |
520 | if ( !fIsSplitSeg ) | |
521 | return ImageLoaderMachO::mapSegments(fd, offsetInFat, lenInFat, fileLen, context); | |
522 | ||
523 | #if SPLIT_SEG_SHARED_REGION_SUPPORT | |
412ebb8e A |
524 | // don't map split-seg dylibs into shared region if shared cache is in use |
525 | if ( ! context.dyldLoadedAtSameAddressNeededBySharedCache ) { | |
526 | // try to map into shared region at preferred address | |
527 | if ( mapSplitSegDylibInfoSharedRegion(fd, offsetInFat, lenInFat, fileLen, context) == 0) | |
528 | return; | |
529 | } | |
39a8cd10 A |
530 | // if there is a problem, fall into case where we map file somewhere outside the shared region |
531 | #endif | |
532 | ||
533 | #if SPLIT_SEG_DYLIB_SUPPORT | |
534 | // support old split-seg dylibs by mapping them where ever we find space | |
535 | if ( mapSplitSegDylibOutsideSharedRegion(fd, offsetInFat, lenInFat, fileLen, context) != 0 ) | |
536 | #endif | |
537 | throw "mapping error"; | |
538 | } | |
539 | ||
540 | ||
541 | #if SPLIT_SEG_SHARED_REGION_SUPPORT | |
542 | static int _shared_region_map_np(int fd, uint32_t count, const shared_file_mapping_np mappings[]) | |
543 | { | |
544 | return syscall(295, fd, count, mappings); | |
545 | } | |
546 | ||
547 | int | |
548 | ImageLoaderMachOClassic::mapSplitSegDylibInfoSharedRegion(int fd, | |
549 | uint64_t offsetInFat, | |
550 | uint64_t lenInFat, | |
551 | uint64_t fileLen, | |
552 | const LinkContext& context) | |
553 | { | |
554 | // build table of segments to map | |
555 | const unsigned int segmentCount = fSegmentsCount; | |
556 | const unsigned int extraZeroFillEntries = getExtraZeroFillEntriesCount(); | |
557 | const unsigned int mappingTableCount = segmentCount+extraZeroFillEntries; | |
558 | shared_file_mapping_np mappingTable[mappingTableCount]; | |
559 | initMappingTable(offsetInFat, mappingTable); | |
560 | ||
561 | // try to map it in shared | |
562 | int r = _shared_region_map_np(fd, mappingTableCount, mappingTable); | |
563 | if ( 0 == r ) { | |
564 | this->setNeverUnload(); | |
565 | if ( context.verboseMapping ) { | |
566 | dyld::log("dyld: Mapping split-seg shared %s\n", this->getPath()); | |
567 | for(unsigned int segIndex=0,entryIndex=0; segIndex < segmentCount; ++segIndex, ++entryIndex){ | |
568 | const shared_file_mapping_np* entry = &mappingTable[entryIndex]; | |
569 | if ( (entry->sfm_init_prot & VM_PROT_ZF) == 0 ) | |
570 | dyld::log("%18s at 0x%08lX->0x%08lX\n", | |
571 | segName(segIndex), segActualLoadAddress(segIndex), segActualEndAddress(segIndex)-1); | |
572 | if ( entryIndex < (mappingTableCount-1) ) { | |
573 | const shared_file_mapping_np* nextEntry = &mappingTable[entryIndex+1]; | |
574 | if ( (nextEntry->sfm_init_prot & VM_PROT_ZF) != 0 ) { | |
575 | uint64_t segOffset = nextEntry->sfm_address - entry->sfm_address; | |
576 | dyld::log("%18s at 0x%08lX->0x%08lX\n", | |
577 | segName(segIndex), (uintptr_t)(segActualLoadAddress(segIndex) + segOffset), | |
578 | (uintptr_t)(segActualLoadAddress(segIndex) + segOffset + nextEntry->sfm_size - 1)); | |
579 | ++entryIndex; | |
580 | } | |
581 | } | |
582 | } | |
583 | } | |
584 | } | |
585 | return r; | |
586 | } | |
587 | ||
588 | #endif // SPLIT_SEG_SHARED_REGION_SUPPORT | |
589 | ||
590 | // test if this image is re-exported through parent (the image that loaded this one) | |
591 | bool ImageLoaderMachOClassic::isSubframeworkOf(const LinkContext& context, const ImageLoader* parent) const | |
592 | { | |
593 | if ( fInUmbrella ) { | |
594 | const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; | |
595 | const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; | |
596 | const struct load_command* cmd = cmds; | |
597 | for (uint32_t i = 0; i < cmd_count; ++i) { | |
598 | if (cmd->cmd == LC_SUB_FRAMEWORK) { | |
599 | const struct sub_framework_command* subf = (struct sub_framework_command*)cmd; | |
600 | const char* exportThruName = (char*)cmd + subf->umbrella.offset; | |
601 | // need to match LC_SUB_FRAMEWORK string against the leaf name of the install location of parent... | |
602 | const char* parentInstallPath = parent->getInstallPath(); | |
603 | if ( parentInstallPath != NULL ) { | |
604 | const char* lastSlash = strrchr(parentInstallPath, '/'); | |
605 | if ( lastSlash != NULL ) { | |
606 | if ( strcmp(&lastSlash[1], exportThruName) == 0 ) | |
607 | return true; | |
608 | if ( context.imageSuffix != NULL ) { | |
609 | // when DYLD_IMAGE_SUFFIX is used, lastSlash string needs imageSuffix removed from end | |
610 | char reexportAndSuffix[strlen(context.imageSuffix)+strlen(exportThruName)+1]; | |
611 | strcpy(reexportAndSuffix, exportThruName); | |
612 | strcat(reexportAndSuffix, context.imageSuffix); | |
613 | if ( strcmp(&lastSlash[1], reexportAndSuffix) == 0 ) | |
614 | return true; | |
615 | } | |
616 | } | |
617 | } | |
618 | } | |
619 | cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); | |
620 | } | |
621 | } | |
622 | return false; | |
623 | } | |
624 | ||
625 | // test if child is re-exported | |
626 | bool ImageLoaderMachOClassic::hasSubLibrary(const LinkContext& context, const ImageLoader* child) const | |
627 | { | |
628 | if ( fHasSubLibraries ) { | |
629 | // need to match LC_SUB_LIBRARY string against the leaf name (without extension) of the install location of child... | |
630 | const char* childInstallPath = child->getInstallPath(); | |
631 | if ( childInstallPath != NULL ) { | |
632 | const char* lastSlash = strrchr(childInstallPath, '/'); | |
633 | if ( lastSlash != NULL ) { | |
634 | const char* firstDot = strchr(lastSlash, '.'); | |
635 | int len; | |
636 | if ( firstDot == NULL ) | |
637 | len = strlen(lastSlash); | |
638 | else | |
639 | len = firstDot-lastSlash-1; | |
640 | char childLeafName[len+1]; | |
641 | strncpy(childLeafName, &lastSlash[1], len); | |
642 | childLeafName[len] = '\0'; | |
643 | const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; | |
644 | const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; | |
645 | const struct load_command* cmd = cmds; | |
646 | for (uint32_t i = 0; i < cmd_count; ++i) { | |
647 | switch (cmd->cmd) { | |
648 | case LC_SUB_LIBRARY: | |
649 | { | |
650 | const struct sub_library_command* lib = (struct sub_library_command*)cmd; | |
651 | const char* aSubLibName = (char*)cmd + lib->sub_library.offset; | |
652 | if ( strcmp(aSubLibName, childLeafName) == 0 ) | |
653 | return true; | |
654 | if ( context.imageSuffix != NULL ) { | |
655 | // when DYLD_IMAGE_SUFFIX is used, childLeafName string needs imageSuffix removed from end | |
656 | char aSubLibNameAndSuffix[strlen(context.imageSuffix)+strlen(aSubLibName)+1]; | |
657 | strcpy(aSubLibNameAndSuffix, aSubLibName); | |
658 | strcat(aSubLibNameAndSuffix, context.imageSuffix); | |
659 | if ( strcmp(aSubLibNameAndSuffix, childLeafName) == 0 ) | |
660 | return true; | |
661 | } | |
662 | } | |
663 | break; | |
664 | } | |
665 | cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); | |
666 | } | |
667 | } | |
668 | } | |
669 | } | |
670 | if ( fHasSubUmbrella ) { | |
671 | // need to match LC_SUB_UMBRELLA string against the leaf name of install location of child... | |
672 | const char* childInstallPath = child->getInstallPath(); | |
673 | if ( childInstallPath != NULL ) { | |
674 | const char* lastSlash = strrchr(childInstallPath, '/'); | |
675 | if ( lastSlash != NULL ) { | |
676 | const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; | |
677 | const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; | |
678 | const struct load_command* cmd = cmds; | |
679 | for (uint32_t i = 0; i < cmd_count; ++i) { | |
680 | switch (cmd->cmd) { | |
681 | case LC_SUB_UMBRELLA: | |
682 | { | |
683 | const struct sub_umbrella_command* um = (struct sub_umbrella_command*)cmd; | |
684 | const char* aSubUmbrellaName = (char*)cmd + um->sub_umbrella.offset; | |
685 | if ( strcmp(aSubUmbrellaName, &lastSlash[1]) == 0 ) | |
686 | return true; | |
687 | if ( context.imageSuffix != NULL ) { | |
688 | // when DYLD_IMAGE_SUFFIX is used, lastSlash string needs imageSuffix removed from end | |
689 | char umbrellaAndSuffix[strlen(context.imageSuffix)+strlen(aSubUmbrellaName)+1]; | |
690 | strcpy(umbrellaAndSuffix, aSubUmbrellaName); | |
691 | strcat(umbrellaAndSuffix, context.imageSuffix); | |
692 | if ( strcmp(umbrellaAndSuffix, &lastSlash[1]) == 0 ) | |
693 | return true; | |
694 | } | |
695 | } | |
696 | break; | |
697 | } | |
698 | cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); | |
699 | } | |
700 | } | |
701 | } | |
702 | } | |
703 | return false; | |
704 | } | |
705 | ||
706 | ||
707 | uintptr_t ImageLoaderMachOClassic::getFirstWritableSegmentAddress() | |
708 | { | |
709 | // in split segment libraries r_address is offset from first writable segment | |
710 | for(unsigned int i=0; i < fSegmentsCount; ++i) { | |
711 | if ( segWriteable(i) ) | |
712 | return segActualLoadAddress(i); | |
713 | } | |
714 | throw "no writable segment"; | |
715 | } | |
716 | ||
717 | uintptr_t ImageLoaderMachOClassic::getRelocBase() | |
718 | { | |
719 | // r_address is either an offset from the first segment address | |
720 | // or from the first writable segment address | |
721 | #if __x86_64__ | |
722 | return getFirstWritableSegmentAddress(); | |
723 | #else | |
724 | if ( fIsSplitSeg ) | |
725 | return getFirstWritableSegmentAddress(); | |
726 | else | |
727 | return segActualLoadAddress(0); | |
728 | #endif | |
729 | } | |
730 | ||
731 | ||
732 | #if __ppc__ | |
733 | static inline void otherRelocsPPC(uintptr_t* locationToFix, uint8_t relocationType, uint16_t otherHalf, uintptr_t slide) | |
734 | { | |
735 | // low 16 bits of 32-bit ppc instructions need fixing | |
736 | struct ppcInstruction { uint16_t opcode; int16_t immediateValue; }; | |
737 | ppcInstruction* instruction = (ppcInstruction*)locationToFix; | |
738 | //uint32_t before = *((uint32_t*)locationToFix); | |
739 | switch ( relocationType ) | |
740 | { | |
741 | case PPC_RELOC_LO16: | |
742 | instruction->immediateValue = ((otherHalf << 16) | instruction->immediateValue) + slide; | |
743 | break; | |
744 | case PPC_RELOC_HI16: | |
745 | instruction->immediateValue = ((((instruction->immediateValue << 16) | otherHalf) + slide) >> 16); | |
746 | break; | |
747 | case PPC_RELOC_HA16: | |
748 | int16_t signedOtherHalf = (int16_t)(otherHalf & 0xffff); | |
749 | uint32_t temp = (instruction->immediateValue << 16) + signedOtherHalf + slide; | |
750 | if ( (temp & 0x00008000) != 0 ) | |
751 | temp += 0x00008000; | |
752 | instruction->immediateValue = temp >> 16; | |
753 | } | |
754 | //uint32_t after = *((uint32_t*)locationToFix); | |
755 | //dyld::log("dyld: ppc fixup %0p type %d from 0x%08X to 0x%08X\n", locationToFix, relocationType, before, after); | |
756 | } | |
757 | #endif | |
758 | ||
759 | #if PREBOUND_IMAGE_SUPPORT | |
760 | void ImageLoaderMachOClassic::resetPreboundLazyPointers(const LinkContext& context) | |
761 | { | |
762 | // loop through all local (internal) relocation records looking for pre-bound-lazy-pointer values | |
763 | const uintptr_t relocBase = this->getRelocBase(); | |
764 | register const uintptr_t slide = this->fSlide; | |
765 | const relocation_info* const relocsStart = (struct relocation_info*)(&fLinkEditBase[fDynamicInfo->locreloff]); | |
766 | const relocation_info* const relocsEnd = &relocsStart[fDynamicInfo->nlocrel]; | |
767 | for (const relocation_info* reloc=relocsStart; reloc < relocsEnd; ++reloc) { | |
768 | if ( (reloc->r_address & R_SCATTERED) != 0 ) { | |
769 | const struct scattered_relocation_info* sreloc = (struct scattered_relocation_info*)reloc; | |
770 | if (sreloc->r_length == RELOC_SIZE) { | |
771 | uintptr_t* locationToFix = (uintptr_t*)(sreloc->r_address + relocBase); | |
772 | switch(sreloc->r_type) { | |
773 | #if __ppc__ | |
774 | case PPC_RELOC_PB_LA_PTR: | |
775 | *locationToFix = sreloc->r_value + slide; | |
776 | break; | |
777 | #endif | |
778 | #if __i386__ | |
779 | case GENERIC_RELOC_PB_LA_PTR: | |
780 | *locationToFix = sreloc->r_value + slide; | |
781 | break; | |
782 | #endif | |
783 | #if __arm__ | |
784 | case ARM_RELOC_PB_LA_PTR: | |
785 | *locationToFix = sreloc->r_value + slide; | |
786 | break; | |
787 | #endif | |
788 | } | |
789 | } | |
790 | } | |
791 | } | |
792 | } | |
793 | #endif | |
794 | ||
795 | ||
796 | ||
797 | ||
798 | void ImageLoaderMachOClassic::rebase(const LinkContext& context) | |
799 | { | |
412ebb8e | 800 | CRSetCrashLogMessage2(this->getPath()); |
39a8cd10 A |
801 | register const uintptr_t slide = this->fSlide; |
802 | const uintptr_t relocBase = this->getRelocBase(); | |
803 | ||
804 | // prefetch any LINKEDIT pages needed | |
805 | if ( !context.preFetchDisabled && !this->isPrebindable()) | |
806 | this->prefetchLINKEDIT(context); | |
807 | ||
808 | // loop through all local (internal) relocation records | |
809 | const relocation_info* const relocsStart = (struct relocation_info*)(&fLinkEditBase[fDynamicInfo->locreloff]); | |
810 | const relocation_info* const relocsEnd = &relocsStart[fDynamicInfo->nlocrel]; | |
811 | for (const relocation_info* reloc=relocsStart; reloc < relocsEnd; ++reloc) { | |
812 | try { | |
813 | #if LINKEDIT_USAGE_DEBUG | |
814 | noteAccessedLinkEditAddress(reloc); | |
815 | #endif | |
816 | #if __x86_64__ | |
817 | // only one kind of local relocation supported for x86_64 | |
818 | if ( reloc->r_length != 3 ) | |
819 | throw "bad local relocation length"; | |
820 | if ( reloc->r_type != X86_64_RELOC_UNSIGNED ) | |
821 | throw "unknown local relocation type"; | |
822 | if ( reloc->r_pcrel != 0 ) | |
823 | throw "bad local relocation pc_rel"; | |
824 | if ( reloc->r_extern != 0 ) | |
825 | throw "extern relocation found with local relocations"; | |
826 | *((uintptr_t*)(reloc->r_address + relocBase)) += slide; | |
827 | #else | |
828 | if ( (reloc->r_address & R_SCATTERED) == 0 ) { | |
829 | if ( reloc->r_symbolnum == R_ABS ) { | |
830 | // ignore absolute relocations | |
831 | } | |
832 | else if (reloc->r_length == RELOC_SIZE) { | |
833 | switch(reloc->r_type) { | |
834 | case GENERIC_RELOC_VANILLA: | |
835 | *((uintptr_t*)(reloc->r_address + relocBase)) += slide; | |
836 | break; | |
837 | #if __ppc__ | |
838 | case PPC_RELOC_HI16: | |
839 | case PPC_RELOC_LO16: | |
840 | case PPC_RELOC_HA16: | |
841 | // some tools leave object file relocations in linked images | |
842 | otherRelocsPPC((uintptr_t*)(reloc->r_address + relocBase), reloc->r_type, reloc[1].r_address, slide); | |
843 | ++reloc; // these relocations come in pairs, skip next | |
844 | break; | |
845 | #endif | |
846 | default: | |
847 | throw "unknown local relocation type"; | |
848 | } | |
849 | } | |
850 | else { | |
851 | throw "bad local relocation length"; | |
852 | } | |
853 | } | |
854 | else { | |
855 | const struct scattered_relocation_info* sreloc = (struct scattered_relocation_info*)reloc; | |
856 | if (sreloc->r_length == RELOC_SIZE) { | |
857 | uintptr_t* locationToFix = (uintptr_t*)(sreloc->r_address + relocBase); | |
858 | switch(sreloc->r_type) { | |
859 | case GENERIC_RELOC_VANILLA: | |
860 | *locationToFix += slide; | |
861 | break; | |
862 | #if __ppc__ | |
863 | case PPC_RELOC_HI16: | |
864 | case PPC_RELOC_LO16: | |
865 | case PPC_RELOC_HA16: | |
866 | // Metrowerks compiler sometimes leaves object file relocations in linked images??? | |
867 | ++reloc; // these relocations come in pairs, get next one | |
868 | otherRelocsPPC(locationToFix, sreloc->r_type, reloc->r_address, slide); | |
869 | break; | |
870 | case PPC_RELOC_PB_LA_PTR: | |
871 | // do nothing | |
872 | break; | |
873 | #elif __ppc64__ | |
874 | case PPC_RELOC_PB_LA_PTR: | |
875 | // needed for compatibility with ppc64 binaries built with the first ld64 | |
876 | // which used PPC_RELOC_PB_LA_PTR relocs instead of GENERIC_RELOC_VANILLA for lazy pointers | |
877 | *locationToFix += slide; | |
878 | break; | |
879 | #elif __i386__ | |
880 | case GENERIC_RELOC_PB_LA_PTR: | |
881 | // do nothing | |
882 | break; | |
883 | #elif __arm__ | |
884 | case ARM_RELOC_PB_LA_PTR: | |
885 | // do nothing | |
886 | break; | |
887 | #endif | |
888 | default: | |
889 | throw "unknown local scattered relocation type"; | |
890 | } | |
891 | } | |
892 | else { | |
893 | throw "bad local scattered relocation length"; | |
894 | } | |
895 | } | |
896 | #endif // x86_64 | |
897 | } | |
898 | catch (const char* msg) { | |
899 | const uint8_t* r = (uint8_t*)reloc; | |
900 | dyld::throwf("%s in %s. reloc record at %p: 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X", | |
901 | msg, this->getPath(), reloc, r[0], r[1], r[2], r[3], r[4], r[5], r[6], r[7]); | |
902 | } | |
903 | } | |
904 | ||
905 | // update stats | |
906 | fgTotalRebaseFixups += fDynamicInfo->nlocrel; | |
412ebb8e | 907 | CRSetCrashLogMessage2(NULL); |
39a8cd10 A |
908 | } |
909 | ||
910 | ||
911 | ||
912 | const struct macho_nlist* ImageLoaderMachOClassic::binarySearchWithToc(const char* key, const char stringPool[], const struct macho_nlist symbols[], | |
913 | const struct dylib_table_of_contents toc[], uint32_t symbolCount, uint32_t hintIndex) const | |
914 | { | |
915 | int32_t high = symbolCount-1; | |
916 | int32_t mid = hintIndex; | |
917 | ||
918 | // handle out of range hint | |
919 | if ( mid >= (int32_t)symbolCount ) | |
920 | mid = symbolCount/2; | |
921 | ++ImageLoaderMachO::fgSymbolTableBinarySearchs; | |
922 | ++fgTotalBindImageSearches; | |
923 | ||
924 | //dyld::log("dyld: binarySearchWithToc for %s in %s\n", key, this->getShortName()); | |
925 | ||
926 | for (int32_t low = 0; low <= high; mid = (low+high)/2) { | |
927 | const uint32_t index = toc[mid].symbol_index; | |
928 | const struct macho_nlist* pivot = &symbols[index]; | |
929 | const char* pivotStr = &stringPool[pivot->n_un.n_strx]; | |
930 | #if LINKEDIT_USAGE_DEBUG | |
931 | noteAccessedLinkEditAddress(&toc[mid]); | |
932 | noteAccessedLinkEditAddress(pivot); | |
933 | noteAccessedLinkEditAddress(pivotStr); | |
934 | #endif | |
935 | int cmp = astrcmp(key, pivotStr); | |
936 | if ( cmp == 0 ) | |
937 | return pivot; | |
938 | if ( cmp > 0 ) { | |
939 | // key > pivot | |
940 | low = mid + 1; | |
941 | } | |
942 | else { | |
943 | // key < pivot | |
944 | high = mid - 1; | |
945 | } | |
946 | } | |
947 | return NULL; | |
948 | } | |
949 | ||
950 | const struct macho_nlist* ImageLoaderMachOClassic::binarySearch(const char* key, const char stringPool[], const struct macho_nlist symbols[], uint32_t symbolCount) const | |
951 | { | |
952 | // update stats | |
953 | ++fgTotalBindImageSearches; | |
954 | ++ImageLoaderMachO::fgSymbolTableBinarySearchs; | |
955 | ||
956 | //dyld::log("dyld: binarySearch for %s in %s, stringpool=%p, symbols=%p, symbolCount=%u\n", | |
957 | // key, this->getShortName(), stringPool, symbols, symbolCount); | |
958 | ||
959 | const struct macho_nlist* base = symbols; | |
960 | for (uint32_t n = symbolCount; n > 0; n /= 2) { | |
961 | const struct macho_nlist* pivot = &base[n/2]; | |
962 | const char* pivotStr = &stringPool[pivot->n_un.n_strx]; | |
963 | #if LINKEDIT_USAGE_DEBUG | |
964 | noteAccessedLinkEditAddress(pivot); | |
965 | noteAccessedLinkEditAddress(pivotStr); | |
966 | #endif | |
967 | int cmp = astrcmp(key, pivotStr); | |
968 | if ( cmp == 0 ) | |
969 | return pivot; | |
970 | if ( cmp > 0 ) { | |
971 | // key > pivot | |
972 | // move base to symbol after pivot | |
973 | base = &pivot[1]; | |
974 | --n; | |
975 | } | |
976 | else { | |
977 | // key < pivot | |
978 | // keep same base | |
979 | } | |
980 | } | |
981 | return NULL; | |
982 | } | |
983 | ||
984 | ||
985 | const ImageLoader::Symbol* ImageLoaderMachOClassic::findExportedSymbol(const char* name, const ImageLoader** foundIn) const | |
986 | { | |
987 | const struct macho_nlist* sym = NULL; | |
988 | if ( fDynamicInfo->tocoff == 0 ) | |
989 | sym = binarySearch(name, fStrings, &fSymbolTable[fDynamicInfo->iextdefsym], fDynamicInfo->nextdefsym); | |
990 | else | |
991 | sym = binarySearchWithToc(name, fStrings, fSymbolTable, (dylib_table_of_contents*)&fLinkEditBase[fDynamicInfo->tocoff], | |
992 | fDynamicInfo->ntoc, fDynamicInfo->nextdefsym); | |
993 | if ( sym != NULL ) { | |
994 | if ( foundIn != NULL ) | |
995 | *foundIn = (ImageLoader*)this; | |
996 | return (const Symbol*)sym; | |
997 | } | |
998 | return NULL; | |
999 | } | |
1000 | ||
1001 | ||
1002 | ||
1003 | bool ImageLoaderMachOClassic::containsSymbol(const void* addr) const | |
1004 | { | |
1005 | return ( (fSymbolTable <= addr) && (addr < fStrings) ); | |
1006 | } | |
1007 | ||
1008 | ||
412ebb8e | 1009 | uintptr_t ImageLoaderMachOClassic::exportedSymbolAddress(const LinkContext& context, const Symbol* symbol, bool runResolver) const |
39a8cd10 A |
1010 | { |
1011 | const struct macho_nlist* sym = (macho_nlist*)symbol; | |
1012 | uintptr_t result = sym->n_value + fSlide; | |
1013 | #if __arm__ | |
1014 | // processor assumes code address with low bit set is thumb | |
1015 | if (sym->n_desc & N_ARM_THUMB_DEF) | |
1016 | result |= 1; | |
1017 | #endif | |
1018 | return result; | |
1019 | } | |
1020 | ||
1021 | bool ImageLoaderMachOClassic::exportedSymbolIsWeakDefintion(const Symbol* symbol) const | |
1022 | { | |
1023 | const struct macho_nlist* nlistSym = (const struct macho_nlist*)symbol; | |
1024 | return ( (nlistSym->n_desc & N_WEAK_DEF) != 0 ); | |
1025 | } | |
1026 | ||
1027 | const char* ImageLoaderMachOClassic::exportedSymbolName(const Symbol* symbol) const | |
1028 | { | |
1029 | const struct macho_nlist* nlistSym = (const struct macho_nlist*)symbol; | |
1030 | return &fStrings[nlistSym->n_un.n_strx]; | |
1031 | } | |
1032 | ||
1033 | unsigned int ImageLoaderMachOClassic::exportedSymbolCount() const | |
1034 | { | |
1035 | return fDynamicInfo->nextdefsym; | |
1036 | } | |
1037 | ||
1038 | const ImageLoader::Symbol* ImageLoaderMachOClassic::exportedSymbolIndexed(unsigned int index) const | |
1039 | { | |
1040 | if ( index < fDynamicInfo->nextdefsym ) { | |
1041 | const struct macho_nlist* sym = &fSymbolTable[fDynamicInfo->iextdefsym + index]; | |
1042 | return (const ImageLoader::Symbol*)sym; | |
1043 | } | |
1044 | return NULL; | |
1045 | } | |
1046 | ||
1047 | unsigned int ImageLoaderMachOClassic::importedSymbolCount() const | |
1048 | { | |
1049 | return fDynamicInfo->nundefsym; | |
1050 | } | |
1051 | ||
1052 | const ImageLoader::Symbol* ImageLoaderMachOClassic::importedSymbolIndexed(unsigned int index) const | |
1053 | { | |
1054 | if ( index < fDynamicInfo->nundefsym ) { | |
1055 | const struct macho_nlist* sym = &fSymbolTable[fDynamicInfo->iundefsym + index]; | |
1056 | return (const ImageLoader::Symbol*)sym; | |
1057 | } | |
1058 | return NULL; | |
1059 | } | |
1060 | ||
1061 | const char* ImageLoaderMachOClassic::importedSymbolName(const Symbol* symbol) const | |
1062 | { | |
1063 | const struct macho_nlist* nlistSym = (const struct macho_nlist*)symbol; | |
1064 | return &fStrings[nlistSym->n_un.n_strx]; | |
1065 | } | |
1066 | ||
1067 | ||
1068 | ||
1069 | bool ImageLoaderMachOClassic::symbolIsWeakDefinition(const struct macho_nlist* symbol) | |
1070 | { | |
1071 | // if a define and weak ==> coalesced | |
1072 | if ( ((symbol->n_type & N_TYPE) == N_SECT) && ((symbol->n_desc & N_WEAK_DEF) != 0) ) | |
1073 | return true; | |
1074 | ||
1075 | // regular symbol | |
1076 | return false; | |
1077 | } | |
1078 | ||
1079 | bool ImageLoaderMachOClassic::symbolIsWeakReference(const struct macho_nlist* symbol) | |
1080 | { | |
1081 | // if an undefine and not referencing a weak symbol ==> coalesced | |
1082 | if ( ((symbol->n_type & N_TYPE) != N_SECT) && ((symbol->n_desc & N_REF_TO_WEAK) != 0) ) | |
1083 | return true; | |
1084 | ||
1085 | // regular symbol | |
1086 | return false; | |
1087 | } | |
1088 | ||
412ebb8e | 1089 | uintptr_t ImageLoaderMachOClassic::getSymbolAddress(const macho_nlist* sym, const LinkContext& context, bool runResolver) const |
39a8cd10 | 1090 | { |
412ebb8e | 1091 | return ImageLoaderMachO::getSymbolAddress((Symbol*)sym, this, context, runResolver); |
39a8cd10 A |
1092 | } |
1093 | ||
1094 | uintptr_t ImageLoaderMachOClassic::resolveUndefined(const LinkContext& context, const struct macho_nlist* undefinedSymbol, | |
1095 | bool twoLevel, bool dontCoalesce, const ImageLoader** foundIn) | |
1096 | { | |
1097 | ++fgTotalBindSymbolsResolved; | |
1098 | const char* symbolName = &fStrings[undefinedSymbol->n_un.n_strx]; | |
1099 | ||
1100 | #if LINKEDIT_USAGE_DEBUG | |
1101 | noteAccessedLinkEditAddress(undefinedSymbol); | |
1102 | noteAccessedLinkEditAddress(symbolName); | |
1103 | #endif | |
1104 | if ( context.bindFlat || !twoLevel ) { | |
1105 | // flat lookup | |
1106 | if ( ((undefinedSymbol->n_type & N_PEXT) != 0) && ((undefinedSymbol->n_type & N_TYPE) == N_SECT) ) { | |
1107 | // is a multi-module private_extern internal reference that the linker did not optimize away | |
412ebb8e | 1108 | uintptr_t addr = this->getSymbolAddress(undefinedSymbol, context, false); |
39a8cd10 A |
1109 | *foundIn = this; |
1110 | return addr; | |
1111 | } | |
1112 | const Symbol* sym; | |
1113 | if ( context.flatExportFinder(symbolName, &sym, foundIn) ) { | |
1114 | if ( (*foundIn != this) && !(*foundIn)->neverUnload() ) | |
1115 | this->addDynamicReference(*foundIn); | |
1116 | return (*foundIn)->getExportedSymbolAddress(sym, context, this); | |
1117 | } | |
1118 | // if a bundle is loaded privately the above will not find its exports | |
1119 | if ( this->isBundle() && this->hasHiddenExports() ) { | |
1120 | // look in self for needed symbol | |
1121 | sym = this->findExportedSymbol(symbolName, foundIn); | |
1122 | if ( sym != NULL ) | |
1123 | return (*foundIn)->getExportedSymbolAddress(sym, context, this); | |
1124 | } | |
1125 | if ( (undefinedSymbol->n_desc & N_WEAK_REF) != 0 ) { | |
1126 | // definition can't be found anywhere | |
1127 | // if reference is weak_import, then it is ok, just return 0 | |
1128 | return 0; | |
1129 | } | |
412ebb8e | 1130 | throwSymbolNotFound(context, symbolName, this->getPath(), "flat namespace"); |
39a8cd10 A |
1131 | } |
1132 | else { | |
1133 | // symbol requires searching images with coalesced symbols (not done during prebinding) | |
1134 | if ( !context.prebinding && !dontCoalesce && (symbolIsWeakReference(undefinedSymbol) || symbolIsWeakDefinition(undefinedSymbol)) ) { | |
1135 | const Symbol* sym; | |
1136 | if ( context.coalescedExportFinder(symbolName, &sym, foundIn) ) { | |
1137 | if ( (*foundIn != this) && !(*foundIn)->neverUnload() ) | |
1138 | this->addDynamicReference(*foundIn); | |
1139 | return (*foundIn)->getExportedSymbolAddress(sym, context, this); | |
1140 | } | |
412ebb8e | 1141 | //throwSymbolNotFound(context, symbolName, this->getPath(), "coalesced namespace"); |
39a8cd10 A |
1142 | //dyld::log("dyld: coalesced symbol %s not found in any coalesced image, falling back to two-level lookup", symbolName); |
1143 | } | |
1144 | ||
1145 | // if this is a real definition (not an undefined symbol) there is no ordinal | |
1146 | if ( (undefinedSymbol->n_type & N_TYPE) == N_SECT ) { | |
1147 | // static linker should never generate this case, but if it does, do something sane | |
412ebb8e | 1148 | uintptr_t addr = this->getSymbolAddress(undefinedSymbol, context, false); |
39a8cd10 A |
1149 | *foundIn = this; |
1150 | return addr; | |
1151 | } | |
1152 | ||
1153 | // two level lookup | |
1154 | ImageLoader* target = NULL; | |
1155 | uint8_t ord = GET_LIBRARY_ORDINAL(undefinedSymbol->n_desc); | |
1156 | if ( ord == EXECUTABLE_ORDINAL ) { | |
1157 | target = context.mainExecutable; | |
1158 | } | |
1159 | else if ( ord == SELF_LIBRARY_ORDINAL ) { | |
1160 | target = this; | |
1161 | } | |
1162 | else if ( ord == DYNAMIC_LOOKUP_ORDINAL ) { | |
1163 | // rnielsen: HACKHACK | |
1164 | // flat lookup | |
1165 | const Symbol* sym; | |
1166 | if ( context.flatExportFinder(symbolName, &sym, foundIn) ) | |
1167 | return (*foundIn)->getExportedSymbolAddress(sym, context, this); | |
1168 | // no image has exports this symbol | |
1169 | // report error | |
1170 | context.undefinedHandler(symbolName); | |
1171 | // try looking again | |
1172 | if ( context.flatExportFinder(symbolName, &sym, foundIn) ) | |
1173 | return (*foundIn)->getExportedSymbolAddress(sym, context, this); | |
1174 | ||
412ebb8e | 1175 | throwSymbolNotFound(context, symbolName, this->getPath(), "dynamic lookup"); |
39a8cd10 A |
1176 | } |
1177 | else if ( ord <= libraryCount() ) { | |
1178 | target = libImage(ord-1); | |
1179 | if ( target == NULL ) { | |
1180 | // if target library not loaded and reference is weak or library is weak return 0 | |
1181 | return 0; | |
1182 | } | |
1183 | } | |
1184 | else { | |
1185 | dyld::throwf("bad mach-o binary, library ordinal (%u) too big (max %u) for symbol %s in %s", | |
1186 | ord, libraryCount(), symbolName, this->getPath()); | |
1187 | } | |
1188 | ||
1189 | if ( target == NULL ) { | |
1190 | //dyld::log("resolveUndefined(%s) in %s\n", symbolName, this->getPath()); | |
1191 | throw "symbol not found"; | |
1192 | } | |
1193 | ||
1194 | const Symbol* sym = target->findExportedSymbol(symbolName, true, foundIn); | |
1195 | if ( sym!= NULL ) { | |
1196 | return (*foundIn)->getExportedSymbolAddress(sym, context, this); | |
1197 | } | |
1198 | else if ( (undefinedSymbol->n_type & N_PEXT) != 0 ) { | |
1199 | // don't know why the static linker did not eliminate the internal reference to a private extern definition | |
1200 | *foundIn = this; | |
412ebb8e | 1201 | return this->getSymbolAddress(undefinedSymbol, context, false); |
39a8cd10 A |
1202 | } |
1203 | else if ( (undefinedSymbol->n_desc & N_WEAK_REF) != 0 ) { | |
1204 | // if definition not found and reference is weak return 0 | |
1205 | return 0; | |
1206 | } | |
1207 | ||
1208 | // nowhere to be found | |
412ebb8e | 1209 | throwSymbolNotFound(context, symbolName, this->getPath(), target->getPath()); |
39a8cd10 A |
1210 | } |
1211 | } | |
1212 | ||
1213 | ||
1214 | ||
1215 | // returns if 'addr' is within the address range of section 'sectionIndex' | |
1216 | // fSlide is not used. 'addr' is assumed to be a prebound address in this image | |
1217 | bool ImageLoaderMachOClassic::isAddrInSection(uintptr_t addr, uint8_t sectionIndex) | |
1218 | { | |
1219 | uint8_t currentSectionIndex = 1; | |
1220 | const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; | |
1221 | const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; | |
1222 | const struct load_command* cmd = cmds; | |
1223 | for (uint32_t i = 0; i < cmd_count; ++i) { | |
1224 | if ( cmd->cmd == LC_SEGMENT_COMMAND ) { | |
1225 | const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; | |
1226 | if ( (currentSectionIndex <= sectionIndex) && (sectionIndex < currentSectionIndex+seg->nsects) ) { | |
1227 | // 'sectionIndex' is in this segment, get section info | |
1228 | const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command)); | |
1229 | const struct macho_section* const section = §ionsStart[sectionIndex-currentSectionIndex]; | |
1230 | return ( (section->addr <= addr) && (addr < section->addr+section->size) ); | |
1231 | } | |
1232 | else { | |
1233 | // 'sectionIndex' not in this segment, skip to next segment | |
1234 | currentSectionIndex += seg->nsects; | |
1235 | } | |
1236 | } | |
1237 | cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); | |
1238 | } | |
1239 | ||
1240 | return false; | |
1241 | } | |
1242 | ||
1243 | void ImageLoaderMachOClassic::doBindExternalRelocations(const LinkContext& context) | |
1244 | { | |
1245 | const uintptr_t relocBase = this->getRelocBase(); | |
1246 | const bool twoLevel = this->usesTwoLevelNameSpace(); | |
1247 | const bool prebound = this->isPrebindable(); | |
1248 | ||
1249 | #if TEXT_RELOC_SUPPORT | |
1250 | // if there are __TEXT fixups, temporarily make __TEXT writable | |
1251 | if ( fTextSegmentBinds ) | |
1252 | this->makeTextSegmentWritable(context, true); | |
1253 | #endif | |
1254 | // cache last lookup | |
1255 | const struct macho_nlist* lastUndefinedSymbol = NULL; | |
1256 | uintptr_t symbolAddr = 0; | |
1257 | const ImageLoader* image = NULL; | |
1258 | ||
1259 | // loop through all external relocation records and bind each | |
1260 | const relocation_info* const relocsStart = (struct relocation_info*)(&fLinkEditBase[fDynamicInfo->extreloff]); | |
1261 | const relocation_info* const relocsEnd = &relocsStart[fDynamicInfo->nextrel]; | |
1262 | for (const relocation_info* reloc=relocsStart; reloc < relocsEnd; ++reloc) { | |
1263 | if (reloc->r_length == RELOC_SIZE) { | |
1264 | switch(reloc->r_type) { | |
1265 | case POINTER_RELOC: | |
1266 | { | |
1267 | const struct macho_nlist* undefinedSymbol = &fSymbolTable[reloc->r_symbolnum]; | |
1268 | uintptr_t* location = ((uintptr_t*)(reloc->r_address + relocBase)); | |
1269 | uintptr_t value = *location; | |
1270 | bool symbolAddrCached = true; | |
1271 | #if __i386__ | |
1272 | if ( reloc->r_pcrel ) { | |
1273 | value += (uintptr_t)location + 4 - fSlide; | |
1274 | } | |
1275 | #endif | |
1276 | if ( prebound ) { | |
1277 | // we are doing relocations, so prebinding was not usable | |
1278 | // in a prebound executable, the n_value field of an undefined symbol is set to the address where the symbol was found when prebound | |
1279 | // so, subtracting that gives the initial displacement which we need to add to the newly found symbol address | |
1280 | // if mach-o relocation structs had an "addend" field this complication would not be necessary. | |
1281 | if ( ((undefinedSymbol->n_type & N_TYPE) == N_SECT) && ((undefinedSymbol->n_desc & N_WEAK_DEF) != 0) ) { | |
1282 | // weak symbols need special casing, since *location may have been prebound to a definition in another image. | |
1283 | // If *location is currently prebound to somewhere in the same section as the weak definition, we assume | |
1284 | // that we can subtract off the weak symbol address to get the addend. | |
1285 | // If prebound elsewhere, we've lost the addend and have to assume it is zero. | |
1286 | // The prebinding to elsewhere only happens with 10.4+ update_prebinding which only operates on a small set of Apple dylibs | |
1287 | if ( (value == undefinedSymbol->n_value) || this->isAddrInSection(value, undefinedSymbol->n_sect) ) { | |
1288 | value -= undefinedSymbol->n_value; | |
1289 | #if __arm__ | |
1290 | // if weak and thumb subtract off extra thumb bit | |
1291 | if ( (undefinedSymbol->n_desc & N_ARM_THUMB_DEF) != 0 ) | |
1292 | value -= 1; | |
1293 | #endif | |
1294 | } | |
1295 | else | |
1296 | value = 0; | |
1297 | } | |
1298 | #if __arm__ | |
1299 | else if ( ((undefinedSymbol->n_type & N_TYPE) == N_SECT) && ((undefinedSymbol->n_desc & N_ARM_THUMB_DEF) != 0) ) { | |
1300 | // it was prebound to a defined symbol for thumb code in the same linkage unit | |
1301 | // we need to subtract off one to get real addend | |
1302 | value -= (undefinedSymbol->n_value+1); | |
1303 | } | |
1304 | #endif | |
1305 | else { | |
1306 | // is undefined or non-weak symbol, so do subtraction to get addend | |
1307 | value -= undefinedSymbol->n_value; | |
1308 | } | |
1309 | } | |
1310 | // if undefinedSymbol is same as last time, then symbolAddr and image will resolve to the same too | |
1311 | if ( undefinedSymbol != lastUndefinedSymbol ) { | |
1312 | bool dontCoalesce = true; | |
1313 | if ( symbolIsWeakReference(undefinedSymbol) ) { | |
1314 | // when weakbind() is run on a classic mach-o encoding, it won't try | |
1315 | // to coalesce N_REF_TO_WEAK symbols because they are not in the sorted | |
1316 | // range of global symbols. To handle that case we do the coalesing now. | |
1317 | dontCoalesce = false; | |
1318 | } | |
1319 | symbolAddr = this->resolveUndefined(context, undefinedSymbol, twoLevel, dontCoalesce, &image); | |
1320 | lastUndefinedSymbol = undefinedSymbol; | |
1321 | symbolAddrCached = false; | |
1322 | } | |
1323 | if ( context.verboseBind ) { | |
1324 | const char *path = NULL; | |
1325 | if ( image != NULL ) { | |
1326 | path = image->getShortName(); | |
1327 | } | |
1328 | const char* cachedString = "(cached)"; | |
1329 | if ( !symbolAddrCached ) | |
1330 | cachedString = ""; | |
1331 | if ( value == 0 ) { | |
1332 | dyld::log("dyld: bind: %s:0x%08lX = %s:%s, *0x%08lX = 0x%08lX%s\n", | |
1333 | this->getShortName(), (uintptr_t)location, | |
1334 | path, &fStrings[undefinedSymbol->n_un.n_strx], (uintptr_t)location, symbolAddr, cachedString); | |
1335 | } | |
1336 | else { | |
1337 | dyld::log("dyld: bind: %s:0x%08lX = %s:%s, *0x%08lX = 0x%08lX%s + %ld\n", | |
1338 | this->getShortName(), (uintptr_t)location, | |
1339 | path, &fStrings[undefinedSymbol->n_un.n_strx], (uintptr_t)location, symbolAddr, cachedString, value); | |
1340 | } | |
1341 | } | |
1342 | value += symbolAddr; | |
1343 | #if __i386__ | |
1344 | if ( reloc->r_pcrel ) { | |
1345 | *location = value - ((uintptr_t)location + 4); | |
1346 | } | |
1347 | else { | |
1348 | // don't dirty page if prebound value was correct | |
1349 | if ( !prebound || (*location != value) ) | |
1350 | *location = value; | |
1351 | } | |
1352 | #else | |
1353 | // don't dirty page if prebound value was correct | |
1354 | if ( !prebound || (*location != value) ) | |
1355 | *location = value; | |
1356 | #endif | |
1357 | // update stats | |
1358 | ++fgTotalBindFixups; | |
1359 | } | |
1360 | break; | |
1361 | default: | |
1362 | throw "unknown external relocation type"; | |
1363 | } | |
1364 | } | |
1365 | else { | |
1366 | throw "bad external relocation length"; | |
1367 | } | |
1368 | } | |
1369 | ||
1370 | #if TEXT_RELOC_SUPPORT | |
1371 | // if there were __TEXT fixups, restore write protection | |
1372 | if ( fTextSegmentBinds ) { | |
1373 | this->makeTextSegmentWritable(context, true); | |
1374 | } | |
1375 | #endif | |
1376 | } | |
1377 | ||
1378 | ||
1379 | ||
1380 | uintptr_t ImageLoaderMachOClassic::bindIndirectSymbol(uintptr_t* ptrToBind, const struct macho_section* sect, const char* symbolName, uintptr_t targetAddr, const ImageLoader* targetImage, const LinkContext& context) | |
1381 | { | |
1382 | if ( context.verboseBind ) { | |
1383 | const char* path = NULL; | |
1384 | if ( targetImage != NULL ) | |
1385 | path = targetImage->getShortName(); | |
1386 | dyld::log("dyld: bind indirect sym: %s:%s$%s = %s:%s, *0x%08lx = 0x%08lx\n", | |
1387 | this->getShortName(), symbolName, (((sect->flags & SECTION_TYPE)==S_NON_LAZY_SYMBOL_POINTERS) ? "non_lazy_ptr" : "lazy_ptr"), | |
1388 | ((path != NULL) ? path : "<weak_import-not-found>"), symbolName, (uintptr_t)ptrToBind, targetAddr); | |
1389 | } | |
1390 | if ( context.bindingHandler != NULL ) { | |
1391 | const char* path = NULL; | |
1392 | if ( targetImage != NULL ) | |
1393 | path = targetImage->getShortName(); | |
1394 | targetAddr = (uintptr_t)context.bindingHandler(path, symbolName, (void *)targetAddr); | |
1395 | } | |
1396 | #if __i386__ | |
1397 | // i386 has special self-modifying stubs that change from "CALL rel32" to "JMP rel32" | |
1398 | if ( ((sect->flags & SECTION_TYPE) == S_SYMBOL_STUBS) && ((sect->flags & S_ATTR_SELF_MODIFYING_CODE) != 0) && (sect->reserved2 == 5) ) { | |
1399 | uint32_t rel32 = targetAddr - (((uint32_t)ptrToBind)+5); | |
1400 | // re-write instruction in a thread-safe manner | |
1401 | // use 8-byte compare-and-swap to alter 5-byte jump table entries | |
1402 | // loop is required in case the extra three bytes that cover the next entry are altered by another thread | |
1403 | bool done = false; | |
1404 | while ( !done ) { | |
1405 | volatile int64_t* jumpPtr = (int64_t*)ptrToBind; | |
1406 | int pad = 0; | |
1407 | // By default the three extra bytes swapped follow the 5-byte JMP. | |
1408 | // But, if the 5-byte jump is up against the end of the __IMPORT segment | |
1409 | // We don't want to access bytes off the end of the segment, so we shift | |
1410 | // the extra bytes to precede the 5-byte JMP. | |
1411 | if ( (((uint32_t)ptrToBind + 8) & 0x00000FFC) == 0x00000000 ) { | |
1412 | jumpPtr = (int64_t*)((uint32_t)ptrToBind - 3); | |
1413 | pad = 3; | |
1414 | } | |
1415 | int64_t oldEntry = *jumpPtr; | |
1416 | union { | |
1417 | int64_t int64; | |
1418 | uint8_t bytes[8]; | |
1419 | } newEntry; | |
1420 | newEntry.int64 = oldEntry; | |
1421 | newEntry.bytes[pad+0] = 0xE9; // JMP rel32 | |
1422 | newEntry.bytes[pad+1] = rel32 & 0xFF; | |
1423 | newEntry.bytes[pad+2] = (rel32 >> 8) & 0xFF; | |
1424 | newEntry.bytes[pad+3] = (rel32 >> 16) & 0xFF; | |
1425 | newEntry.bytes[pad+4] = (rel32 >> 24) & 0xFF; | |
1426 | done = OSAtomicCompareAndSwap64Barrier(oldEntry, newEntry.int64, (int64_t*)jumpPtr); | |
1427 | } | |
1428 | } | |
1429 | else | |
1430 | #endif | |
1431 | *ptrToBind = targetAddr; | |
1432 | return targetAddr; | |
1433 | } | |
1434 | ||
412ebb8e | 1435 | uintptr_t ImageLoaderMachOClassic::doBindFastLazySymbol(uint32_t lazyBindingInfoOffset, const LinkContext& context, void (*lock)(), void (*unlock)()) |
39a8cd10 A |
1436 | { |
1437 | throw "compressed LINKEDIT lazy binder called with classic LINKEDIT"; | |
1438 | } | |
1439 | ||
1440 | uintptr_t ImageLoaderMachOClassic::doBindLazySymbol(uintptr_t* lazyPointer, const LinkContext& context) | |
1441 | { | |
1442 | // scan for all lazy-pointer sections | |
1443 | const bool twoLevel = this->usesTwoLevelNameSpace(); | |
1444 | const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; | |
1445 | const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; | |
1446 | const struct load_command* cmd = cmds; | |
1447 | const uint32_t* const indirectTable = (uint32_t*)&fLinkEditBase[fDynamicInfo->indirectsymoff]; | |
1448 | for (uint32_t i = 0; i < cmd_count; ++i) { | |
1449 | switch (cmd->cmd) { | |
1450 | case LC_SEGMENT_COMMAND: | |
1451 | { | |
1452 | const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; | |
1453 | const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command)); | |
1454 | const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects]; | |
1455 | for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { | |
1456 | const uint8_t type = sect->flags & SECTION_TYPE; | |
1457 | uint32_t symbolIndex = INDIRECT_SYMBOL_LOCAL; | |
1458 | if ( type == S_LAZY_SYMBOL_POINTERS ) { | |
1459 | const uint32_t pointerCount = sect->size / sizeof(uintptr_t); | |
1460 | uintptr_t* const symbolPointers = (uintptr_t*)(sect->addr + fSlide); | |
1461 | if ( (lazyPointer >= symbolPointers) && (lazyPointer < &symbolPointers[pointerCount]) ) { | |
1462 | const uint32_t indirectTableOffset = sect->reserved1; | |
1463 | const uint32_t lazyIndex = lazyPointer - symbolPointers; | |
1464 | symbolIndex = indirectTable[indirectTableOffset + lazyIndex]; | |
1465 | } | |
1466 | } | |
1467 | #if __i386__ | |
1468 | else if ( (type == S_SYMBOL_STUBS) && (sect->flags & S_ATTR_SELF_MODIFYING_CODE) && (sect->reserved2 == 5) ) { | |
1469 | // 5 bytes stubs on i386 are new "fast stubs" | |
1470 | uint8_t* const jmpTableBase = (uint8_t*)(sect->addr + fSlide); | |
1471 | uint8_t* const jmpTableEnd = jmpTableBase + sect->size; | |
1472 | // initial CALL instruction in jump table leaves pointer to next entry, so back up | |
1473 | uint8_t* const jmpTableEntryToPatch = ((uint8_t*)lazyPointer) - 5; | |
1474 | lazyPointer = (uintptr_t*)jmpTableEntryToPatch; | |
1475 | if ( (jmpTableEntryToPatch >= jmpTableBase) && (jmpTableEntryToPatch < jmpTableEnd) ) { | |
1476 | const uint32_t indirectTableOffset = sect->reserved1; | |
1477 | const uint32_t entryIndex = (jmpTableEntryToPatch - jmpTableBase)/5; | |
1478 | symbolIndex = indirectTable[indirectTableOffset + entryIndex]; | |
1479 | } | |
1480 | } | |
1481 | #endif | |
1482 | if ( symbolIndex != INDIRECT_SYMBOL_ABS && symbolIndex != INDIRECT_SYMBOL_LOCAL ) { | |
1483 | const char* symbolName = &fStrings[fSymbolTable[symbolIndex].n_un.n_strx]; | |
1484 | const ImageLoader* image = NULL; | |
1485 | uintptr_t symbolAddr = this->resolveUndefined(context, &fSymbolTable[symbolIndex], twoLevel, false, &image); | |
1486 | symbolAddr = this->bindIndirectSymbol(lazyPointer, sect, symbolName, symbolAddr, image, context); | |
1487 | ++fgTotalLazyBindFixups; | |
1488 | return symbolAddr; | |
1489 | } | |
1490 | } | |
1491 | } | |
1492 | break; | |
1493 | } | |
1494 | cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); | |
1495 | } | |
1496 | dyld::throwf("lazy pointer not found at address %p in image %s", lazyPointer, this->getPath()); | |
1497 | } | |
1498 | ||
1499 | ||
1500 | ||
1501 | void ImageLoaderMachOClassic::initializeCoalIterator(CoalIterator& it, unsigned int loadOrder) | |
1502 | { | |
1503 | it.image = this; | |
1504 | it.symbolName = " "; | |
1505 | it.loadOrder = loadOrder; | |
1506 | it.weakSymbol = false; | |
1507 | it.symbolMatches = false; | |
1508 | it.done = false; | |
1509 | it.type = 0; | |
1510 | if ( fDynamicInfo->tocoff != 0 ) { | |
1511 | it.curIndex = 0; | |
1512 | it.endIndex = fDynamicInfo->ntoc; | |
1513 | } | |
1514 | else { | |
1515 | it.curIndex = 0; | |
1516 | it.endIndex = fDynamicInfo->nextdefsym; | |
1517 | } | |
1518 | } | |
1519 | ||
1520 | ||
1521 | bool ImageLoaderMachOClassic::incrementCoalIterator(CoalIterator& it) | |
1522 | { | |
1523 | if ( it.done ) | |
1524 | return false; | |
1525 | ||
1526 | if ( fDynamicInfo->tocoff != 0 ) { | |
1527 | if ( it.curIndex >= fDynamicInfo->ntoc ) { | |
1528 | it.done = true; | |
1529 | it.symbolName = "~~~"; | |
1530 | return true; | |
1531 | } | |
1532 | else { | |
1533 | const dylib_table_of_contents* toc = (dylib_table_of_contents*)&fLinkEditBase[fDynamicInfo->tocoff]; | |
1534 | const uint32_t index = toc[it.curIndex].symbol_index; | |
1535 | const struct macho_nlist* sym = &fSymbolTable[index]; | |
1536 | const char* symStr = &fStrings[sym->n_un.n_strx]; | |
1537 | it.symbolName = symStr; | |
1538 | it.weakSymbol = (sym->n_desc & N_WEAK_DEF); | |
1539 | it.symbolMatches = false; | |
1540 | it.type = 0; // clear flag that says we applied updates for this symbol | |
1541 | //dyld::log("incrementCoalIterator() curIndex=%ld, symbolName=%s in %s\n", it.curIndex, symStr, this->getPath()); | |
1542 | it.curIndex++; | |
1543 | return false; | |
1544 | } | |
1545 | } | |
1546 | else { | |
1547 | if ( it.curIndex >= fDynamicInfo->nextdefsym ) { | |
1548 | it.done = true; | |
1549 | it.symbolName = "~~~"; | |
1550 | return true; | |
1551 | } | |
1552 | else { | |
1553 | const struct macho_nlist* sym = &fSymbolTable[fDynamicInfo->iextdefsym+it.curIndex]; | |
1554 | const char* symStr = &fStrings[sym->n_un.n_strx]; | |
1555 | it.symbolName = symStr; | |
1556 | it.weakSymbol = (sym->n_desc & N_WEAK_DEF); | |
1557 | it.symbolMatches = false; | |
1558 | it.type = 0; // clear flag that says we applied updates for this symbol | |
1559 | //dyld::log("incrementCoalIterator() curIndex=%ld, symbolName=%s in %s\n", it.curIndex, symStr, this->getPath()); | |
1560 | it.curIndex++; | |
1561 | return false; | |
1562 | } | |
1563 | } | |
1564 | ||
1565 | return false; | |
1566 | } | |
1567 | ||
1568 | uintptr_t ImageLoaderMachOClassic::getAddressCoalIterator(CoalIterator& it, const LinkContext& context) | |
1569 | { | |
1570 | uint32_t symbol_index = 0; | |
1571 | if ( fDynamicInfo->tocoff != 0 ) { | |
1572 | const dylib_table_of_contents* toc = (dylib_table_of_contents*)&fLinkEditBase[fDynamicInfo->tocoff]; | |
1573 | symbol_index = toc[it.curIndex-1].symbol_index; | |
1574 | } | |
1575 | else { | |
1576 | symbol_index = fDynamicInfo->iextdefsym+it.curIndex-1; | |
1577 | } | |
1578 | const struct macho_nlist* sym = &fSymbolTable[symbol_index]; | |
1579 | //dyld::log("getAddressCoalIterator() => 0x%llX, %s symbol_index=%d, in %s\n", (uint64_t)(sym->n_value + fSlide), &fStrings[sym->n_un.n_strx], symbol_index, this->getPath()); | |
412ebb8e A |
1580 | #if __arm__ |
1581 | // processor assumes code address with low bit set is thumb | |
1582 | if (sym->n_desc & N_ARM_THUMB_DEF) | |
1583 | return (sym->n_value | 1) + fSlide ; | |
1584 | else | |
1585 | return sym->n_value + fSlide; | |
1586 | #else | |
39a8cd10 | 1587 | return sym->n_value + fSlide; |
412ebb8e | 1588 | #endif |
39a8cd10 A |
1589 | } |
1590 | ||
1591 | ||
1592 | void ImageLoaderMachOClassic::updateUsesCoalIterator(CoalIterator& it, uintptr_t value, ImageLoader* targetImage, const LinkContext& context) | |
1593 | { | |
1594 | // flat_namespace images with classic LINKEDIT do not need late coalescing. | |
1595 | // They still need to be iterated becuase they may implement | |
1596 | // something needed by other coalescing images. | |
1597 | // But they need no updating because during the bind phase every symbol lookup is a full scan. | |
1598 | if ( !this->usesTwoLevelNameSpace() ) | |
1599 | return; | |
1600 | ||
1601 | // <rdar://problem/6570879> weak binding done too early with inserted libraries | |
1602 | if ( this->getState() < dyld_image_state_bound ) | |
1603 | return; | |
1604 | ||
1605 | uint32_t symbol_index = 0; | |
1606 | if ( fDynamicInfo->tocoff != 0 ) { | |
1607 | const dylib_table_of_contents* toc = (dylib_table_of_contents*)&fLinkEditBase[fDynamicInfo->tocoff]; | |
1608 | symbol_index = toc[it.curIndex-1].symbol_index; | |
1609 | } | |
1610 | else { | |
1611 | symbol_index = fDynamicInfo->iextdefsym+it.curIndex-1; | |
1612 | } | |
1613 | ||
1614 | // if this image's copy of the symbol is not a weak definition nor a weak reference then nothing to coalesce here | |
1615 | if ( !symbolIsWeakReference(&fSymbolTable[symbol_index]) && !symbolIsWeakDefinition(&fSymbolTable[symbol_index]) ) { | |
1616 | return; | |
1617 | } | |
1618 | ||
1619 | // <rdar://problem/6555720> malformed dylib with duplicate weak symbols causes re-binding | |
1620 | if ( it.type ) | |
1621 | return; | |
1622 | ||
1623 | bool boundSomething = false; | |
1624 | // scan external relocations for uses of symbol_index | |
1625 | const uintptr_t relocBase = this->getRelocBase(); | |
1626 | const bool prebound = this->isPrebindable(); | |
1627 | const relocation_info* const relocsStart = (struct relocation_info*)(&fLinkEditBase[fDynamicInfo->extreloff]); | |
1628 | const relocation_info* const relocsEnd = &relocsStart[fDynamicInfo->nextrel]; | |
1629 | for (const relocation_info* reloc=relocsStart; reloc < relocsEnd; ++reloc) { | |
1630 | if ( reloc->r_symbolnum == symbol_index ) { | |
1631 | //dyld::log("found external reloc using symbol_index=%d in %s\n",symbol_index, this->getPath()); | |
1632 | const struct macho_nlist* undefinedSymbol = &fSymbolTable[reloc->r_symbolnum]; | |
1633 | const char* symbolName = &fStrings[undefinedSymbol->n_un.n_strx]; | |
1634 | uintptr_t* location = ((uintptr_t*)(reloc->r_address + relocBase)); | |
1635 | const uintptr_t initialValue = *location; | |
1636 | uintptr_t addend = 0; | |
1637 | if ( prebound ) { | |
1638 | // we are doing relocations, so prebinding was not usable | |
1639 | // in a prebound executable, the n_value field of an undefined symbol is set to the address where the symbol was found when prebound | |
1640 | // so, subtracting that gives the initial displacement which we need to add to the newly found symbol address | |
1641 | // if mach-o relocation structs had an "addend" field this complication would not be necessary. | |
1642 | if ( ((undefinedSymbol->n_type & N_TYPE) == N_SECT) && ((undefinedSymbol->n_desc & N_WEAK_DEF) != 0) ) { | |
1643 | // weak symbols need special casing, since *location may have been prebound to a definition in another image. | |
1644 | // If *location is currently prebound to somewhere in the same section as the weak definition, we assume | |
1645 | // that we can subtract off the weak symbol address to get the addend. | |
1646 | // If prebound elsewhere, we've lost the addend and have to assume it is zero. | |
1647 | // The prebinding to elsewhere only happens with 10.4+ update_prebinding which only operates on a small set of Apple dylibs | |
1648 | if ( (initialValue == undefinedSymbol->n_value) || this->isAddrInSection(initialValue, undefinedSymbol->n_sect) ) { | |
1649 | addend = initialValue - undefinedSymbol->n_value; | |
1650 | #if __arm__ | |
1651 | // if weak and thumb subtract off extra thumb bit | |
1652 | if ( (undefinedSymbol->n_desc & N_ARM_THUMB_DEF) != 0 ) | |
412ebb8e | 1653 | addend &= -2; |
39a8cd10 A |
1654 | #endif |
1655 | } | |
1656 | } | |
1657 | #if __arm__ | |
1658 | else if ( ((undefinedSymbol->n_type & N_TYPE) == N_SECT) && ((undefinedSymbol->n_desc & N_ARM_THUMB_DEF) != 0) ) { | |
1659 | // it was prebound to a defined symbol for thumb code in the same linkage unit | |
1660 | // we need to subtract off one to get real addend | |
1661 | addend = initialValue - (undefinedSymbol->n_value+1); | |
1662 | } | |
1663 | #endif | |
1664 | else { | |
1665 | // is undefined or non-weak symbol, so do subtraction to get addend | |
1666 | addend = initialValue - undefinedSymbol->n_value; | |
1667 | } | |
1668 | } | |
1669 | else { | |
1670 | // non-prebound case | |
1671 | if ( ((undefinedSymbol->n_type & N_TYPE) == N_SECT) && ((undefinedSymbol->n_desc & N_WEAK_DEF) != 0) ) { | |
1672 | // if target is weak-def in same linkage unit, then bind phase has already set initialValue | |
1673 | // to be definition address plus addend | |
1674 | //dyld::log("weak def, initialValue=0x%lX, undefAddr=0x%lX\n", initialValue, undefinedSymbol->n_value+fSlide); | |
1675 | addend = initialValue - (undefinedSymbol->n_value + fSlide); | |
412ebb8e A |
1676 | #if __arm__ |
1677 | // if weak and thumb subtract off extra thumb bit | |
1678 | if ( (undefinedSymbol->n_desc & N_ARM_THUMB_DEF) != 0 ) | |
1679 | addend &= -2; | |
1680 | #endif | |
39a8cd10 A |
1681 | } |
1682 | else { | |
1683 | // nothing fixed up yet, addend is just initial value | |
1684 | //dyld::log("addend=0x%lX\n", initialValue); | |
1685 | addend = initialValue; | |
1686 | } | |
1687 | } | |
1688 | ||
1689 | uint8_t type = BIND_TYPE_POINTER; | |
1690 | #if __i386__ | |
1691 | if ( reloc->r_pcrel ) | |
1692 | type = BIND_TYPE_TEXT_PCREL32; | |
1693 | #endif | |
1694 | this->bindLocation(context, (uintptr_t)location, value, targetImage, type, symbolName, addend, "weak "); | |
1695 | boundSomething = true; | |
1696 | } | |
1697 | } | |
1698 | ||
1699 | // scan lazy and non-lazy pointers for uses of symbol_index | |
1700 | const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; | |
1701 | const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; | |
1702 | const struct load_command* cmd = cmds; | |
1703 | const uint32_t* const indirectTable = (uint32_t*)&fLinkEditBase[fDynamicInfo->indirectsymoff]; | |
1704 | for (uint32_t i = 0; i < cmd_count; ++i) { | |
1705 | if ( cmd->cmd == LC_SEGMENT_COMMAND ) { | |
1706 | const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; | |
1707 | const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command)); | |
1708 | const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects]; | |
1709 | for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { | |
1710 | uint32_t elementSize = sizeof(uintptr_t); | |
1711 | switch ( sect->flags & SECTION_TYPE ) { | |
1712 | #if __i386__ | |
1713 | case S_SYMBOL_STUBS: | |
1714 | if ( ((sect->flags & S_ATTR_SELF_MODIFYING_CODE) ==0) || (sect->reserved2 != 5) ) | |
1715 | continue; | |
1716 | elementSize = 5; | |
1717 | #endif | |
1718 | case S_NON_LAZY_SYMBOL_POINTERS: | |
1719 | case S_LAZY_SYMBOL_POINTERS: | |
1720 | { | |
1721 | uint32_t elementCount = sect->size / elementSize; | |
1722 | const uint32_t indirectTableOffset = sect->reserved1; | |
1723 | uint8_t* ptrToBind = (uint8_t*)(sect->addr + fSlide); | |
1724 | //dyld::log(" scanning section %s of %s starting at %p\n", sect->sectname, this->getShortName(), ptrToBind); | |
1725 | for (uint32_t j=0; j < elementCount; ++j, ptrToBind += elementSize) { | |
1726 | if ( indirectTable[indirectTableOffset + j] == symbol_index ) { | |
1727 | //dyld::log(" found symbol index match at %d/%d, ptrToBind=%p\n", j, elementCount, ptrToBind); | |
1728 | // update pointer | |
1729 | this->bindIndirectSymbol((uintptr_t*)ptrToBind, sect, it.symbolName, value, targetImage, context); | |
1730 | boundSomething = true; | |
1731 | } | |
1732 | } | |
1733 | } | |
1734 | break; | |
1735 | } | |
1736 | } | |
1737 | } | |
1738 | cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); | |
1739 | } | |
1740 | if ( boundSomething && (targetImage != this) && !targetImage->neverUnload() ) | |
1741 | this->addDynamicReference(targetImage); | |
1742 | ||
1743 | // mark that this symbol has already been bound, so we don't try to bind again | |
1744 | it.type = 1; | |
1745 | } | |
1746 | ||
1747 | ||
1748 | void ImageLoaderMachOClassic::bindIndirectSymbolPointers(const LinkContext& context, bool bindNonLazys, bool bindLazys) | |
1749 | { | |
1750 | // scan for all non-lazy-pointer sections | |
1751 | const bool twoLevel = this->usesTwoLevelNameSpace(); | |
1752 | const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; | |
1753 | const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; | |
1754 | const struct load_command* cmd = cmds; | |
1755 | const uint32_t* const indirectTable = (uint32_t*)&fLinkEditBase[fDynamicInfo->indirectsymoff]; | |
1756 | for (uint32_t i = 0; i < cmd_count; ++i) { | |
1757 | switch (cmd->cmd) { | |
1758 | case LC_SEGMENT_COMMAND: | |
1759 | { | |
1760 | const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; | |
1761 | const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command)); | |
1762 | const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects]; | |
1763 | for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { | |
1764 | bool isLazySymbol = false; | |
1765 | const uint8_t type = sect->flags & SECTION_TYPE; | |
1766 | uint32_t elementSize = sizeof(uintptr_t); | |
1767 | uint32_t elementCount = sect->size / elementSize; | |
1768 | if ( type == S_NON_LAZY_SYMBOL_POINTERS ) { | |
1769 | if ( ! bindNonLazys ) | |
1770 | continue; | |
1771 | } | |
1772 | else if ( type == S_LAZY_SYMBOL_POINTERS ) { | |
1773 | // process each symbol pointer in this section | |
1774 | fgTotalPossibleLazyBindFixups += elementCount; | |
1775 | isLazySymbol = true; | |
1776 | if ( ! bindLazys ) | |
1777 | continue; | |
1778 | } | |
1779 | #if __i386__ | |
1780 | else if ( (type == S_SYMBOL_STUBS) && (sect->flags & S_ATTR_SELF_MODIFYING_CODE) && (sect->reserved2 == 5) ) { | |
1781 | // process each jmp entry in this section | |
1782 | elementCount = sect->size / 5; | |
1783 | elementSize = 5; | |
1784 | fgTotalPossibleLazyBindFixups += elementCount; | |
1785 | isLazySymbol = true; | |
1786 | if ( ! bindLazys ) | |
1787 | continue; | |
1788 | } | |
1789 | #endif | |
1790 | else { | |
1791 | continue; | |
1792 | } | |
1793 | const uint32_t indirectTableOffset = sect->reserved1; | |
1794 | uint8_t* ptrToBind = (uint8_t*)(sect->addr + fSlide); | |
1795 | for (uint32_t j=0; j < elementCount; ++j, ptrToBind += elementSize) { | |
1796 | #if LINKEDIT_USAGE_DEBUG | |
1797 | noteAccessedLinkEditAddress(&indirectTable[indirectTableOffset + j]); | |
1798 | #endif | |
1799 | uint32_t symbolIndex = indirectTable[indirectTableOffset + j]; | |
1800 | if ( symbolIndex == INDIRECT_SYMBOL_LOCAL) { | |
1801 | *((uintptr_t*)ptrToBind) += this->fSlide; | |
1802 | } | |
1803 | else if ( symbolIndex == INDIRECT_SYMBOL_ABS) { | |
1804 | // do nothing since already has absolute address | |
1805 | } | |
1806 | else { | |
1807 | const struct macho_nlist* sym = &fSymbolTable[symbolIndex]; | |
1808 | if ( symbolIndex == 0 ) { | |
1809 | // This could be rdar://problem/3534709 | |
1810 | if ( ((const macho_header*)fMachOData)->filetype == MH_EXECUTE ) { | |
1811 | static bool alreadyWarned = false; | |
1812 | if ( (sym->n_type & N_TYPE) != N_UNDF ) { | |
1813 | // The indirect table parallels the (non)lazy pointer sections. For | |
1814 | // instance, to find info about the fifth lazy pointer you look at the | |
1815 | // fifth entry in the indirect table. (try otool -Iv on a file). | |
1816 | // The entry in the indirect table contains an index into the symbol table. | |
1817 | ||
1818 | // The bug in ld caused the entry in the indirect table to be zero | |
1819 | // (instead of a magic value that means a local symbol). So, if the | |
1820 | // symbolIndex == 0, we may be encountering the bug, or 0 may be a valid | |
1821 | // symbol table index. The check I put in place is to see if the zero'th | |
1822 | // symbol table entry is an import entry (usually it is a local symbol | |
1823 | // definition). | |
1824 | if ( context.verboseWarnings && !alreadyWarned ) { | |
1825 | dyld::log("dyld: malformed executable '%s', skipping indirect symbol to %s\n", | |
1826 | this->getPath(), &fStrings[sym->n_un.n_strx]); | |
1827 | alreadyWarned = true; | |
1828 | } | |
1829 | continue; | |
1830 | } | |
1831 | } | |
1832 | } | |
1833 | const ImageLoader* image = NULL; | |
1834 | // let weak definitions resolve to themselves, later coalescing may overwrite them | |
1835 | bool dontCoalesce = true; | |
1836 | if ( bindLazys && isLazySymbol ) { | |
1837 | // if this is something normally lazy bound, but we are forcing | |
1838 | // it to be bound now, do coalescing | |
1839 | dontCoalesce = false; | |
1840 | } | |
1841 | if ( symbolIsWeakReference(sym) ) { | |
1842 | // when weakbind() is run on a classic mach-o encoding, it won't try | |
1843 | // to coalesce N_REF_TO_WEAK symbols because they are not in the sorted | |
1844 | // range of global symbols. To handle that case we do the coalesing now. | |
1845 | dontCoalesce = false; | |
1846 | } | |
1847 | uintptr_t symbolAddr = resolveUndefined(context, sym, twoLevel, dontCoalesce, &image); | |
1848 | // update pointer | |
1849 | symbolAddr = this->bindIndirectSymbol((uintptr_t*)ptrToBind, sect, &fStrings[sym->n_un.n_strx], symbolAddr, image, context); | |
1850 | // update stats | |
1851 | ++fgTotalBindFixups; | |
1852 | } | |
1853 | } | |
1854 | } | |
1855 | } | |
1856 | break; | |
1857 | } | |
1858 | cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); | |
1859 | } | |
1860 | } | |
1861 | ||
1862 | ||
1863 | ||
1864 | #if __i386__ | |
1865 | void ImageLoaderMachOClassic::initializeLazyStubs(const LinkContext& context) | |
1866 | { | |
1867 | if ( ! this->usablePrebinding(context) ) { | |
1868 | // reset all "fast" stubs | |
1869 | const macho_header* mh = (macho_header*)fMachOData; | |
1870 | const uint32_t cmd_count = mh->ncmds; | |
1871 | const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; | |
1872 | const struct load_command* cmd = cmds; | |
1873 | for (uint32_t i = 0; i < cmd_count; ++i) { | |
1874 | switch (cmd->cmd) { | |
1875 | case LC_SEGMENT_COMMAND: | |
1876 | { | |
1877 | const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; | |
1878 | const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command)); | |
1879 | const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects]; | |
1880 | for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { | |
1881 | const uint8_t type = sect->flags & SECTION_TYPE; | |
1882 | if ( (type == S_SYMBOL_STUBS) && (sect->flags & S_ATTR_SELF_MODIFYING_CODE) && (sect->reserved2 == 5) ) { | |
1883 | // reset each jmp entry in this section | |
1884 | const uint32_t indirectTableOffset = sect->reserved1; | |
1885 | const uint32_t* const indirectTable = (uint32_t*)&fLinkEditBase[fDynamicInfo->indirectsymoff]; | |
1886 | uint8_t* start = (uint8_t*)(sect->addr + this->fSlide); | |
1887 | uint8_t* end = start + sect->size; | |
1888 | uintptr_t dyldHandler = (uintptr_t)&fast_stub_binding_helper_interface; | |
1889 | uint32_t entryIndex = 0; | |
1890 | for (uint8_t* entry = start; entry < end; entry += 5, ++entryIndex) { | |
1891 | bool installLazyHandler = true; | |
1892 | // jump table entries that cross a (64-byte) cache line boundary have the potential to cause crashes | |
1893 | // if the instruction is updated by one thread while being executed by another | |
1894 | if ( ((uint32_t)entry & 0xFFFFFFC0) != ((uint32_t)entry+4 & 0xFFFFFFC0) ) { | |
1895 | // need to bind this now to avoid a potential problem if bound lazily | |
1896 | uint32_t symbolIndex = indirectTable[indirectTableOffset + entryIndex]; | |
1897 | // the latest linker marks 64-byte crossing stubs with INDIRECT_SYMBOL_ABS so they are not used | |
1898 | if ( symbolIndex != INDIRECT_SYMBOL_ABS ) { | |
1899 | const char* symbolName = &fStrings[fSymbolTable[symbolIndex].n_un.n_strx]; | |
1900 | const ImageLoader* image = NULL; | |
1901 | try { | |
1902 | uintptr_t symbolAddr = this->resolveUndefined(context, &fSymbolTable[symbolIndex], this->usesTwoLevelNameSpace(), false, &image); | |
1903 | symbolAddr = this->bindIndirectSymbol((uintptr_t*)entry, sect, symbolName, symbolAddr, image, context); | |
1904 | ++fgTotalBindFixups; | |
1905 | uint32_t rel32 = symbolAddr - (((uint32_t)entry)+5); | |
1906 | entry[0] = 0xE9; // JMP rel32 | |
1907 | entry[1] = rel32 & 0xFF; | |
1908 | entry[2] = (rel32 >> 8) & 0xFF; | |
1909 | entry[3] = (rel32 >> 16) & 0xFF; | |
1910 | entry[4] = (rel32 >> 24) & 0xFF; | |
1911 | installLazyHandler = false; | |
1912 | } | |
1913 | catch (const char* msg) { | |
1914 | // ignore errors when binding symbols early | |
1915 | // maybe the function is never called, and therefore erroring out now would be a regression | |
1916 | } | |
1917 | } | |
1918 | } | |
1919 | if ( installLazyHandler ) { | |
1920 | uint32_t rel32 = dyldHandler - (((uint32_t)entry)+5); | |
1921 | entry[0] = 0xE8; // CALL rel32 | |
1922 | entry[1] = rel32 & 0xFF; | |
1923 | entry[2] = (rel32 >> 8) & 0xFF; | |
1924 | entry[3] = (rel32 >> 16) & 0xFF; | |
1925 | entry[4] = (rel32 >> 24) & 0xFF; | |
1926 | } | |
1927 | } | |
1928 | } | |
1929 | } | |
1930 | } | |
1931 | } | |
1932 | cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); | |
1933 | } | |
1934 | } | |
1935 | } | |
1936 | #endif // __i386__ | |
1937 | ||
1938 | ||
1939 | void ImageLoaderMachOClassic::doBind(const LinkContext& context, bool forceLazysBound) | |
1940 | { | |
412ebb8e | 1941 | CRSetCrashLogMessage2(this->getPath()); |
39a8cd10 A |
1942 | #if __i386__ |
1943 | this->initializeLazyStubs(context); | |
1944 | #endif | |
1945 | ||
1946 | // if prebound and loaded at prebound address, and all libraries are same as when this was prebound, then no need to bind | |
1947 | // note: flat-namespace binaries need to have imports rebound (even if correctly prebound) | |
1948 | if ( this->usablePrebinding(context) ) { | |
1949 | // binding already up to date | |
1950 | } | |
1951 | else { | |
1952 | // no valid prebinding, so bind symbols. | |
1953 | // values bound by name are stored two different ways in classic mach-o: | |
1954 | ||
412ebb8e A |
1955 | #if TEXT_RELOC_SUPPORT |
1956 | // if there are __TEXT fixups, temporarily make __TEXT writable | |
1957 | if ( fTextSegmentBinds ) | |
1958 | this->makeTextSegmentWritable(context, true); | |
1959 | #endif | |
1960 | ||
39a8cd10 A |
1961 | // 1) external relocations are used for data initialized to external symbols |
1962 | this->doBindExternalRelocations(context); | |
1963 | ||
1964 | // 2) "indirect symbols" are used for code references to external symbols | |
1965 | // if this image is in the shared cache, there is no way to reset the lazy pointers, so bind them now | |
1966 | this->bindIndirectSymbolPointers(context, true, forceLazysBound || fInSharedCache); | |
1967 | ||
412ebb8e A |
1968 | #if TEXT_RELOC_SUPPORT |
1969 | // if there were __TEXT fixups, restore write protection | |
1970 | if ( fTextSegmentBinds ) | |
1971 | this->makeTextSegmentWritable(context, false); | |
1972 | #endif | |
39a8cd10 A |
1973 | } |
1974 | ||
1975 | // set up dyld entry points in image | |
1976 | this->setupLazyPointerHandler(context); | |
412ebb8e A |
1977 | |
1978 | CRSetCrashLogMessage2(NULL); | |
39a8cd10 A |
1979 | } |
1980 | ||
1981 | void ImageLoaderMachOClassic::doBindJustLazies(const LinkContext& context) | |
1982 | { | |
1983 | // some API called requested that all lazy pointers in this image be force bound | |
1984 | this->bindIndirectSymbolPointers(context, false, true); | |
1985 | } | |
1986 | ||
412ebb8e A |
1987 | void ImageLoaderMachOClassic::doInterpose(const LinkContext& context) |
1988 | { | |
1989 | if ( context.verboseInterposing ) | |
1990 | dyld::log("dyld: interposing %lu tuples onto: %s\n", fgInterposingTuples.size(), this->getPath()); | |
1991 | ||
1992 | // scan indirect symbols | |
1993 | const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; | |
1994 | const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; | |
1995 | const struct load_command* cmd = cmds; | |
1996 | for (uint32_t i = 0; i < cmd_count; ++i) { | |
1997 | switch (cmd->cmd) { | |
1998 | case LC_SEGMENT_COMMAND: | |
1999 | { | |
2000 | const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; | |
2001 | const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command)); | |
2002 | const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects]; | |
2003 | for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { | |
2004 | const uint8_t type = sect->flags & SECTION_TYPE; | |
2005 | if ( (type == S_NON_LAZY_SYMBOL_POINTERS) || (type == S_LAZY_SYMBOL_POINTERS) ) { | |
2006 | const uint32_t pointerCount = sect->size / sizeof(uintptr_t); | |
2007 | uintptr_t* const symbolPointers = (uintptr_t*)(sect->addr + fSlide); | |
2008 | for (uint32_t pointerIndex=0; pointerIndex < pointerCount; ++pointerIndex) { | |
2009 | for (std::vector<InterposeTuple>::iterator it=fgInterposingTuples.begin(); it != fgInterposingTuples.end(); it++) { | |
2010 | // replace all references to 'replacee' with 'replacement' | |
2011 | if ( (symbolPointers[pointerIndex] == it->replacee) && (this != it->replacementImage) ) { | |
2012 | if ( context.verboseInterposing ) { | |
2013 | dyld::log("dyld: interposing: at %p replace 0x%lX with 0x%lX in %s\n", | |
2014 | &symbolPointers[pointerIndex], it->replacee, it->replacement, this->getPath()); | |
2015 | } | |
2016 | symbolPointers[pointerIndex] = it->replacement; | |
2017 | } | |
2018 | } | |
2019 | } | |
2020 | } | |
2021 | #if __i386__ | |
2022 | // i386 has special self-modifying stubs that might be prebound to "JMP rel32" that need checking | |
2023 | else if ( (type == S_SYMBOL_STUBS) && ((sect->flags & S_ATTR_SELF_MODIFYING_CODE) != 0) && (sect->reserved2 == 5) ) { | |
2024 | // check each jmp entry in this section | |
2025 | uint8_t* start = (uint8_t*)(sect->addr + this->fSlide); | |
2026 | uint8_t* end = start + sect->size; | |
2027 | for (uint8_t* entry = start; entry < end; entry += 5) { | |
2028 | if ( entry[0] == 0xE9 ) { // 0xE9 == JMP | |
2029 | uint32_t rel32 = *((uint32_t*)&entry[1]); // assume unaligned load of uint32_t is ok | |
2030 | uint32_t target = (uint32_t)&entry[5] + rel32; | |
2031 | for (std::vector<InterposeTuple>::iterator it=fgInterposingTuples.begin(); it != fgInterposingTuples.end(); it++) { | |
2032 | // replace all references to 'replacee' with 'replacement' | |
2033 | if ( (it->replacee == target) && (this != it->replacementImage) ) { | |
2034 | if ( context.verboseInterposing ) { | |
2035 | dyld::log("dyld: interposing: at %p replace JMP 0x%lX with JMP 0x%lX in %s\n", | |
2036 | &entry[1], it->replacee, it->replacement, this->getPath()); | |
2037 | } | |
2038 | uint32_t newRel32 = it->replacement - (uint32_t)&entry[5]; | |
2039 | *((uint32_t*)&entry[1]) = newRel32; // assume unaligned store of uint32_t is ok | |
2040 | } | |
2041 | } | |
2042 | } | |
2043 | } | |
2044 | } | |
2045 | #endif | |
2046 | } | |
2047 | } | |
2048 | break; | |
2049 | } | |
2050 | cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); | |
2051 | } | |
2052 | ||
2053 | // scan external relocations | |
2054 | const uintptr_t relocBase = this->getRelocBase(); | |
2055 | const relocation_info* const relocsStart = (struct relocation_info*)(&fLinkEditBase[fDynamicInfo->extreloff]); | |
2056 | const relocation_info* const relocsEnd = &relocsStart[fDynamicInfo->nextrel]; | |
2057 | for (const relocation_info* reloc=relocsStart; reloc < relocsEnd; ++reloc) { | |
2058 | if (reloc->r_length == RELOC_SIZE) { | |
2059 | switch(reloc->r_type) { | |
2060 | case POINTER_RELOC: | |
2061 | { | |
2062 | uintptr_t* location = ((uintptr_t*)(reloc->r_address + relocBase)); | |
2063 | for (std::vector<InterposeTuple>::iterator it=fgInterposingTuples.begin(); it != fgInterposingTuples.end(); it++) { | |
2064 | // replace all references to 'replacee' with 'replacement' | |
2065 | if ( (*location == it->replacee) && (this != it->replacementImage) ) { | |
2066 | if ( context.verboseInterposing ) { | |
2067 | dyld::log("dyld: interposing: at %p replace 0x%lX with 0x%lX in %s\n", | |
2068 | location, it->replacee, it->replacement, this->getPath()); | |
2069 | } | |
2070 | *location = it->replacement; | |
2071 | } | |
2072 | } | |
2073 | } | |
2074 | break; | |
2075 | } | |
2076 | } | |
2077 | } | |
2078 | } | |
2079 | ||
2080 | ||
39a8cd10 A |
2081 | const char* ImageLoaderMachOClassic::findClosestSymbol(const void* addr, const void** closestAddr) const |
2082 | { | |
2083 | uintptr_t targetAddress = (uintptr_t)addr - fSlide; | |
2084 | const struct macho_nlist* bestSymbol = NULL; | |
2085 | // first walk all global symbols | |
2086 | const struct macho_nlist* const globalsStart = &fSymbolTable[fDynamicInfo->iextdefsym]; | |
2087 | const struct macho_nlist* const globalsEnd= &globalsStart[fDynamicInfo->nextdefsym]; | |
2088 | for (const struct macho_nlist* s = globalsStart; s < globalsEnd; ++s) { | |
2089 | if ( (s->n_type & N_TYPE) == N_SECT ) { | |
2090 | if ( bestSymbol == NULL ) { | |
2091 | if ( s->n_value <= targetAddress ) | |
2092 | bestSymbol = s; | |
2093 | } | |
2094 | else if ( (s->n_value <= targetAddress) && (bestSymbol->n_value < s->n_value) ) { | |
2095 | bestSymbol = s; | |
2096 | } | |
2097 | } | |
2098 | } | |
2099 | // next walk all local symbols | |
2100 | const struct macho_nlist* const localsStart = &fSymbolTable[fDynamicInfo->ilocalsym]; | |
2101 | const struct macho_nlist* const localsEnd= &localsStart[fDynamicInfo->nlocalsym]; | |
2102 | for (const struct macho_nlist* s = localsStart; s < localsEnd; ++s) { | |
2103 | if ( ((s->n_type & N_TYPE) == N_SECT) && ((s->n_type & N_STAB) == 0) ) { | |
2104 | if ( bestSymbol == NULL ) { | |
2105 | if ( s->n_value <= targetAddress ) | |
2106 | bestSymbol = s; | |
2107 | } | |
2108 | else if ( (s->n_value <= targetAddress) && (bestSymbol->n_value < s->n_value) ) { | |
2109 | bestSymbol = s; | |
2110 | } | |
2111 | } | |
2112 | } | |
2113 | if ( bestSymbol != NULL ) { | |
412ebb8e A |
2114 | #if __arm__ |
2115 | if (bestSymbol->n_desc & N_ARM_THUMB_DEF) | |
2116 | *closestAddr = (void*)((bestSymbol->n_value | 1) + fSlide); | |
2117 | else | |
2118 | *closestAddr = (void*)(bestSymbol->n_value + fSlide); | |
2119 | #else | |
39a8cd10 | 2120 | *closestAddr = (void*)(bestSymbol->n_value + fSlide); |
412ebb8e | 2121 | #endif |
39a8cd10 A |
2122 | return &fStrings[bestSymbol->n_un.n_strx]; |
2123 | } | |
2124 | return NULL; | |
2125 | } | |
2126 | ||
2127 |