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