]>
Commit | Line | Data |
---|---|---|
39a8cd10 A |
1 | /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- |
2 | * | |
3 | * Copyright (c) 2008 Apple Inc. All rights reserved. | |
4 | * | |
5 | * @APPLE_LICENSE_HEADER_START@ | |
6 | * | |
7 | * This file contains Original Code and/or Modifications of Original Code | |
8 | * as defined in and that are subject to the Apple Public Source License | |
9 | * Version 2.0 (the 'License'). You may not use this file except in | |
10 | * compliance with the License. Please obtain a copy of the License at | |
11 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
12 | * file. | |
13 | * | |
14 | * The Original Code and all software distributed under the License are | |
15 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
16 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
17 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
18 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
19 | * Please see the License for the specific language governing rights and | |
20 | * limitations under the License. | |
21 | * | |
22 | * @APPLE_LICENSE_HEADER_END@ | |
23 | */ | |
24 | ||
25 | ||
26 | #include <string.h> | |
27 | #include <fcntl.h> | |
28 | #include <errno.h> | |
29 | #include <sys/types.h> | |
30 | #include <sys/fcntl.h> | |
31 | #include <sys/stat.h> | |
32 | #include <sys/mman.h> | |
33 | #include <sys/param.h> | |
34 | #include <mach/mach.h> | |
35 | #include <mach/thread_status.h> | |
36 | #include <mach-o/loader.h> | |
37 | ||
38 | #include "ImageLoaderMachOCompressed.h" | |
39 | #include "mach-o/dyld_images.h" | |
40 | ||
2fd3f4e8 A |
41 | #ifndef EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE |
42 | #define EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE 0x02 | |
43 | #endif | |
39a8cd10 A |
44 | |
45 | // relocation_info.r_length field has value 3 for 64-bit executables and value 2 for 32-bit executables | |
46 | #if __LP64__ | |
47 | #define RELOC_SIZE 3 | |
48 | #define LC_SEGMENT_COMMAND LC_SEGMENT_64 | |
49 | #define LC_ROUTINES_COMMAND LC_ROUTINES_64 | |
50 | struct macho_segment_command : public segment_command_64 {}; | |
51 | struct macho_section : public section_64 {}; | |
52 | struct macho_routines_command : public routines_command_64 {}; | |
53 | #else | |
54 | #define RELOC_SIZE 2 | |
55 | #define LC_SEGMENT_COMMAND LC_SEGMENT | |
56 | #define LC_ROUTINES_COMMAND LC_ROUTINES | |
57 | struct macho_segment_command : public segment_command {}; | |
58 | struct macho_section : public section {}; | |
59 | struct macho_routines_command : public routines_command {}; | |
60 | #endif | |
61 | ||
62 | ||
63 | static uintptr_t read_uleb128(const uint8_t*& p, const uint8_t* end) | |
64 | { | |
65 | uint64_t result = 0; | |
66 | int bit = 0; | |
67 | do { | |
68 | if (p == end) | |
69 | dyld::throwf("malformed uleb128"); | |
70 | ||
71 | uint64_t slice = *p & 0x7f; | |
72 | ||
412ebb8e A |
73 | if (bit > 63) |
74 | dyld::throwf("uleb128 too big for uint64, bit=%d, result=0x%0llX", bit, result); | |
39a8cd10 A |
75 | else { |
76 | result |= (slice << bit); | |
77 | bit += 7; | |
78 | } | |
412ebb8e | 79 | } while (*p++ & 0x80); |
39a8cd10 A |
80 | return result; |
81 | } | |
82 | ||
83 | ||
84 | static intptr_t read_sleb128(const uint8_t*& p, const uint8_t* end) | |
85 | { | |
86 | int64_t result = 0; | |
87 | int bit = 0; | |
88 | uint8_t byte; | |
89 | do { | |
90 | if (p == end) | |
91 | throw "malformed sleb128"; | |
92 | byte = *p++; | |
832b6fce | 93 | result |= (((int64_t)(byte & 0x7f)) << bit); |
39a8cd10 A |
94 | bit += 7; |
95 | } while (byte & 0x80); | |
96 | // sign extend negative numbers | |
97 | if ( (byte & 0x40) != 0 ) | |
98 | result |= (-1LL) << bit; | |
99 | return result; | |
100 | } | |
101 | ||
102 | ||
103 | // create image for main executable | |
104 | ImageLoaderMachOCompressed* ImageLoaderMachOCompressed::instantiateMainExecutable(const macho_header* mh, uintptr_t slide, const char* path, | |
105 | unsigned int segCount, unsigned int libCount, const LinkContext& context) | |
106 | { | |
107 | ImageLoaderMachOCompressed* image = ImageLoaderMachOCompressed::instantiateStart(mh, path, segCount, libCount); | |
108 | ||
109 | // set slide for PIE programs | |
110 | image->setSlide(slide); | |
111 | ||
112 | // for PIE record end of program, to know where to start loading dylibs | |
412ebb8e | 113 | if ( slide != 0 ) |
39a8cd10 A |
114 | fgNextPIEDylibAddress = (uintptr_t)image->getEnd(); |
115 | ||
39a8cd10 | 116 | image->instantiateFinish(context); |
412ebb8e | 117 | image->setMapped(context); |
39a8cd10 A |
118 | |
119 | if ( context.verboseMapping ) { | |
120 | dyld::log("dyld: Main executable mapped %s\n", path); | |
121 | for(unsigned int i=0, e=image->segmentCount(); i < e; ++i) { | |
122 | const char* name = image->segName(i); | |
123 | if ( (strcmp(name, "__PAGEZERO") == 0) || (strcmp(name, "__UNIXSTACK") == 0) ) | |
124 | dyld::log("%18s at 0x%08lX->0x%08lX\n", name, image->segPreferredLoadAddress(i), image->segPreferredLoadAddress(i)+image->segSize(i)); | |
125 | else | |
126 | dyld::log("%18s at 0x%08lX->0x%08lX\n", name, image->segActualLoadAddress(i), image->segActualEndAddress(i)); | |
127 | } | |
128 | } | |
129 | ||
130 | return image; | |
131 | } | |
132 | ||
133 | // create image by mapping in a mach-o file | |
134 | ImageLoaderMachOCompressed* ImageLoaderMachOCompressed::instantiateFromFile(const char* path, int fd, const uint8_t* fileData, | |
135 | uint64_t offsetInFat, uint64_t lenInFat, const struct stat& info, | |
412ebb8e A |
136 | unsigned int segCount, unsigned int libCount, |
137 | const struct linkedit_data_command* codeSigCmd, const LinkContext& context) | |
39a8cd10 A |
138 | { |
139 | ImageLoaderMachOCompressed* image = ImageLoaderMachOCompressed::instantiateStart((macho_header*)fileData, path, segCount, libCount); | |
140 | ||
141 | try { | |
142 | // record info about file | |
143 | image->setFileInfo(info.st_dev, info.st_ino, info.st_mtime); | |
144 | ||
412ebb8e | 145 | // if this image is code signed, let kernel validate signature before mapping any pages from image |
2fd3f4e8 | 146 | image->loadCodeSignature(codeSigCmd, fd, offsetInFat, context); |
412ebb8e | 147 | |
39a8cd10 A |
148 | // mmap segments |
149 | image->mapSegments(fd, offsetInFat, lenInFat, info.st_size, context); | |
150 | ||
2fd3f4e8 A |
151 | // probe to see if code signed correctly |
152 | image->crashIfInvalidCodeSignature(); | |
153 | ||
39a8cd10 A |
154 | // finish construction |
155 | image->instantiateFinish(context); | |
156 | ||
157 | // if path happens to be same as in LC_DYLIB_ID load command use that, otherwise malloc a copy of the path | |
158 | const char* installName = image->getInstallPath(); | |
159 | if ( (installName != NULL) && (strcmp(installName, path) == 0) && (path[0] == '/') ) | |
160 | image->setPathUnowned(installName); | |
161 | #if __MAC_OS_X_VERSION_MIN_REQUIRED | |
162 | // <rdar://problem/6563887> app crashes when libSystem cannot be found | |
163 | else if ( (installName != NULL) && (strcmp(path, "/usr/lib/libgcc_s.1.dylib") == 0) && (strcmp(installName, "/usr/lib/libSystem.B.dylib") == 0) ) | |
164 | image->setPathUnowned("/usr/lib/libSystem.B.dylib"); | |
165 | #endif | |
832b6fce A |
166 | else if ( (path[0] != '/') || (strstr(path, "../") != NULL) ) { |
167 | // rdar://problem/10733082 Fix up @rpath based paths during introspection | |
39a8cd10 A |
168 | // rdar://problem/5135363 turn relative paths into absolute paths so gdb, Symbolication can later find them |
169 | char realPath[MAXPATHLEN]; | |
832b6fce A |
170 | if ( fcntl(fd, F_GETPATH, realPath) == 0 ) |
171 | image->setPaths(path, realPath); | |
39a8cd10 A |
172 | else |
173 | image->setPath(path); | |
174 | } | |
175 | else | |
176 | image->setPath(path); | |
177 | ||
412ebb8e A |
178 | // make sure path is stable before recording in dyld_all_image_infos |
179 | image->setMapped(context); | |
180 | ||
39a8cd10 A |
181 | // pre-fetch content of __DATA and __LINKEDIT segment for faster launches |
182 | // don't do this on prebound images or if prefetching is disabled | |
183 | if ( !context.preFetchDisabled && !image->isPrebindable()) { | |
184 | image->preFetchDATA(fd, offsetInFat, context); | |
185 | image->markSequentialLINKEDIT(context); | |
186 | } | |
187 | } | |
188 | catch (...) { | |
189 | // ImageLoader::setMapped() can throw an exception to block loading of image | |
190 | // <rdar://problem/6169686> Leaked fSegmentsArray and image segments during failed dlopen_preflight | |
191 | delete image; | |
192 | throw; | |
193 | } | |
194 | ||
195 | return image; | |
196 | } | |
197 | ||
198 | // create image by using cached mach-o file | |
412ebb8e A |
199 | ImageLoaderMachOCompressed* ImageLoaderMachOCompressed::instantiateFromCache(const macho_header* mh, const char* path, long slide, |
200 | const struct stat& info, unsigned int segCount, | |
201 | unsigned int libCount, const LinkContext& context) | |
39a8cd10 A |
202 | { |
203 | ImageLoaderMachOCompressed* image = ImageLoaderMachOCompressed::instantiateStart(mh, path, segCount, libCount); | |
204 | try { | |
205 | // record info about file | |
206 | image->setFileInfo(info.st_dev, info.st_ino, info.st_mtime); | |
207 | ||
208 | // remember this is from shared cache and cannot be unloaded | |
209 | image->fInSharedCache = true; | |
210 | image->setNeverUnload(); | |
412ebb8e | 211 | image->setSlide(slide); |
39a8cd10 A |
212 | |
213 | // segments already mapped in cache | |
214 | if ( context.verboseMapping ) { | |
215 | dyld::log("dyld: Using shared cached for %s\n", path); | |
216 | for(unsigned int i=0; i < image->fSegmentsCount; ++i) { | |
217 | dyld::log("%18s at 0x%08lX->0x%08lX\n", image->segName(i), image->segActualLoadAddress(i), image->segActualEndAddress(i)); | |
218 | } | |
219 | } | |
220 | ||
221 | image->instantiateFinish(context); | |
412ebb8e | 222 | image->setMapped(context); |
39a8cd10 A |
223 | } |
224 | catch (...) { | |
225 | // ImageLoader::setMapped() can throw an exception to block loading of image | |
226 | // <rdar://problem/6169686> Leaked fSegmentsArray and image segments during failed dlopen_preflight | |
227 | delete image; | |
228 | throw; | |
229 | } | |
230 | ||
231 | return image; | |
232 | } | |
233 | ||
234 | // create image by copying an in-memory mach-o file | |
235 | ImageLoaderMachOCompressed* ImageLoaderMachOCompressed::instantiateFromMemory(const char* moduleName, const macho_header* mh, uint64_t len, | |
236 | unsigned int segCount, unsigned int libCount, const LinkContext& context) | |
237 | { | |
238 | ImageLoaderMachOCompressed* image = ImageLoaderMachOCompressed::instantiateStart(mh, moduleName, segCount, libCount); | |
239 | try { | |
240 | // map segments | |
241 | if ( mh->filetype == MH_EXECUTE ) | |
242 | throw "can't load another MH_EXECUTE"; | |
243 | ||
244 | // vmcopy segments | |
245 | image->mapSegments((const void*)mh, len, context); | |
246 | ||
247 | // for compatibility, never unload dylibs loaded from memory | |
248 | image->setNeverUnload(); | |
249 | ||
250 | // bundle loads need path copied | |
251 | if ( moduleName != NULL ) | |
252 | image->setPath(moduleName); | |
253 | ||
254 | image->instantiateFinish(context); | |
412ebb8e | 255 | image->setMapped(context); |
39a8cd10 A |
256 | } |
257 | catch (...) { | |
258 | // ImageLoader::setMapped() can throw an exception to block loading of image | |
259 | // <rdar://problem/6169686> Leaked fSegmentsArray and image segments during failed dlopen_preflight | |
260 | delete image; | |
261 | throw; | |
262 | } | |
263 | ||
264 | return image; | |
265 | } | |
266 | ||
267 | ||
268 | ImageLoaderMachOCompressed::ImageLoaderMachOCompressed(const macho_header* mh, const char* path, unsigned int segCount, | |
269 | uint32_t segOffsets[], unsigned int libCount) | |
270 | : ImageLoaderMachO(mh, path, segCount, segOffsets, libCount), fDyldInfo(NULL) | |
271 | { | |
272 | } | |
273 | ||
274 | ImageLoaderMachOCompressed::~ImageLoaderMachOCompressed() | |
275 | { | |
276 | // don't do clean up in ~ImageLoaderMachO() because virtual call to segmentCommandOffsets() won't work | |
277 | destroy(); | |
278 | } | |
279 | ||
280 | ||
281 | ||
282 | // construct ImageLoaderMachOCompressed using "placement new" with SegmentMachO objects array at end | |
283 | ImageLoaderMachOCompressed* ImageLoaderMachOCompressed::instantiateStart(const macho_header* mh, const char* path, | |
284 | unsigned int segCount, unsigned int libCount) | |
285 | { | |
286 | size_t size = sizeof(ImageLoaderMachOCompressed) + segCount * sizeof(uint32_t) + libCount * sizeof(ImageLoader*); | |
287 | ImageLoaderMachOCompressed* allocatedSpace = static_cast<ImageLoaderMachOCompressed*>(malloc(size)); | |
288 | if ( allocatedSpace == NULL ) | |
289 | throw "malloc failed"; | |
290 | uint32_t* segOffsets = ((uint32_t*)(((uint8_t*)allocatedSpace) + sizeof(ImageLoaderMachOCompressed))); | |
291 | bzero(&segOffsets[segCount], libCount*sizeof(void*)); // zero out lib array | |
292 | return new (allocatedSpace) ImageLoaderMachOCompressed(mh, path, segCount, segOffsets, libCount); | |
293 | } | |
294 | ||
295 | ||
296 | // common code to finish initializing object | |
297 | void ImageLoaderMachOCompressed::instantiateFinish(const LinkContext& context) | |
298 | { | |
299 | // now that segments are mapped in, get real fMachOData, fLinkEditBase, and fSlide | |
300 | this->parseLoadCmds(); | |
39a8cd10 A |
301 | } |
302 | ||
303 | uint32_t* ImageLoaderMachOCompressed::segmentCommandOffsets() const | |
304 | { | |
305 | return ((uint32_t*)(((uint8_t*)this) + sizeof(ImageLoaderMachOCompressed))); | |
306 | } | |
307 | ||
308 | ||
309 | ImageLoader* ImageLoaderMachOCompressed::libImage(unsigned int libIndex) const | |
310 | { | |
311 | const uintptr_t* images = ((uintptr_t*)(((uint8_t*)this) + sizeof(ImageLoaderMachOCompressed) + fSegmentsCount*sizeof(uint32_t))); | |
412ebb8e A |
312 | // mask off low bits |
313 | return (ImageLoader*)(images[libIndex] & (-4)); | |
39a8cd10 A |
314 | } |
315 | ||
316 | bool ImageLoaderMachOCompressed::libReExported(unsigned int libIndex) const | |
317 | { | |
318 | const uintptr_t* images = ((uintptr_t*)(((uint8_t*)this) + sizeof(ImageLoaderMachOCompressed) + fSegmentsCount*sizeof(uint32_t))); | |
319 | // re-export flag is low bit | |
320 | return ((images[libIndex] & 1) != 0); | |
321 | } | |
322 | ||
412ebb8e A |
323 | bool ImageLoaderMachOCompressed::libIsUpward(unsigned int libIndex) const |
324 | { | |
325 | const uintptr_t* images = ((uintptr_t*)(((uint8_t*)this) + sizeof(ImageLoaderMachOCompressed) + fSegmentsCount*sizeof(uint32_t))); | |
326 | // re-export flag is second bit | |
327 | return ((images[libIndex] & 2) != 0); | |
328 | } | |
329 | ||
39a8cd10 | 330 | |
412ebb8e | 331 | void ImageLoaderMachOCompressed::setLibImage(unsigned int libIndex, ImageLoader* image, bool reExported, bool upward) |
39a8cd10 A |
332 | { |
333 | uintptr_t* images = ((uintptr_t*)(((uint8_t*)this) + sizeof(ImageLoaderMachOCompressed) + fSegmentsCount*sizeof(uint32_t))); | |
334 | uintptr_t value = (uintptr_t)image; | |
335 | if ( reExported ) | |
336 | value |= 1; | |
412ebb8e A |
337 | if ( upward ) |
338 | value |= 2; | |
39a8cd10 A |
339 | images[libIndex] = value; |
340 | } | |
341 | ||
342 | ||
343 | void ImageLoaderMachOCompressed::markFreeLINKEDIT(const LinkContext& context) | |
344 | { | |
345 | // mark that we are done with rebase and bind info | |
346 | markLINKEDIT(context, MADV_FREE); | |
347 | } | |
348 | ||
349 | void ImageLoaderMachOCompressed::markSequentialLINKEDIT(const LinkContext& context) | |
350 | { | |
351 | // mark the rebase and bind info and using sequential access | |
352 | markLINKEDIT(context, MADV_SEQUENTIAL); | |
353 | } | |
354 | ||
355 | void ImageLoaderMachOCompressed::markLINKEDIT(const LinkContext& context, int advise) | |
356 | { | |
357 | // if not loaded at preferred address, mark rebase info | |
358 | uintptr_t start = 0; | |
359 | if ( (fSlide != 0) && (fDyldInfo->rebase_size != 0) ) | |
360 | start = (uintptr_t)fLinkEditBase + fDyldInfo->rebase_off; | |
361 | else if ( fDyldInfo->bind_off != 0 ) | |
362 | start = (uintptr_t)fLinkEditBase + fDyldInfo->bind_off; | |
363 | else | |
364 | return; // no binding info to prefetch | |
365 | ||
366 | // end is at end of bind info | |
367 | uintptr_t end = 0; | |
368 | if ( fDyldInfo->bind_off != 0 ) | |
369 | end = (uintptr_t)fLinkEditBase + fDyldInfo->bind_off + fDyldInfo->bind_size; | |
370 | else if ( fDyldInfo->rebase_off != 0 ) | |
371 | end = (uintptr_t)fLinkEditBase + fDyldInfo->rebase_off + fDyldInfo->rebase_size; | |
372 | else | |
373 | return; | |
374 | ||
375 | ||
376 | // round to whole pages | |
19894a12 A |
377 | start = dyld_page_trunc(start); |
378 | end = dyld_page_round(end); | |
39a8cd10 A |
379 | |
380 | // do nothing if only one page of rebase/bind info | |
19894a12 | 381 | if ( (end-start) <= dyld_page_size ) |
39a8cd10 A |
382 | return; |
383 | ||
384 | // tell kernel about our access to these pages | |
385 | madvise((void*)start, end-start, advise); | |
386 | if ( context.verboseMapping ) { | |
387 | const char* adstr = "sequential"; | |
388 | if ( advise == MADV_FREE ) | |
389 | adstr = "free"; | |
412ebb8e | 390 | dyld::log("%18s %s 0x%0lX -> 0x%0lX for %s\n", "__LINKEDIT", adstr, start, end-1, this->getPath()); |
39a8cd10 A |
391 | } |
392 | } | |
393 | ||
394 | ||
395 | ||
396 | void ImageLoaderMachOCompressed::rebaseAt(const LinkContext& context, uintptr_t addr, uintptr_t slide, uint8_t type) | |
397 | { | |
832b6fce A |
398 | if ( context.verboseRebase ) { |
399 | dyld::log("dyld: rebase: %s:*0x%08lX += 0x%08lX\n", this->getShortName(), (uintptr_t)addr, slide); | |
400 | } | |
39a8cd10 A |
401 | //dyld::log("0x%08lX type=%d\n", addr, type); |
402 | uintptr_t* locationToFix = (uintptr_t*)addr; | |
403 | switch (type) { | |
404 | case REBASE_TYPE_POINTER: | |
405 | *locationToFix += slide; | |
406 | break; | |
407 | case REBASE_TYPE_TEXT_ABSOLUTE32: | |
408 | *locationToFix += slide; | |
409 | break; | |
410 | default: | |
411 | dyld::throwf("bad rebase type %d", type); | |
412 | } | |
413 | } | |
414 | ||
415 | void ImageLoaderMachOCompressed::throwBadRebaseAddress(uintptr_t address, uintptr_t segmentEndAddress, int segmentIndex, | |
416 | const uint8_t* startOpcodes, const uint8_t* endOpcodes, const uint8_t* pos) | |
417 | { | |
418 | dyld::throwf("malformed rebase opcodes (%ld/%ld): address 0x%08lX is beyond end of segment %s (0x%08lX -> 0x%08lX)", | |
419 | (intptr_t)(pos-startOpcodes), (intptr_t)(endOpcodes-startOpcodes), address, segName(segmentIndex), | |
420 | segActualLoadAddress(segmentIndex), segmentEndAddress); | |
421 | } | |
422 | ||
423 | void ImageLoaderMachOCompressed::rebase(const LinkContext& context) | |
424 | { | |
412ebb8e | 425 | CRSetCrashLogMessage2(this->getPath()); |
39a8cd10 A |
426 | const uintptr_t slide = this->fSlide; |
427 | const uint8_t* const start = fLinkEditBase + fDyldInfo->rebase_off; | |
428 | const uint8_t* const end = &start[fDyldInfo->rebase_size]; | |
429 | const uint8_t* p = start; | |
430 | ||
431 | try { | |
432 | uint8_t type = 0; | |
433 | int segmentIndex = 0; | |
434 | uintptr_t address = segActualLoadAddress(0); | |
435 | uintptr_t segmentEndAddress = segActualEndAddress(0); | |
19894a12 A |
436 | uintptr_t count; |
437 | uintptr_t skip; | |
39a8cd10 A |
438 | bool done = false; |
439 | while ( !done && (p < end) ) { | |
440 | uint8_t immediate = *p & REBASE_IMMEDIATE_MASK; | |
441 | uint8_t opcode = *p & REBASE_OPCODE_MASK; | |
442 | ++p; | |
443 | switch (opcode) { | |
444 | case REBASE_OPCODE_DONE: | |
445 | done = true; | |
446 | break; | |
447 | case REBASE_OPCODE_SET_TYPE_IMM: | |
448 | type = immediate; | |
449 | break; | |
450 | case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: | |
451 | segmentIndex = immediate; | |
19894a12 A |
452 | if ( segmentIndex >= fSegmentsCount ) |
453 | dyld::throwf("REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is too large (0..%d)", | |
454 | segmentIndex, fSegmentsCount-1); | |
39a8cd10 A |
455 | address = segActualLoadAddress(segmentIndex) + read_uleb128(p, end); |
456 | segmentEndAddress = segActualEndAddress(segmentIndex); | |
457 | break; | |
458 | case REBASE_OPCODE_ADD_ADDR_ULEB: | |
459 | address += read_uleb128(p, end); | |
460 | break; | |
461 | case REBASE_OPCODE_ADD_ADDR_IMM_SCALED: | |
462 | address += immediate*sizeof(uintptr_t); | |
463 | break; | |
464 | case REBASE_OPCODE_DO_REBASE_IMM_TIMES: | |
465 | for (int i=0; i < immediate; ++i) { | |
466 | if ( address >= segmentEndAddress ) | |
467 | throwBadRebaseAddress(address, segmentEndAddress, segmentIndex, start, end, p); | |
468 | rebaseAt(context, address, slide, type); | |
469 | address += sizeof(uintptr_t); | |
470 | } | |
471 | fgTotalRebaseFixups += immediate; | |
472 | break; | |
473 | case REBASE_OPCODE_DO_REBASE_ULEB_TIMES: | |
474 | count = read_uleb128(p, end); | |
475 | for (uint32_t i=0; i < count; ++i) { | |
476 | if ( address >= segmentEndAddress ) | |
477 | throwBadRebaseAddress(address, segmentEndAddress, segmentIndex, start, end, p); | |
478 | rebaseAt(context, address, slide, type); | |
479 | address += sizeof(uintptr_t); | |
480 | } | |
481 | fgTotalRebaseFixups += count; | |
482 | break; | |
483 | case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB: | |
484 | if ( address >= segmentEndAddress ) | |
485 | throwBadRebaseAddress(address, segmentEndAddress, segmentIndex, start, end, p); | |
486 | rebaseAt(context, address, slide, type); | |
487 | address += read_uleb128(p, end) + sizeof(uintptr_t); | |
488 | ++fgTotalRebaseFixups; | |
489 | break; | |
490 | case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB: | |
491 | count = read_uleb128(p, end); | |
492 | skip = read_uleb128(p, end); | |
493 | for (uint32_t i=0; i < count; ++i) { | |
494 | if ( address >= segmentEndAddress ) | |
495 | throwBadRebaseAddress(address, segmentEndAddress, segmentIndex, start, end, p); | |
496 | rebaseAt(context, address, slide, type); | |
497 | address += skip + sizeof(uintptr_t); | |
498 | } | |
499 | fgTotalRebaseFixups += count; | |
500 | break; | |
501 | default: | |
502 | dyld::throwf("bad rebase opcode %d", *p); | |
503 | } | |
504 | } | |
505 | } | |
506 | catch (const char* msg) { | |
507 | const char* newMsg = dyld::mkstringf("%s in %s", msg, this->getPath()); | |
508 | free((void*)msg); | |
509 | throw newMsg; | |
510 | } | |
412ebb8e | 511 | CRSetCrashLogMessage2(NULL); |
39a8cd10 A |
512 | } |
513 | ||
412ebb8e A |
514 | // |
515 | // This function is the hotspot of symbol lookup. It was pulled out of findExportedSymbol() | |
516 | // to enable it to be re-written in assembler if needed. | |
517 | // | |
518 | const uint8_t* ImageLoaderMachOCompressed::trieWalk(const uint8_t* start, const uint8_t* end, const char* s) | |
39a8cd10 | 519 | { |
39a8cd10 | 520 | const uint8_t* p = start; |
412ebb8e | 521 | while ( p != NULL ) { |
19894a12 | 522 | uintptr_t terminalSize = *p++; |
412ebb8e A |
523 | if ( terminalSize > 127 ) { |
524 | // except for re-export-with-rename, all terminal sizes fit in one byte | |
525 | --p; | |
526 | terminalSize = read_uleb128(p, end); | |
527 | } | |
39a8cd10 | 528 | if ( (*s == '\0') && (terminalSize != 0) ) { |
412ebb8e A |
529 | //dyld::log("trieWalk(%p) returning %p\n", start, p); |
530 | return p; | |
39a8cd10 | 531 | } |
412ebb8e A |
532 | const uint8_t* children = p + terminalSize; |
533 | //dyld::log("trieWalk(%p) sym=%s, terminalSize=%d, children=%p\n", start, s, terminalSize, children); | |
534 | uint8_t childrenRemaining = *children++; | |
535 | p = children; | |
19894a12 | 536 | uintptr_t nodeOffset = 0; |
412ebb8e | 537 | for (; childrenRemaining > 0; --childrenRemaining) { |
39a8cd10 | 538 | const char* ss = s; |
412ebb8e | 539 | //dyld::log("trieWalk(%p) child str=%s\n", start, (char*)p); |
39a8cd10 | 540 | bool wrongEdge = false; |
39a8cd10 A |
541 | // scan whole edge to get to next edge |
542 | // if edge is longer than target symbol name, don't read past end of symbol name | |
412ebb8e A |
543 | char c = *p; |
544 | while ( c != '\0' ) { | |
39a8cd10 | 545 | if ( !wrongEdge ) { |
412ebb8e | 546 | if ( c != *ss ) |
39a8cd10 | 547 | wrongEdge = true; |
412ebb8e | 548 | ++ss; |
39a8cd10 | 549 | } |
412ebb8e A |
550 | ++p; |
551 | c = *p; | |
39a8cd10 A |
552 | } |
553 | if ( wrongEdge ) { | |
554 | // advance to next child | |
412ebb8e A |
555 | ++p; // skip over zero terminator |
556 | // skip over uleb128 until last byte is found | |
557 | while ( (*p & 0x80) != 0 ) | |
558 | ++p; | |
559 | ++p; // skil over last byte of uleb128 | |
39a8cd10 A |
560 | } |
561 | else { | |
412ebb8e | 562 | // the symbol so far matches this edge (child) |
39a8cd10 | 563 | // so advance to the child's node |
412ebb8e A |
564 | ++p; |
565 | nodeOffset = read_uleb128(p, end); | |
39a8cd10 | 566 | s = ss; |
412ebb8e | 567 | //dyld::log("trieWalk() found matching edge advancing to node 0x%x\n", nodeOffset); |
39a8cd10 A |
568 | break; |
569 | } | |
570 | } | |
412ebb8e A |
571 | if ( nodeOffset != 0 ) |
572 | p = &start[nodeOffset]; | |
573 | else | |
574 | p = NULL; | |
575 | } | |
576 | //dyld::log("trieWalk(%p) return NULL\n", start); | |
577 | return NULL; | |
578 | } | |
579 | ||
580 | ||
581 | const ImageLoader::Symbol* ImageLoaderMachOCompressed::findExportedSymbol(const char* symbol, const ImageLoader** foundIn) const | |
582 | { | |
583 | //dyld::log("Compressed::findExportedSymbol(%s) in %s\n", symbol, this->getShortName()); | |
584 | if ( fDyldInfo->export_size == 0 ) | |
585 | return NULL; | |
586 | #if LOG_BINDINGS | |
587 | dyld::logBindings("%s: %s\n", this->getShortName(), symbol); | |
588 | #endif | |
589 | ++ImageLoaderMachO::fgSymbolTrieSearchs; | |
590 | const uint8_t* start = &fLinkEditBase[fDyldInfo->export_off]; | |
591 | const uint8_t* end = &start[fDyldInfo->export_size]; | |
592 | const uint8_t* foundNodeStart = this->trieWalk(start, end, symbol); | |
593 | if ( foundNodeStart != NULL ) { | |
594 | const uint8_t* p = foundNodeStart; | |
19894a12 | 595 | const uintptr_t flags = read_uleb128(p, end); |
412ebb8e A |
596 | // found match, return pointer to terminal part of node |
597 | if ( flags & EXPORT_SYMBOL_FLAGS_REEXPORT ) { | |
598 | // re-export from another dylib, lookup there | |
19894a12 | 599 | const uintptr_t ordinal = read_uleb128(p, end); |
412ebb8e A |
600 | const char* importedName = (char*)p; |
601 | if ( importedName[0] == '\0' ) | |
602 | importedName = symbol; | |
603 | if ( (ordinal > 0) && (ordinal <= libraryCount()) ) { | |
19894a12 | 604 | const ImageLoader* reexportedFrom = libImage((unsigned int)ordinal-1); |
412ebb8e A |
605 | //dyld::log("Compressed::findExportedSymbol(), %s -> %s/%s\n", symbol, reexportedFrom->getShortName(), importedName); |
606 | return reexportedFrom->findExportedSymbol(importedName, true, foundIn); | |
607 | } | |
608 | else { | |
609 | //dyld::throwf("bad mach-o binary, library ordinal (%u) invalid (max %u) for re-exported symbol %s in %s", | |
610 | // ordinal, libraryCount(), symbol, this->getPath()); | |
611 | } | |
612 | } | |
39a8cd10 | 613 | else { |
412ebb8e A |
614 | //dyld::log("findExportedSymbol(%s) in %s found match, returning %p\n", symbol, this->getShortName(), p); |
615 | if ( foundIn != NULL ) | |
616 | *foundIn = (ImageLoader*)this; | |
617 | // return pointer to terminal part of node | |
618 | return (Symbol*)foundNodeStart; | |
39a8cd10 | 619 | } |
412ebb8e A |
620 | } |
621 | return NULL; | |
39a8cd10 A |
622 | } |
623 | ||
624 | ||
625 | bool ImageLoaderMachOCompressed::containsSymbol(const void* addr) const | |
626 | { | |
627 | const uint8_t* start = &fLinkEditBase[fDyldInfo->export_off]; | |
628 | const uint8_t* end = &start[fDyldInfo->export_size]; | |
629 | return ( (start <= addr) && (addr < end) ); | |
630 | } | |
631 | ||
632 | ||
2fd3f4e8 | 633 | uintptr_t ImageLoaderMachOCompressed::exportedSymbolAddress(const LinkContext& context, const Symbol* symbol, const ImageLoader* requestor, bool runResolver) const |
39a8cd10 A |
634 | { |
635 | const uint8_t* exportNode = (uint8_t*)symbol; | |
636 | const uint8_t* exportTrieStart = fLinkEditBase + fDyldInfo->export_off; | |
637 | const uint8_t* exportTrieEnd = exportTrieStart + fDyldInfo->export_size; | |
638 | if ( (exportNode < exportTrieStart) || (exportNode > exportTrieEnd) ) | |
639 | throw "symbol is not in trie"; | |
412ebb8e | 640 | //dyld::log("exportedSymbolAddress(): node=%p, nodeOffset=0x%04X in %s\n", symbol, (int)((uint8_t*)symbol - exportTrieStart), this->getShortName()); |
19894a12 | 641 | uintptr_t flags = read_uleb128(exportNode, exportTrieEnd); |
2fd3f4e8 A |
642 | switch ( flags & EXPORT_SYMBOL_FLAGS_KIND_MASK ) { |
643 | case EXPORT_SYMBOL_FLAGS_KIND_REGULAR: | |
644 | if ( runResolver && (flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER) ) { | |
645 | // this node has a stub and resolver, run the resolver to get target address | |
646 | uintptr_t stub = read_uleb128(exportNode, exportTrieEnd) + (uintptr_t)fMachOData; // skip over stub | |
647 | // <rdar://problem/10657737> interposing dylibs have the stub address as their replacee | |
19894a12 A |
648 | uintptr_t interposedStub = interposedAddress(context, stub, requestor); |
649 | if ( interposedStub != stub ) | |
650 | return interposedStub; | |
651 | // stub was not interposed, so run resolver | |
2fd3f4e8 A |
652 | typedef uintptr_t (*ResolverProc)(void); |
653 | ResolverProc resolver = (ResolverProc)(read_uleb128(exportNode, exportTrieEnd) + (uintptr_t)fMachOData); | |
654 | uintptr_t result = (*resolver)(); | |
655 | if ( context.verboseBind ) | |
656 | dyld::log("dyld: resolver at %p returned 0x%08lX\n", resolver, result); | |
657 | return result; | |
832b6fce | 658 | } |
2fd3f4e8 A |
659 | return read_uleb128(exportNode, exportTrieEnd) + (uintptr_t)fMachOData; |
660 | case EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL: | |
661 | if ( flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER ) | |
19894a12 | 662 | dyld::throwf("unsupported exported symbol kind. flags=%lu at node=%p", flags, symbol); |
2fd3f4e8 A |
663 | return read_uleb128(exportNode, exportTrieEnd) + (uintptr_t)fMachOData; |
664 | case EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE: | |
665 | if ( flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER ) | |
19894a12 | 666 | dyld::throwf("unsupported exported symbol kind. flags=%lu at node=%p", flags, symbol); |
2fd3f4e8 A |
667 | return read_uleb128(exportNode, exportTrieEnd); |
668 | default: | |
19894a12 | 669 | dyld::throwf("unsupported exported symbol kind. flags=%lu at node=%p", flags, symbol); |
412ebb8e | 670 | } |
39a8cd10 A |
671 | } |
672 | ||
673 | bool ImageLoaderMachOCompressed::exportedSymbolIsWeakDefintion(const Symbol* symbol) const | |
674 | { | |
675 | const uint8_t* exportNode = (uint8_t*)symbol; | |
676 | const uint8_t* exportTrieStart = fLinkEditBase + fDyldInfo->export_off; | |
677 | const uint8_t* exportTrieEnd = exportTrieStart + fDyldInfo->export_size; | |
678 | if ( (exportNode < exportTrieStart) || (exportNode > exportTrieEnd) ) | |
679 | throw "symbol is not in trie"; | |
19894a12 | 680 | uintptr_t flags = read_uleb128(exportNode, exportTrieEnd); |
39a8cd10 A |
681 | return ( flags & EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION ); |
682 | } | |
683 | ||
684 | ||
685 | const char* ImageLoaderMachOCompressed::exportedSymbolName(const Symbol* symbol) const | |
686 | { | |
687 | throw "NSNameOfSymbol() not supported with compressed LINKEDIT"; | |
688 | } | |
689 | ||
690 | unsigned int ImageLoaderMachOCompressed::exportedSymbolCount() const | |
691 | { | |
692 | throw "NSSymbolDefinitionCountInObjectFileImage() not supported with compressed LINKEDIT"; | |
693 | } | |
694 | ||
695 | const ImageLoader::Symbol* ImageLoaderMachOCompressed::exportedSymbolIndexed(unsigned int index) const | |
696 | { | |
697 | throw "NSSymbolDefinitionNameInObjectFileImage() not supported with compressed LINKEDIT"; | |
698 | } | |
699 | ||
700 | unsigned int ImageLoaderMachOCompressed::importedSymbolCount() const | |
701 | { | |
702 | throw "NSSymbolReferenceCountInObjectFileImage() not supported with compressed LINKEDIT"; | |
703 | } | |
704 | ||
705 | const ImageLoader::Symbol* ImageLoaderMachOCompressed::importedSymbolIndexed(unsigned int index) const | |
706 | { | |
707 | throw "NSSymbolReferenceCountInObjectFileImage() not supported with compressed LINKEDIT"; | |
708 | } | |
709 | ||
710 | const char* ImageLoaderMachOCompressed::importedSymbolName(const Symbol* symbol) const | |
711 | { | |
712 | throw "NSSymbolReferenceNameInObjectFileImage() not supported with compressed LINKEDIT"; | |
713 | } | |
714 | ||
715 | ||
716 | ||
412ebb8e A |
717 | uintptr_t ImageLoaderMachOCompressed::resolveFlat(const LinkContext& context, const char* symbolName, bool weak_import, |
718 | bool runResolver, const ImageLoader** foundIn) | |
39a8cd10 A |
719 | { |
720 | const Symbol* sym; | |
721 | if ( context.flatExportFinder(symbolName, &sym, foundIn) ) { | |
2fd3f4e8 A |
722 | if ( *foundIn != this ) |
723 | context.addDynamicReference(this, const_cast<ImageLoader*>(*foundIn)); | |
412ebb8e | 724 | return (*foundIn)->getExportedSymbolAddress(sym, context, this, runResolver); |
39a8cd10 A |
725 | } |
726 | // if a bundle is loaded privately the above will not find its exports | |
727 | if ( this->isBundle() && this->hasHiddenExports() ) { | |
728 | // look in self for needed symbol | |
729 | sym = this->ImageLoaderMachO::findExportedSymbol(symbolName, false, foundIn); | |
730 | if ( sym != NULL ) | |
412ebb8e | 731 | return (*foundIn)->getExportedSymbolAddress(sym, context, this, runResolver); |
39a8cd10 A |
732 | } |
733 | if ( weak_import ) { | |
734 | // definition can't be found anywhere, ok because it is weak, just return 0 | |
735 | return 0; | |
736 | } | |
19894a12 | 737 | throwSymbolNotFound(context, symbolName, this->getPath(), "", "flat namespace"); |
39a8cd10 A |
738 | } |
739 | ||
740 | ||
741 | uintptr_t ImageLoaderMachOCompressed::resolveTwolevel(const LinkContext& context, const ImageLoader* targetImage, bool weak_import, | |
412ebb8e | 742 | const char* symbolName, bool runResolver, const ImageLoader** foundIn) |
39a8cd10 A |
743 | { |
744 | // two level lookup | |
745 | const Symbol* sym = targetImage->findExportedSymbol(symbolName, true, foundIn); | |
746 | if ( sym != NULL ) { | |
412ebb8e | 747 | return (*foundIn)->getExportedSymbolAddress(sym, context, this, runResolver); |
39a8cd10 A |
748 | } |
749 | ||
750 | if ( weak_import ) { | |
751 | // definition can't be found anywhere, ok because it is weak, just return 0 | |
752 | return 0; | |
753 | } | |
754 | ||
19894a12 A |
755 | // nowhere to be found, check if maybe this image is too new for this OS |
756 | char versMismatch[256]; | |
757 | versMismatch[0] = '\0'; | |
758 | uint32_t imageMinOS = this->minOSVersion(); | |
759 | // dyld is always built for the current OS, so we can get the current OS version | |
760 | // from the load command in dyld itself. | |
761 | extern const mach_header __dso_handle; | |
762 | uint32_t dyldMinOS = ImageLoaderMachO::minOSVersion(&__dso_handle); | |
763 | if ( imageMinOS > dyldMinOS ) { | |
764 | #if __MAC_OS_X_VERSION_MIN_REQUIRED | |
765 | const char* msg = dyld::mkstringf(" (which was built for Mac OS X %d.%d)", imageMinOS >> 16, (imageMinOS >> 8) & 0xFF); | |
766 | #else | |
767 | const char* msg = dyld::mkstringf(" (which was built for iOS %d.%d)", imageMinOS >> 16, (imageMinOS >> 8) & 0xFF); | |
768 | #endif | |
769 | strcpy(versMismatch, msg); | |
770 | ::free((void*)msg); | |
771 | } | |
772 | throwSymbolNotFound(context, symbolName, this->getPath(), versMismatch, targetImage->getPath()); | |
39a8cd10 A |
773 | } |
774 | ||
775 | ||
776 | uintptr_t ImageLoaderMachOCompressed::resolve(const LinkContext& context, const char* symbolName, | |
19894a12 | 777 | uint8_t symboFlags, long libraryOrdinal, const ImageLoader** targetImage, |
412ebb8e | 778 | LastLookup* last, bool runResolver) |
39a8cd10 A |
779 | { |
780 | *targetImage = NULL; | |
781 | ||
782 | // only clients that benefit from caching last lookup pass in a LastLookup struct | |
783 | if ( last != NULL ) { | |
784 | if ( (last->ordinal == libraryOrdinal) | |
785 | && (last->flags == symboFlags) | |
786 | && (last->name == symbolName) ) { | |
787 | *targetImage = last->foundIn; | |
788 | return last->result; | |
789 | } | |
790 | } | |
791 | ||
792 | bool weak_import = (symboFlags & BIND_SYMBOL_FLAGS_WEAK_IMPORT); | |
793 | uintptr_t symbolAddress; | |
794 | if ( context.bindFlat || (libraryOrdinal == BIND_SPECIAL_DYLIB_FLAT_LOOKUP) ) { | |
412ebb8e | 795 | symbolAddress = this->resolveFlat(context, symbolName, weak_import, runResolver, targetImage); |
39a8cd10 A |
796 | } |
797 | else { | |
798 | if ( libraryOrdinal == BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE ) { | |
799 | *targetImage = context.mainExecutable; | |
800 | } | |
801 | else if ( libraryOrdinal == BIND_SPECIAL_DYLIB_SELF ) { | |
802 | *targetImage = this; | |
803 | } | |
804 | else if ( libraryOrdinal <= 0 ) { | |
19894a12 | 805 | dyld::throwf("bad mach-o binary, unknown special library ordinal (%ld) too big for symbol %s in %s", |
39a8cd10 A |
806 | libraryOrdinal, symbolName, this->getPath()); |
807 | } | |
808 | else if ( (unsigned)libraryOrdinal <= libraryCount() ) { | |
19894a12 | 809 | *targetImage = libImage((unsigned int)libraryOrdinal-1); |
39a8cd10 A |
810 | } |
811 | else { | |
19894a12 | 812 | dyld::throwf("bad mach-o binary, library ordinal (%ld) too big (max %u) for symbol %s in %s", |
39a8cd10 A |
813 | libraryOrdinal, libraryCount(), symbolName, this->getPath()); |
814 | } | |
815 | if ( *targetImage == NULL ) { | |
816 | if ( weak_import ) { | |
817 | // if target library not loaded and reference is weak or library is weak return 0 | |
818 | symbolAddress = 0; | |
819 | } | |
820 | else { | |
19894a12 | 821 | dyld::throwf("can't resolve symbol %s in %s because dependent dylib #%ld could not be loaded", |
39a8cd10 A |
822 | symbolName, this->getPath(), libraryOrdinal); |
823 | } | |
824 | } | |
825 | else { | |
412ebb8e | 826 | symbolAddress = resolveTwolevel(context, *targetImage, weak_import, symbolName, runResolver, targetImage); |
39a8cd10 A |
827 | } |
828 | } | |
829 | ||
830 | // save off lookup results if client wants | |
831 | if ( last != NULL ) { | |
832 | last->ordinal = libraryOrdinal; | |
833 | last->flags = symboFlags; | |
834 | last->name = symbolName; | |
835 | last->foundIn = *targetImage; | |
836 | last->result = symbolAddress; | |
837 | } | |
838 | ||
839 | return symbolAddress; | |
840 | } | |
841 | ||
842 | uintptr_t ImageLoaderMachOCompressed::bindAt(const LinkContext& context, uintptr_t addr, uint8_t type, const char* symbolName, | |
19894a12 | 843 | uint8_t symboFlags, intptr_t addend, long libraryOrdinal, const char* msg, |
412ebb8e | 844 | LastLookup* last, bool runResolver) |
39a8cd10 A |
845 | { |
846 | const ImageLoader* targetImage; | |
847 | uintptr_t symbolAddress; | |
848 | ||
849 | // resolve symbol | |
412ebb8e | 850 | symbolAddress = this->resolve(context, symbolName, symboFlags, libraryOrdinal, &targetImage, last, runResolver); |
39a8cd10 A |
851 | |
852 | // do actual update | |
853 | return this->bindLocation(context, addr, symbolAddress, targetImage, type, symbolName, addend, msg); | |
854 | } | |
855 | ||
856 | void ImageLoaderMachOCompressed::throwBadBindingAddress(uintptr_t address, uintptr_t segmentEndAddress, int segmentIndex, | |
857 | const uint8_t* startOpcodes, const uint8_t* endOpcodes, const uint8_t* pos) | |
858 | { | |
859 | dyld::throwf("malformed binding opcodes (%ld/%ld): address 0x%08lX is beyond end of segment %s (0x%08lX -> 0x%08lX)", | |
860 | (intptr_t)(pos-startOpcodes), (intptr_t)(endOpcodes-startOpcodes), address, segName(segmentIndex), | |
861 | segActualLoadAddress(segmentIndex), segmentEndAddress); | |
862 | } | |
863 | ||
864 | ||
865 | void ImageLoaderMachOCompressed::doBind(const LinkContext& context, bool forceLazysBound) | |
866 | { | |
412ebb8e A |
867 | CRSetCrashLogMessage2(this->getPath()); |
868 | ||
39a8cd10 A |
869 | // if prebound and loaded at prebound address, and all libraries are same as when this was prebound, then no need to bind |
870 | // note: flat-namespace binaries need to have imports rebound (even if correctly prebound) | |
871 | if ( this->usablePrebinding(context) ) { | |
872 | // don't need to bind | |
873 | } | |
874 | else { | |
412ebb8e A |
875 | |
876 | #if TEXT_RELOC_SUPPORT | |
877 | // if there are __TEXT fixups, temporarily make __TEXT writable | |
878 | if ( fTextSegmentBinds ) | |
879 | this->makeTextSegmentWritable(context, true); | |
880 | #endif | |
881 | ||
39a8cd10 A |
882 | // run through all binding opcodes |
883 | eachBind(context, &ImageLoaderMachOCompressed::bindAt); | |
884 | ||
412ebb8e A |
885 | #if TEXT_RELOC_SUPPORT |
886 | // if there were __TEXT fixups, restore write protection | |
887 | if ( fTextSegmentBinds ) | |
888 | this->makeTextSegmentWritable(context, false); | |
889 | #endif | |
890 | ||
891 | // if this image is in the shared cache, but depends on something no longer in the shared cache, | |
39a8cd10 | 892 | // there is no way to reset the lazy pointers, so force bind them now |
412ebb8e | 893 | if ( forceLazysBound || fInSharedCache ) |
39a8cd10 | 894 | this->doBindJustLazies(context); |
412ebb8e A |
895 | |
896 | // this image is in cache, but something below it is not. If | |
897 | // this image has lazy pointer to a resolver function, then | |
898 | // the stub may have been altered to point to a shared lazy pointer. | |
899 | if ( fInSharedCache ) | |
900 | this->updateOptimizedLazyPointers(context); | |
901 | ||
902 | // tell kernel we are done with chunks of LINKEDIT | |
903 | if ( !context.preFetchDisabled ) | |
904 | this->markFreeLINKEDIT(context); | |
39a8cd10 A |
905 | } |
906 | ||
907 | // set up dyld entry points in image | |
908 | // do last so flat main executables will have __dyld or __program_vars set up | |
909 | this->setupLazyPointerHandler(context); | |
412ebb8e | 910 | CRSetCrashLogMessage2(NULL); |
39a8cd10 A |
911 | } |
912 | ||
913 | ||
914 | void ImageLoaderMachOCompressed::doBindJustLazies(const LinkContext& context) | |
915 | { | |
916 | eachLazyBind(context, &ImageLoaderMachOCompressed::bindAt); | |
917 | } | |
918 | ||
919 | void ImageLoaderMachOCompressed::eachBind(const LinkContext& context, bind_handler handler) | |
920 | { | |
921 | try { | |
922 | uint8_t type = 0; | |
923 | int segmentIndex = 0; | |
924 | uintptr_t address = segActualLoadAddress(0); | |
925 | uintptr_t segmentEndAddress = segActualEndAddress(0); | |
926 | const char* symbolName = NULL; | |
927 | uint8_t symboFlags = 0; | |
19894a12 | 928 | long libraryOrdinal = 0; |
39a8cd10 | 929 | intptr_t addend = 0; |
19894a12 A |
930 | uintptr_t count; |
931 | uintptr_t skip; | |
39a8cd10 A |
932 | LastLookup last = { 0, 0, NULL, 0, NULL }; |
933 | const uint8_t* const start = fLinkEditBase + fDyldInfo->bind_off; | |
934 | const uint8_t* const end = &start[fDyldInfo->bind_size]; | |
935 | const uint8_t* p = start; | |
936 | bool done = false; | |
937 | while ( !done && (p < end) ) { | |
938 | uint8_t immediate = *p & BIND_IMMEDIATE_MASK; | |
939 | uint8_t opcode = *p & BIND_OPCODE_MASK; | |
940 | ++p; | |
941 | switch (opcode) { | |
942 | case BIND_OPCODE_DONE: | |
943 | done = true; | |
944 | break; | |
945 | case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: | |
946 | libraryOrdinal = immediate; | |
947 | break; | |
948 | case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: | |
949 | libraryOrdinal = read_uleb128(p, end); | |
950 | break; | |
951 | case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: | |
952 | // the special ordinals are negative numbers | |
953 | if ( immediate == 0 ) | |
954 | libraryOrdinal = 0; | |
955 | else { | |
956 | int8_t signExtended = BIND_OPCODE_MASK | immediate; | |
957 | libraryOrdinal = signExtended; | |
958 | } | |
959 | break; | |
960 | case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: | |
961 | symbolName = (char*)p; | |
962 | symboFlags = immediate; | |
963 | while (*p != '\0') | |
964 | ++p; | |
965 | ++p; | |
966 | break; | |
967 | case BIND_OPCODE_SET_TYPE_IMM: | |
968 | type = immediate; | |
969 | break; | |
970 | case BIND_OPCODE_SET_ADDEND_SLEB: | |
971 | addend = read_sleb128(p, end); | |
972 | break; | |
973 | case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: | |
974 | segmentIndex = immediate; | |
19894a12 A |
975 | if ( segmentIndex >= fSegmentsCount ) |
976 | dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is too large (0..%d)", | |
977 | segmentIndex, fSegmentsCount-1); | |
39a8cd10 A |
978 | address = segActualLoadAddress(segmentIndex) + read_uleb128(p, end); |
979 | segmentEndAddress = segActualEndAddress(segmentIndex); | |
980 | break; | |
981 | case BIND_OPCODE_ADD_ADDR_ULEB: | |
982 | address += read_uleb128(p, end); | |
983 | break; | |
984 | case BIND_OPCODE_DO_BIND: | |
985 | if ( address >= segmentEndAddress ) | |
986 | throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p); | |
412ebb8e | 987 | (this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "", &last, false); |
39a8cd10 A |
988 | address += sizeof(intptr_t); |
989 | break; | |
990 | case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: | |
991 | if ( address >= segmentEndAddress ) | |
992 | throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p); | |
412ebb8e | 993 | (this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "", &last, false); |
39a8cd10 A |
994 | address += read_uleb128(p, end) + sizeof(intptr_t); |
995 | break; | |
996 | case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: | |
997 | if ( address >= segmentEndAddress ) | |
998 | throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p); | |
412ebb8e | 999 | (this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "", &last, false); |
39a8cd10 A |
1000 | address += immediate*sizeof(intptr_t) + sizeof(intptr_t); |
1001 | break; | |
1002 | case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: | |
1003 | count = read_uleb128(p, end); | |
1004 | skip = read_uleb128(p, end); | |
1005 | for (uint32_t i=0; i < count; ++i) { | |
1006 | if ( address >= segmentEndAddress ) | |
1007 | throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p); | |
412ebb8e | 1008 | (this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "", &last, false); |
39a8cd10 A |
1009 | address += skip + sizeof(intptr_t); |
1010 | } | |
1011 | break; | |
1012 | default: | |
1013 | dyld::throwf("bad bind opcode %d in bind info", *p); | |
1014 | } | |
1015 | } | |
1016 | } | |
1017 | catch (const char* msg) { | |
1018 | const char* newMsg = dyld::mkstringf("%s in %s", msg, this->getPath()); | |
1019 | free((void*)msg); | |
1020 | throw newMsg; | |
1021 | } | |
1022 | } | |
1023 | ||
1024 | void ImageLoaderMachOCompressed::eachLazyBind(const LinkContext& context, bind_handler handler) | |
1025 | { | |
1026 | try { | |
1027 | uint8_t type = BIND_TYPE_POINTER; | |
1028 | int segmentIndex = 0; | |
1029 | uintptr_t address = segActualLoadAddress(0); | |
1030 | uintptr_t segmentEndAddress = segActualEndAddress(0); | |
1031 | const char* symbolName = NULL; | |
1032 | uint8_t symboFlags = 0; | |
19894a12 | 1033 | long libraryOrdinal = 0; |
39a8cd10 A |
1034 | intptr_t addend = 0; |
1035 | const uint8_t* const start = fLinkEditBase + fDyldInfo->lazy_bind_off; | |
1036 | const uint8_t* const end = &start[fDyldInfo->lazy_bind_size]; | |
1037 | const uint8_t* p = start; | |
1038 | bool done = false; | |
1039 | while ( !done && (p < end) ) { | |
1040 | uint8_t immediate = *p & BIND_IMMEDIATE_MASK; | |
1041 | uint8_t opcode = *p & BIND_OPCODE_MASK; | |
1042 | ++p; | |
1043 | switch (opcode) { | |
1044 | case BIND_OPCODE_DONE: | |
1045 | // there is BIND_OPCODE_DONE at end of each lazy bind, don't stop until end of whole sequence | |
1046 | break; | |
1047 | case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: | |
1048 | libraryOrdinal = immediate; | |
1049 | break; | |
1050 | case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: | |
1051 | libraryOrdinal = read_uleb128(p, end); | |
1052 | break; | |
1053 | case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: | |
1054 | // the special ordinals are negative numbers | |
1055 | if ( immediate == 0 ) | |
1056 | libraryOrdinal = 0; | |
1057 | else { | |
1058 | int8_t signExtended = BIND_OPCODE_MASK | immediate; | |
1059 | libraryOrdinal = signExtended; | |
1060 | } | |
1061 | break; | |
1062 | case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: | |
1063 | symbolName = (char*)p; | |
1064 | symboFlags = immediate; | |
1065 | while (*p != '\0') | |
1066 | ++p; | |
1067 | ++p; | |
1068 | break; | |
1069 | case BIND_OPCODE_SET_TYPE_IMM: | |
1070 | type = immediate; | |
1071 | break; | |
1072 | case BIND_OPCODE_SET_ADDEND_SLEB: | |
1073 | addend = read_sleb128(p, end); | |
1074 | break; | |
1075 | case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: | |
1076 | segmentIndex = immediate; | |
19894a12 A |
1077 | if ( segmentIndex >= fSegmentsCount ) |
1078 | dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is too large (0..%d)", | |
1079 | segmentIndex, fSegmentsCount-1); | |
39a8cd10 A |
1080 | address = segActualLoadAddress(segmentIndex) + read_uleb128(p, end); |
1081 | segmentEndAddress = segActualEndAddress(segmentIndex); | |
1082 | break; | |
1083 | case BIND_OPCODE_ADD_ADDR_ULEB: | |
1084 | address += read_uleb128(p, end); | |
1085 | break; | |
1086 | case BIND_OPCODE_DO_BIND: | |
1087 | if ( address >= segmentEndAddress ) | |
1088 | throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p); | |
2fd3f4e8 | 1089 | (this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "forced lazy ", NULL, false); |
39a8cd10 A |
1090 | address += sizeof(intptr_t); |
1091 | break; | |
1092 | case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: | |
1093 | case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: | |
1094 | case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: | |
1095 | default: | |
1096 | dyld::throwf("bad lazy bind opcode %d", *p); | |
1097 | } | |
1098 | } | |
1099 | } | |
1100 | ||
1101 | catch (const char* msg) { | |
1102 | const char* newMsg = dyld::mkstringf("%s in %s", msg, this->getPath()); | |
1103 | free((void*)msg); | |
1104 | throw newMsg; | |
1105 | } | |
1106 | } | |
1107 | ||
1108 | // A program built targeting 10.5 will have hybrid stubs. When used with weak symbols | |
1109 | // the classic lazy loader is used even when running on 10.6 | |
1110 | uintptr_t ImageLoaderMachOCompressed::doBindLazySymbol(uintptr_t* lazyPointer, const LinkContext& context) | |
1111 | { | |
1112 | // only works with compressed LINKEDIT if classic symbol table is also present | |
1113 | const macho_nlist* symbolTable = NULL; | |
1114 | const char* symbolTableStrings = NULL; | |
1115 | const dysymtab_command* dynSymbolTable = NULL; | |
1116 | const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; | |
1117 | const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; | |
1118 | const struct load_command* cmd = cmds; | |
1119 | for (uint32_t i = 0; i < cmd_count; ++i) { | |
1120 | switch (cmd->cmd) { | |
1121 | case LC_SYMTAB: | |
1122 | { | |
1123 | const struct symtab_command* symtab = (struct symtab_command*)cmd; | |
1124 | symbolTableStrings = (const char*)&fLinkEditBase[symtab->stroff]; | |
1125 | symbolTable = (macho_nlist*)(&fLinkEditBase[symtab->symoff]); | |
1126 | } | |
1127 | break; | |
1128 | case LC_DYSYMTAB: | |
1129 | dynSymbolTable = (struct dysymtab_command*)cmd; | |
1130 | break; | |
1131 | } | |
1132 | cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); | |
1133 | } | |
1134 | // no symbol table => no lookup by address | |
1135 | if ( (symbolTable == NULL) || (dynSymbolTable == NULL) ) | |
1136 | dyld::throwf("classic lazy binding used with compressed LINKEDIT at %p in image %s", lazyPointer, this->getPath()); | |
1137 | ||
1138 | // scan for all lazy-pointer sections | |
1139 | const bool twoLevel = this->usesTwoLevelNameSpace(); | |
1140 | const uint32_t* const indirectTable = (uint32_t*)&fLinkEditBase[dynSymbolTable->indirectsymoff]; | |
1141 | cmd = cmds; | |
1142 | for (uint32_t i = 0; i < cmd_count; ++i) { | |
1143 | switch (cmd->cmd) { | |
1144 | case LC_SEGMENT_COMMAND: | |
1145 | { | |
1146 | const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; | |
1147 | const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command)); | |
1148 | const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects]; | |
1149 | for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { | |
1150 | const uint8_t type = sect->flags & SECTION_TYPE; | |
1151 | uint32_t symbolIndex = INDIRECT_SYMBOL_LOCAL; | |
1152 | if ( type == S_LAZY_SYMBOL_POINTERS ) { | |
19894a12 | 1153 | const size_t pointerCount = sect->size / sizeof(uintptr_t); |
39a8cd10 A |
1154 | uintptr_t* const symbolPointers = (uintptr_t*)(sect->addr + fSlide); |
1155 | if ( (lazyPointer >= symbolPointers) && (lazyPointer < &symbolPointers[pointerCount]) ) { | |
1156 | const uint32_t indirectTableOffset = sect->reserved1; | |
19894a12 | 1157 | const size_t lazyIndex = lazyPointer - symbolPointers; |
39a8cd10 A |
1158 | symbolIndex = indirectTable[indirectTableOffset + lazyIndex]; |
1159 | } | |
1160 | } | |
1161 | if ( (symbolIndex != INDIRECT_SYMBOL_ABS) && (symbolIndex != INDIRECT_SYMBOL_LOCAL) ) { | |
1162 | const macho_nlist* symbol = &symbolTable[symbolIndex]; | |
1163 | const char* symbolName = &symbolTableStrings[symbol->n_un.n_strx]; | |
1164 | int libraryOrdinal = GET_LIBRARY_ORDINAL(symbol->n_desc); | |
1165 | if ( !twoLevel || context.bindFlat ) | |
1166 | libraryOrdinal = BIND_SPECIAL_DYLIB_FLAT_LOOKUP; | |
1167 | uintptr_t ptrToBind = (uintptr_t)lazyPointer; | |
1168 | uintptr_t symbolAddr = bindAt(context, ptrToBind, BIND_TYPE_POINTER, symbolName, 0, 0, libraryOrdinal, "lazy ", NULL); | |
1169 | ++fgTotalLazyBindFixups; | |
1170 | return symbolAddr; | |
1171 | } | |
1172 | } | |
1173 | } | |
1174 | break; | |
1175 | } | |
1176 | cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); | |
1177 | } | |
1178 | dyld::throwf("lazy pointer not found at address %p in image %s", lazyPointer, this->getPath()); | |
1179 | } | |
1180 | ||
1181 | ||
412ebb8e A |
1182 | uintptr_t ImageLoaderMachOCompressed::doBindFastLazySymbol(uint32_t lazyBindingInfoOffset, const LinkContext& context, |
1183 | void (*lock)(), void (*unlock)()) | |
39a8cd10 | 1184 | { |
412ebb8e A |
1185 | // <rdar://problem/8663923> race condition with flat-namespace lazy binding |
1186 | if ( this->usesTwoLevelNameSpace() ) { | |
1187 | // two-level namespace lookup does not require lock because dependents can't be unloaded before this image | |
1188 | } | |
1189 | else { | |
1190 | // acquire dyld global lock | |
1191 | if ( lock != NULL ) | |
1192 | lock(); | |
1193 | } | |
1194 | ||
39a8cd10 A |
1195 | const uint8_t* const start = fLinkEditBase + fDyldInfo->lazy_bind_off; |
1196 | const uint8_t* const end = &start[fDyldInfo->lazy_bind_size]; | |
412ebb8e A |
1197 | if ( lazyBindingInfoOffset > fDyldInfo->lazy_bind_size ) { |
1198 | dyld::throwf("fast lazy bind offset out of range (%u, max=%u) in image %s", | |
1199 | lazyBindingInfoOffset, fDyldInfo->lazy_bind_size, this->getPath()); | |
1200 | } | |
39a8cd10 A |
1201 | |
1202 | uint8_t type = BIND_TYPE_POINTER; | |
1203 | uintptr_t address = 0; | |
1204 | const char* symbolName = NULL; | |
1205 | uint8_t symboFlags = 0; | |
19894a12 | 1206 | long libraryOrdinal = 0; |
39a8cd10 A |
1207 | bool done = false; |
1208 | uintptr_t result = 0; | |
1209 | const uint8_t* p = &start[lazyBindingInfoOffset]; | |
1210 | while ( !done && (p < end) ) { | |
1211 | uint8_t immediate = *p & BIND_IMMEDIATE_MASK; | |
1212 | uint8_t opcode = *p & BIND_OPCODE_MASK; | |
1213 | ++p; | |
1214 | switch (opcode) { | |
1215 | case BIND_OPCODE_DONE: | |
1216 | done = true; | |
1217 | break; | |
1218 | case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: | |
1219 | libraryOrdinal = immediate; | |
1220 | break; | |
1221 | case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: | |
1222 | libraryOrdinal = read_uleb128(p, end); | |
1223 | break; | |
1224 | case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: | |
1225 | // the special ordinals are negative numbers | |
1226 | if ( immediate == 0 ) | |
1227 | libraryOrdinal = 0; | |
1228 | else { | |
1229 | int8_t signExtended = BIND_OPCODE_MASK | immediate; | |
1230 | libraryOrdinal = signExtended; | |
1231 | } | |
1232 | break; | |
1233 | case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: | |
1234 | symbolName = (char*)p; | |
1235 | symboFlags = immediate; | |
1236 | while (*p != '\0') | |
1237 | ++p; | |
1238 | ++p; | |
1239 | break; | |
1240 | case BIND_OPCODE_SET_TYPE_IMM: | |
1241 | type = immediate; | |
1242 | break; | |
1243 | case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: | |
19894a12 A |
1244 | if ( immediate >= fSegmentsCount ) |
1245 | dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is too large (0..%d)", | |
1246 | immediate, fSegmentsCount-1); | |
39a8cd10 A |
1247 | address = segActualLoadAddress(immediate) + read_uleb128(p, end); |
1248 | break; | |
1249 | case BIND_OPCODE_DO_BIND: | |
412ebb8e A |
1250 | |
1251 | ||
1252 | result = this->bindAt(context, address, type, symbolName, 0, 0, libraryOrdinal, "lazy ", NULL, true); | |
39a8cd10 A |
1253 | break; |
1254 | case BIND_OPCODE_SET_ADDEND_SLEB: | |
1255 | case BIND_OPCODE_ADD_ADDR_ULEB: | |
1256 | case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: | |
1257 | case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: | |
1258 | case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: | |
1259 | default: | |
1260 | dyld::throwf("bad lazy bind opcode %d", *p); | |
1261 | } | |
1262 | } | |
412ebb8e A |
1263 | |
1264 | if ( !this->usesTwoLevelNameSpace() ) { | |
1265 | // release dyld global lock | |
1266 | if ( unlock != NULL ) | |
1267 | unlock(); | |
1268 | } | |
39a8cd10 A |
1269 | return result; |
1270 | } | |
1271 | ||
1272 | void ImageLoaderMachOCompressed::initializeCoalIterator(CoalIterator& it, unsigned int loadOrder) | |
1273 | { | |
1274 | it.image = this; | |
1275 | it.symbolName = " "; | |
1276 | it.loadOrder = loadOrder; | |
1277 | it.weakSymbol = false; | |
1278 | it.symbolMatches = false; | |
1279 | it.done = false; | |
1280 | it.curIndex = 0; | |
1281 | it.endIndex = this->fDyldInfo->weak_bind_size; | |
1282 | it.address = 0; | |
1283 | it.type = 0; | |
1284 | it.addend = 0; | |
1285 | } | |
1286 | ||
1287 | ||
1288 | bool ImageLoaderMachOCompressed::incrementCoalIterator(CoalIterator& it) | |
1289 | { | |
1290 | if ( it.done ) | |
1291 | return false; | |
1292 | ||
1293 | if ( this->fDyldInfo->weak_bind_size == 0 ) { | |
1294 | /// hmmm, ld set MH_WEAK_DEFINES or MH_BINDS_TO_WEAK, but there is no weak binding info | |
1295 | it.done = true; | |
1296 | it.symbolName = "~~~"; | |
1297 | return true; | |
1298 | } | |
1299 | const uint8_t* start = fLinkEditBase + fDyldInfo->weak_bind_off; | |
1300 | const uint8_t* p = start + it.curIndex; | |
1301 | const uint8_t* end = fLinkEditBase + fDyldInfo->weak_bind_off + this->fDyldInfo->weak_bind_size; | |
19894a12 A |
1302 | uintptr_t count; |
1303 | uintptr_t skip; | |
39a8cd10 A |
1304 | while ( p < end ) { |
1305 | uint8_t immediate = *p & BIND_IMMEDIATE_MASK; | |
1306 | uint8_t opcode = *p & BIND_OPCODE_MASK; | |
1307 | ++p; | |
1308 | switch (opcode) { | |
1309 | case BIND_OPCODE_DONE: | |
1310 | it.done = true; | |
1311 | it.curIndex = p - start; | |
1312 | it.symbolName = "~~~"; // sorts to end | |
1313 | return true; | |
1314 | case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: | |
1315 | it.symbolName = (char*)p; | |
1316 | it.weakSymbol = ((immediate & BIND_SYMBOL_FLAGS_NON_WEAK_DEFINITION) == 0); | |
1317 | it.symbolMatches = false; | |
1318 | while (*p != '\0') | |
1319 | ++p; | |
1320 | ++p; | |
1321 | it.curIndex = p - start; | |
1322 | return false; | |
1323 | case BIND_OPCODE_SET_TYPE_IMM: | |
1324 | it.type = immediate; | |
1325 | break; | |
1326 | case BIND_OPCODE_SET_ADDEND_SLEB: | |
1327 | it.addend = read_sleb128(p, end); | |
1328 | break; | |
1329 | case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: | |
19894a12 A |
1330 | if ( immediate >= fSegmentsCount ) |
1331 | dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is too large (0..%d)", | |
1332 | immediate, fSegmentsCount-1); | |
39a8cd10 A |
1333 | it.address = segActualLoadAddress(immediate) + read_uleb128(p, end); |
1334 | break; | |
1335 | case BIND_OPCODE_ADD_ADDR_ULEB: | |
1336 | it.address += read_uleb128(p, end); | |
1337 | break; | |
1338 | case BIND_OPCODE_DO_BIND: | |
1339 | it.address += sizeof(intptr_t); | |
1340 | break; | |
1341 | case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: | |
1342 | it.address += read_uleb128(p, end) + sizeof(intptr_t); | |
1343 | break; | |
1344 | case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: | |
1345 | it.address += immediate*sizeof(intptr_t) + sizeof(intptr_t); | |
1346 | break; | |
1347 | case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: | |
1348 | count = read_uleb128(p, end); | |
1349 | skip = read_uleb128(p, end); | |
1350 | for (uint32_t i=0; i < count; ++i) { | |
1351 | it.address += skip + sizeof(intptr_t); | |
1352 | } | |
1353 | break; | |
1354 | default: | |
2fd3f4e8 | 1355 | dyld::throwf("bad weak bind opcode '%d' found after processing %d bytes in '%s'", *p, (int)(p-start), this->getPath()); |
39a8cd10 A |
1356 | } |
1357 | } | |
1358 | /// hmmm, BIND_OPCODE_DONE is missing... | |
1359 | it.done = true; | |
1360 | it.symbolName = "~~~"; | |
1361 | //dyld::log("missing BIND_OPCODE_DONE for image %s\n", this->getPath()); | |
1362 | return true; | |
1363 | } | |
1364 | ||
1365 | uintptr_t ImageLoaderMachOCompressed::getAddressCoalIterator(CoalIterator& it, const LinkContext& context) | |
1366 | { | |
1367 | //dyld::log("looking for %s in %s\n", it.symbolName, this->getPath()); | |
1368 | const ImageLoader* foundIn = NULL; | |
1369 | const ImageLoader::Symbol* sym = this->findExportedSymbol(it.symbolName, &foundIn); | |
1370 | if ( sym != NULL ) { | |
1371 | //dyld::log("sym=%p, foundIn=%p\n", sym, foundIn); | |
1372 | return foundIn->getExportedSymbolAddress(sym, context, this); | |
1373 | } | |
1374 | return 0; | |
1375 | } | |
1376 | ||
1377 | ||
1378 | void ImageLoaderMachOCompressed::updateUsesCoalIterator(CoalIterator& it, uintptr_t value, ImageLoader* targetImage, const LinkContext& context) | |
1379 | { | |
1380 | // <rdar://problem/6570879> weak binding done too early with inserted libraries | |
1381 | if ( this->getState() < dyld_image_state_bound ) | |
1382 | return; | |
1383 | ||
1384 | const uint8_t* start = fLinkEditBase + fDyldInfo->weak_bind_off; | |
1385 | const uint8_t* p = start + it.curIndex; | |
1386 | const uint8_t* end = fLinkEditBase + fDyldInfo->weak_bind_off + this->fDyldInfo->weak_bind_size; | |
1387 | ||
1388 | uint8_t type = it.type; | |
1389 | uintptr_t address = it.address; | |
1390 | const char* symbolName = it.symbolName; | |
1391 | intptr_t addend = it.addend; | |
19894a12 A |
1392 | uintptr_t count; |
1393 | uintptr_t skip; | |
39a8cd10 A |
1394 | bool done = false; |
1395 | bool boundSomething = false; | |
1396 | while ( !done && (p < end) ) { | |
1397 | uint8_t immediate = *p & BIND_IMMEDIATE_MASK; | |
1398 | uint8_t opcode = *p & BIND_OPCODE_MASK; | |
1399 | ++p; | |
1400 | switch (opcode) { | |
1401 | case BIND_OPCODE_DONE: | |
1402 | done = true; | |
1403 | break; | |
1404 | case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: | |
1405 | done = true; | |
1406 | break; | |
1407 | case BIND_OPCODE_SET_TYPE_IMM: | |
1408 | type = immediate; | |
1409 | break; | |
1410 | case BIND_OPCODE_SET_ADDEND_SLEB: | |
1411 | addend = read_sleb128(p, end); | |
1412 | break; | |
1413 | case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: | |
19894a12 A |
1414 | if ( immediate >= fSegmentsCount ) |
1415 | dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is too large (0..%d)", | |
1416 | immediate, fSegmentsCount-1); | |
39a8cd10 A |
1417 | address = segActualLoadAddress(immediate) + read_uleb128(p, end); |
1418 | break; | |
1419 | case BIND_OPCODE_ADD_ADDR_ULEB: | |
1420 | address += read_uleb128(p, end); | |
1421 | break; | |
1422 | case BIND_OPCODE_DO_BIND: | |
1423 | bindLocation(context, address, value, targetImage, type, symbolName, addend, "weak "); | |
1424 | boundSomething = true; | |
1425 | address += sizeof(intptr_t); | |
1426 | break; | |
1427 | case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: | |
1428 | bindLocation(context, address, value, targetImage, type, symbolName, addend, "weak "); | |
1429 | boundSomething = true; | |
1430 | address += read_uleb128(p, end) + sizeof(intptr_t); | |
1431 | break; | |
1432 | case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: | |
1433 | bindLocation(context, address, value, targetImage, type, symbolName, addend, "weak "); | |
1434 | boundSomething = true; | |
1435 | address += immediate*sizeof(intptr_t) + sizeof(intptr_t); | |
1436 | break; | |
1437 | case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: | |
1438 | count = read_uleb128(p, end); | |
1439 | skip = read_uleb128(p, end); | |
1440 | for (uint32_t i=0; i < count; ++i) { | |
1441 | bindLocation(context, address, value, targetImage, type, symbolName, addend, "weak "); | |
1442 | boundSomething = true; | |
1443 | address += skip + sizeof(intptr_t); | |
1444 | } | |
1445 | break; | |
1446 | default: | |
1447 | dyld::throwf("bad bind opcode %d in weak binding info", *p); | |
1448 | } | |
2fd3f4e8 A |
1449 | } |
1450 | // C++ weak coalescing cannot be tracked by reference counting. Error on side of never unloading. | |
1451 | if ( boundSomething && (targetImage != this) ) | |
1452 | context.addDynamicReference(this, targetImage); | |
39a8cd10 A |
1453 | } |
1454 | ||
412ebb8e | 1455 | uintptr_t ImageLoaderMachOCompressed::interposeAt(const LinkContext& context, uintptr_t addr, uint8_t type, const char*, |
19894a12 | 1456 | uint8_t, intptr_t, long, const char*, LastLookup*, bool runResolver) |
412ebb8e A |
1457 | { |
1458 | if ( type == BIND_TYPE_POINTER ) { | |
1459 | uintptr_t* fixupLocation = (uintptr_t*)addr; | |
19894a12 A |
1460 | uintptr_t curValue = *fixupLocation; |
1461 | uintptr_t newValue = interposedAddress(context, curValue, this); | |
1462 | if ( newValue != curValue) | |
1463 | *fixupLocation = newValue; | |
412ebb8e A |
1464 | } |
1465 | return 0; | |
1466 | } | |
1467 | ||
1468 | void ImageLoaderMachOCompressed::doInterpose(const LinkContext& context) | |
1469 | { | |
1470 | if ( context.verboseInterposing ) | |
1471 | dyld::log("dyld: interposing %lu tuples onto image: %s\n", fgInterposingTuples.size(), this->getPath()); | |
1472 | ||
1473 | // update prebound symbols | |
1474 | eachBind(context, &ImageLoaderMachOCompressed::interposeAt); | |
1475 | eachLazyBind(context, &ImageLoaderMachOCompressed::interposeAt); | |
1476 | } | |
39a8cd10 A |
1477 | |
1478 | ||
19894a12 A |
1479 | uintptr_t ImageLoaderMachOCompressed::dynamicInterposeAt(const LinkContext& context, uintptr_t addr, uint8_t type, const char* symbolName, |
1480 | uint8_t, intptr_t, long, const char*, LastLookup*, bool runResolver) | |
1481 | { | |
1482 | if ( type == BIND_TYPE_POINTER ) { | |
1483 | uintptr_t* fixupLocation = (uintptr_t*)addr; | |
1484 | uintptr_t value = *fixupLocation; | |
1485 | // don't apply interposing to table entries. | |
1486 | if ( (context.dynamicInterposeArray <= (void*)addr) && ((void*)addr < &context.dynamicInterposeArray[context.dynamicInterposeCount]) ) | |
1487 | return 0; | |
1488 | for(size_t i=0; i < context.dynamicInterposeCount; ++i) { | |
1489 | if ( value == (uintptr_t)context.dynamicInterposeArray[i].replacee ) { | |
1490 | if ( context.verboseInterposing ) { | |
1491 | dyld::log("dyld: dynamic interposing: at %p replace %p with %p in %s\n", | |
1492 | fixupLocation, context.dynamicInterposeArray[i].replacee, context.dynamicInterposeArray[i].replacement, this->getPath()); | |
1493 | } | |
1494 | *fixupLocation = (uintptr_t)context.dynamicInterposeArray[i].replacement; | |
1495 | } | |
1496 | } | |
1497 | } | |
1498 | return 0; | |
1499 | } | |
1500 | ||
1501 | void ImageLoaderMachOCompressed::dynamicInterpose(const LinkContext& context) | |
1502 | { | |
1503 | if ( context.verboseInterposing ) | |
1504 | dyld::log("dyld: dynamic interposing %lu tuples onto image: %s\n", context.dynamicInterposeCount, this->getPath()); | |
1505 | ||
1506 | // update already bound references to symbols | |
1507 | eachBind(context, &ImageLoaderMachOCompressed::dynamicInterposeAt); | |
1508 | eachLazyBind(context, &ImageLoaderMachOCompressed::dynamicInterposeAt); | |
1509 | } | |
39a8cd10 A |
1510 | |
1511 | ||
1512 | const char* ImageLoaderMachOCompressed::findClosestSymbol(const void* addr, const void** closestAddr) const | |
1513 | { | |
1514 | // called by dladdr() | |
1515 | // only works with compressed LINKEDIT if classic symbol table is also present | |
1516 | const macho_nlist* symbolTable = NULL; | |
1517 | const char* symbolTableStrings = NULL; | |
1518 | const dysymtab_command* dynSymbolTable = NULL; | |
1519 | const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; | |
1520 | const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; | |
1521 | const struct load_command* cmd = cmds; | |
1522 | for (uint32_t i = 0; i < cmd_count; ++i) { | |
1523 | switch (cmd->cmd) { | |
1524 | case LC_SYMTAB: | |
1525 | { | |
1526 | const struct symtab_command* symtab = (struct symtab_command*)cmd; | |
1527 | symbolTableStrings = (const char*)&fLinkEditBase[symtab->stroff]; | |
1528 | symbolTable = (macho_nlist*)(&fLinkEditBase[symtab->symoff]); | |
1529 | } | |
1530 | break; | |
1531 | case LC_DYSYMTAB: | |
1532 | dynSymbolTable = (struct dysymtab_command*)cmd; | |
1533 | break; | |
1534 | } | |
1535 | cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); | |
1536 | } | |
1537 | // no symbol table => no lookup by address | |
1538 | if ( (symbolTable == NULL) || (dynSymbolTable == NULL) ) | |
1539 | return NULL; | |
1540 | ||
1541 | uintptr_t targetAddress = (uintptr_t)addr - fSlide; | |
1542 | const struct macho_nlist* bestSymbol = NULL; | |
1543 | // first walk all global symbols | |
1544 | const struct macho_nlist* const globalsStart = &symbolTable[dynSymbolTable->iextdefsym]; | |
1545 | const struct macho_nlist* const globalsEnd= &globalsStart[dynSymbolTable->nextdefsym]; | |
1546 | for (const struct macho_nlist* s = globalsStart; s < globalsEnd; ++s) { | |
1547 | if ( (s->n_type & N_TYPE) == N_SECT ) { | |
1548 | if ( bestSymbol == NULL ) { | |
1549 | if ( s->n_value <= targetAddress ) | |
1550 | bestSymbol = s; | |
1551 | } | |
1552 | else if ( (s->n_value <= targetAddress) && (bestSymbol->n_value < s->n_value) ) { | |
1553 | bestSymbol = s; | |
1554 | } | |
1555 | } | |
1556 | } | |
1557 | // next walk all local symbols | |
1558 | const struct macho_nlist* const localsStart = &symbolTable[dynSymbolTable->ilocalsym]; | |
1559 | const struct macho_nlist* const localsEnd= &localsStart[dynSymbolTable->nlocalsym]; | |
1560 | for (const struct macho_nlist* s = localsStart; s < localsEnd; ++s) { | |
1561 | if ( ((s->n_type & N_TYPE) == N_SECT) && ((s->n_type & N_STAB) == 0) ) { | |
1562 | if ( bestSymbol == NULL ) { | |
1563 | if ( s->n_value <= targetAddress ) | |
1564 | bestSymbol = s; | |
1565 | } | |
1566 | else if ( (s->n_value <= targetAddress) && (bestSymbol->n_value < s->n_value) ) { | |
1567 | bestSymbol = s; | |
1568 | } | |
1569 | } | |
1570 | } | |
1571 | if ( bestSymbol != NULL ) { | |
412ebb8e A |
1572 | #if __arm__ |
1573 | if (bestSymbol->n_desc & N_ARM_THUMB_DEF) | |
1574 | *closestAddr = (void*)((bestSymbol->n_value | 1) + fSlide); | |
1575 | else | |
1576 | *closestAddr = (void*)(bestSymbol->n_value + fSlide); | |
1577 | #else | |
39a8cd10 | 1578 | *closestAddr = (void*)(bestSymbol->n_value + fSlide); |
412ebb8e | 1579 | #endif |
39a8cd10 A |
1580 | return &symbolTableStrings[bestSymbol->n_un.n_strx]; |
1581 | } | |
1582 | return NULL; | |
1583 | } | |
1584 | ||
1585 | ||
1586 | #if PREBOUND_IMAGE_SUPPORT | |
1587 | void ImageLoaderMachOCompressed::resetPreboundLazyPointers(const LinkContext& context) | |
1588 | { | |
1589 | // no way to back off a prebound compress image | |
1590 | } | |
1591 | #endif | |
1592 | ||
412ebb8e A |
1593 | |
1594 | #if __arm__ || __x86_64__ | |
2fd3f4e8 | 1595 | void ImageLoaderMachOCompressed::updateAlternateLazyPointer(uint8_t* stub, void** originalLazyPointerAddr, const LinkContext& context) |
412ebb8e A |
1596 | { |
1597 | #if __arm__ | |
1598 | uint32_t* instructions = (uint32_t*)stub; | |
1599 | // sanity check this is a stub we understand | |
1600 | if ( (instructions[0] != 0xe59fc004) || (instructions[1] != 0xe08fc00c) || (instructions[2] != 0xe59cf000) ) | |
1601 | return; | |
1602 | ||
1603 | void** lazyPointerAddr = (void**)(instructions[3] + (stub + 12)); | |
1604 | #endif | |
1605 | #if __x86_64__ | |
1606 | // sanity check this is a stub we understand | |
1607 | if ( (stub[0] != 0xFF) || (stub[1] != 0x25) ) | |
1608 | return; | |
1609 | int32_t ripOffset = *((int32_t*)(&stub[2])); | |
1610 | void** lazyPointerAddr = (void**)(ripOffset + stub + 6); | |
1611 | #endif | |
1612 | ||
1613 | // if stub does not use original lazy pointer (meaning it was optimized by update_dyld_shared_cache) | |
1614 | if ( lazyPointerAddr != originalLazyPointerAddr ) { | |
2fd3f4e8 A |
1615 | // <rdar://problem/12928448> only de-optimization lazy pointers if they are part of shared cache not loaded (because overridden) |
1616 | const ImageLoader* lazyPointerImage = context.findImageContainingAddress(lazyPointerAddr); | |
1617 | if ( lazyPointerImage != NULL ) | |
1618 | return; | |
1619 | ||
412ebb8e A |
1620 | // copy newly re-bound lazy pointer value to shared lazy pointer |
1621 | *lazyPointerAddr = *originalLazyPointerAddr; | |
2fd3f4e8 A |
1622 | |
1623 | if ( context.verboseBind ) | |
1624 | dyld::log("dyld: alter bind: %s: *0x%08lX = 0x%08lX \n", | |
1625 | this->getShortName(), (long)lazyPointerAddr, (long)*originalLazyPointerAddr); | |
412ebb8e A |
1626 | } |
1627 | } | |
1628 | #endif | |
1629 | ||
1630 | ||
1631 | // <rdar://problem/8890875> overriding shared cache dylibs with resolvers fails | |
1632 | void ImageLoaderMachOCompressed::updateOptimizedLazyPointers(const LinkContext& context) | |
1633 | { | |
1634 | #if __arm__ || __x86_64__ | |
1635 | // find stubs and lazy pointer sections | |
1636 | const struct macho_section* stubsSection = NULL; | |
1637 | const struct macho_section* lazyPointerSection = NULL; | |
1638 | const dysymtab_command* dynSymbolTable = NULL; | |
1639 | const macho_header* mh = (macho_header*)fMachOData; | |
1640 | const uint32_t cmd_count = mh->ncmds; | |
1641 | const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; | |
1642 | const struct load_command* cmd = cmds; | |
1643 | for (uint32_t i = 0; i < cmd_count; ++i) { | |
1644 | if (cmd->cmd == LC_SEGMENT_COMMAND) { | |
1645 | const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; | |
1646 | const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command)); | |
1647 | const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects]; | |
1648 | for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { | |
1649 | const uint8_t type = sect->flags & SECTION_TYPE; | |
1650 | if ( type == S_SYMBOL_STUBS ) | |
1651 | stubsSection = sect; | |
1652 | else if ( type == S_LAZY_SYMBOL_POINTERS ) | |
1653 | lazyPointerSection = sect; | |
1654 | } | |
1655 | } | |
1656 | else if ( cmd->cmd == LC_DYSYMTAB ) { | |
1657 | dynSymbolTable = (struct dysymtab_command*)cmd; | |
1658 | } | |
1659 | cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); | |
1660 | } | |
1661 | ||
1662 | // sanity check | |
1663 | if ( dynSymbolTable == NULL ) | |
1664 | return; | |
1665 | if ( (stubsSection == NULL) || (lazyPointerSection == NULL) ) | |
1666 | return; | |
1667 | const uint32_t stubsCount = stubsSection->size / stubsSection->reserved2; | |
1668 | const uint32_t lazyPointersCount = lazyPointerSection->size / sizeof(void*); | |
1669 | if ( stubsCount != lazyPointersCount ) | |
1670 | return; | |
1671 | const uint32_t stubsIndirectTableOffset = stubsSection->reserved1; | |
1672 | const uint32_t lazyPointersIndirectTableOffset = lazyPointerSection->reserved1; | |
1673 | if ( (stubsIndirectTableOffset+stubsCount) > dynSymbolTable->nindirectsyms ) | |
1674 | return; | |
1675 | if ( (lazyPointersIndirectTableOffset+lazyPointersCount) > dynSymbolTable->nindirectsyms ) | |
1676 | return; | |
1677 | ||
1678 | // walk stubs and lazy pointers | |
1679 | const uint32_t* const indirectTable = (uint32_t*)&fLinkEditBase[dynSymbolTable->indirectsymoff]; | |
1680 | void** const lazyPointersStartAddr = (void**)(lazyPointerSection->addr + this->fSlide); | |
1681 | uint8_t* const stubsStartAddr = (uint8_t*)(stubsSection->addr + this->fSlide); | |
1682 | uint8_t* stub = stubsStartAddr; | |
1683 | void** lpa = lazyPointersStartAddr; | |
1684 | for(uint32_t i=0; i < stubsCount; ++i, stub += stubsSection->reserved2, ++lpa) { | |
1685 | // sanity check symbol index of stub and lazy pointer match | |
1686 | if ( indirectTable[stubsIndirectTableOffset+i] != indirectTable[lazyPointersIndirectTableOffset+i] ) | |
1687 | continue; | |
2fd3f4e8 | 1688 | this->updateAlternateLazyPointer(stub, lpa, context); |
412ebb8e A |
1689 | } |
1690 | ||
1691 | #endif | |
1692 | } | |
1693 | ||
1694 |