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#ifndef EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE 42 #define EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE 0x02 43#endif 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 63static 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 73 if (bit > 63) 74 dyld::throwf("uleb128 too big for uint64, bit=%d, result=0x%0llX", bit, result); 75 else { 76 result |= (slice << bit); 77 bit += 7; 78 } 79 } while (*p++ & 0x80); 80 return result; 81} 82 83 84static 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++; 93 result |= (((int64_t)(byte & 0x7f)) << bit); 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 104ImageLoaderMachOCompressed* 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 113 if ( slide != 0 ) 114 fgNextPIEDylibAddress = (uintptr_t)image->getEnd(); 115 116 image->instantiateFinish(context); 117 image->setMapped(context); 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 134ImageLoaderMachOCompressed* ImageLoaderMachOCompressed::instantiateFromFile(const char* path, int fd, const uint8_t* fileData, 135 uint64_t offsetInFat, uint64_t lenInFat, const struct stat& info, 136 unsigned int segCount, unsigned int libCount, 137 const struct linkedit_data_command* codeSigCmd, const LinkContext& context) 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 145 // if this image is code signed, let kernel validate signature before mapping any pages from image 146 image->loadCodeSignature(codeSigCmd, fd, offsetInFat, context); 147 148 // mmap segments 149 image->mapSegments(fd, offsetInFat, lenInFat, info.st_size, context); 150 151 // probe to see if code signed correctly 152 image->crashIfInvalidCodeSignature(); 153 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 166 else if ( (path[0] != '/') || (strstr(path, "../") != NULL) ) { 167 // rdar://problem/10733082 Fix up @rpath based paths during introspection 168 // rdar://problem/5135363 turn relative paths into absolute paths so gdb, Symbolication can later find them 169 char realPath[MAXPATHLEN]; 170 if ( fcntl(fd, F_GETPATH, realPath) == 0 ) 171 image->setPaths(path, realPath); 172 else 173 image->setPath(path); 174 } 175 else 176 image->setPath(path); 177 178 // make sure path is stable before recording in dyld_all_image_infos 179 image->setMapped(context); 180 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 199ImageLoaderMachOCompressed* 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) 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(); 211 image->setSlide(slide); 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); 222 image->setMapped(context); 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 235ImageLoaderMachOCompressed* 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); 255 image->setMapped(context); 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 268ImageLoaderMachOCompressed::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 274ImageLoaderMachOCompressed::~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 283ImageLoaderMachOCompressed* 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 297void ImageLoaderMachOCompressed::instantiateFinish(const LinkContext& context) 298{ 299 // now that segments are mapped in, get real fMachOData, fLinkEditBase, and fSlide 300 this->parseLoadCmds(); 301} 302 303uint32_t* ImageLoaderMachOCompressed::segmentCommandOffsets() const 304{ 305 return ((uint32_t*)(((uint8_t*)this) + sizeof(ImageLoaderMachOCompressed))); 306} 307 308 309ImageLoader* ImageLoaderMachOCompressed::libImage(unsigned int libIndex) const 310{ 311 const uintptr_t* images = ((uintptr_t*)(((uint8_t*)this) + sizeof(ImageLoaderMachOCompressed) + fSegmentsCount*sizeof(uint32_t))); 312 // mask off low bits 313 return (ImageLoader*)(images[libIndex] & (-4)); 314} 315 316bool 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 323bool 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 330 331void ImageLoaderMachOCompressed::setLibImage(unsigned int libIndex, ImageLoader* image, bool reExported, bool upward) 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; 337 if ( upward ) 338 value |= 2; 339 images[libIndex] = value; 340} 341 342 343void ImageLoaderMachOCompressed::markFreeLINKEDIT(const LinkContext& context) 344{ 345 // mark that we are done with rebase and bind info 346 markLINKEDIT(context, MADV_FREE); 347} 348 349void ImageLoaderMachOCompressed::markSequentialLINKEDIT(const LinkContext& context) 350{ 351 // mark the rebase and bind info and using sequential access 352 markLINKEDIT(context, MADV_SEQUENTIAL); 353} 354 355void 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 377 start = start & (-4096); 378 end = (end + 4095) & (-4096); 379 380 // do nothing if only one page of rebase/bind info 381 if ( (end-start) <= 4096 ) 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"; 390 dyld::log("%18s %s 0x%0lX -> 0x%0lX for %s\n", "__LINKEDIT", adstr, start, end-1, this->getPath()); 391 } 392} 393 394 395 396void ImageLoaderMachOCompressed::rebaseAt(const LinkContext& context, uintptr_t addr, uintptr_t slide, uint8_t type) 397{ 398 if ( context.verboseRebase ) { 399 dyld::log("dyld: rebase: %s:*0x%08lX += 0x%08lX\n", this->getShortName(), (uintptr_t)addr, slide); 400 } 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 415void 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 423void ImageLoaderMachOCompressed::rebase(const LinkContext& context) 424{ 425 CRSetCrashLogMessage2(this->getPath()); 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); 436 uint32_t count; 437 uint32_t skip; 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; 452 if ( segmentIndex > fSegmentsCount ) 453 dyld::throwf("REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is too large (%d)\n", 454 segmentIndex, fSegmentsCount); 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 } 511 CRSetCrashLogMessage2(NULL); 512} 513 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// 518const uint8_t* ImageLoaderMachOCompressed::trieWalk(const uint8_t* start, const uint8_t* end, const char* s) 519{ 520 const uint8_t* p = start; 521 while ( p != NULL ) { 522 uint32_t terminalSize = *p++; 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 } 528 if ( (*s == '\0') && (terminalSize != 0) ) { 529 //dyld::log("trieWalk(%p) returning %p\n", start, p); 530 return p; 531 } 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; 536 uint32_t nodeOffset = 0; 537 for (; childrenRemaining > 0; --childrenRemaining) { 538 const char* ss = s; 539 //dyld::log("trieWalk(%p) child str=%s\n", start, (char*)p); 540 bool wrongEdge = false; 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 543 char c = *p; 544 while ( c != '\0' ) { 545 if ( !wrongEdge ) { 546 if ( c != *ss ) 547 wrongEdge = true; 548 ++ss; 549 } 550 ++p; 551 c = *p; 552 } 553 if ( wrongEdge ) { 554 // advance to next child 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 560 } 561 else { 562 // the symbol so far matches this edge (child) 563 // so advance to the child's node 564 ++p; 565 nodeOffset = read_uleb128(p, end); 566 s = ss; 567 //dyld::log("trieWalk() found matching edge advancing to node 0x%x\n", nodeOffset); 568 break; 569 } 570 } 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 581const 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; 595 const uint32_t flags = read_uleb128(p, end); 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 599 const uint32_t ordinal = read_uleb128(p, end); 600 const char* importedName = (char*)p; 601 if ( importedName[0] == '\0' ) 602 importedName = symbol; 603 if ( (ordinal > 0) && (ordinal <= libraryCount()) ) { 604 const ImageLoader* reexportedFrom = libImage(ordinal-1); 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 } 613 else { 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; 619 } 620 } 621 return NULL; 622} 623 624 625bool 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 633uintptr_t ImageLoaderMachOCompressed::exportedSymbolAddress(const LinkContext& context, const Symbol* symbol, const ImageLoader* requestor, bool runResolver) const 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"; 640 //dyld::log("exportedSymbolAddress(): node=%p, nodeOffset=0x%04X in %s\n", symbol, (int)((uint8_t*)symbol - exportTrieStart), this->getShortName()); 641 uint32_t flags = read_uleb128(exportNode, exportTrieEnd); 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 648 for (std::vector<InterposeTuple>::iterator it=fgInterposingTuples.begin(); it != fgInterposingTuples.end(); it++) { 649 // replace all references to 'replacee' with 'replacement' 650 if ( (stub == it->replacee) && (requestor != it->replacementImage) ) { 651 if ( context.verboseInterposing ) { 652 dyld::log("dyld interposing: lazy replace 0x%lX with 0x%lX from %s\n", 653 it->replacee, it->replacement, this->getPath()); 654 } 655 return it->replacement; 656 } 657 } 658 typedef uintptr_t (*ResolverProc)(void); 659 ResolverProc resolver = (ResolverProc)(read_uleb128(exportNode, exportTrieEnd) + (uintptr_t)fMachOData); 660 uintptr_t result = (*resolver)(); 661 if ( context.verboseBind ) 662 dyld::log("dyld: resolver at %p returned 0x%08lX\n", resolver, result); 663 return result; 664 } 665 return read_uleb128(exportNode, exportTrieEnd) + (uintptr_t)fMachOData; 666 case EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL: 667 if ( flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER ) 668 dyld::throwf("unsupported exported symbol kind. flags=%d at node=%p", flags, symbol); 669 return read_uleb128(exportNode, exportTrieEnd) + (uintptr_t)fMachOData; 670 case EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE: 671 if ( flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER ) 672 dyld::throwf("unsupported exported symbol kind. flags=%d at node=%p", flags, symbol); 673 return read_uleb128(exportNode, exportTrieEnd); 674 default: 675 dyld::throwf("unsupported exported symbol kind. flags=%d at node=%p", flags, symbol); 676 } 677} 678 679bool ImageLoaderMachOCompressed::exportedSymbolIsWeakDefintion(const Symbol* symbol) const 680{ 681 const uint8_t* exportNode = (uint8_t*)symbol; 682 const uint8_t* exportTrieStart = fLinkEditBase + fDyldInfo->export_off; 683 const uint8_t* exportTrieEnd = exportTrieStart + fDyldInfo->export_size; 684 if ( (exportNode < exportTrieStart) || (exportNode > exportTrieEnd) ) 685 throw "symbol is not in trie"; 686 uint32_t flags = read_uleb128(exportNode, exportTrieEnd); 687 return ( flags & EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION ); 688} 689 690 691const char* ImageLoaderMachOCompressed::exportedSymbolName(const Symbol* symbol) const 692{ 693 throw "NSNameOfSymbol() not supported with compressed LINKEDIT"; 694} 695 696unsigned int ImageLoaderMachOCompressed::exportedSymbolCount() const 697{ 698 throw "NSSymbolDefinitionCountInObjectFileImage() not supported with compressed LINKEDIT"; 699} 700 701const ImageLoader::Symbol* ImageLoaderMachOCompressed::exportedSymbolIndexed(unsigned int index) const 702{ 703 throw "NSSymbolDefinitionNameInObjectFileImage() not supported with compressed LINKEDIT"; 704} 705 706unsigned int ImageLoaderMachOCompressed::importedSymbolCount() const 707{ 708 throw "NSSymbolReferenceCountInObjectFileImage() not supported with compressed LINKEDIT"; 709} 710 711const ImageLoader::Symbol* ImageLoaderMachOCompressed::importedSymbolIndexed(unsigned int index) const 712{ 713 throw "NSSymbolReferenceCountInObjectFileImage() not supported with compressed LINKEDIT"; 714} 715 716const char* ImageLoaderMachOCompressed::importedSymbolName(const Symbol* symbol) const 717{ 718 throw "NSSymbolReferenceNameInObjectFileImage() not supported with compressed LINKEDIT"; 719} 720 721 722 723uintptr_t ImageLoaderMachOCompressed::resolveFlat(const LinkContext& context, const char* symbolName, bool weak_import, 724 bool runResolver, const ImageLoader** foundIn) 725{ 726 const Symbol* sym; 727 if ( context.flatExportFinder(symbolName, &sym, foundIn) ) { 728 if ( *foundIn != this ) 729 context.addDynamicReference(this, const_cast<ImageLoader*>(*foundIn)); 730 return (*foundIn)->getExportedSymbolAddress(sym, context, this, runResolver); 731 } 732 // if a bundle is loaded privately the above will not find its exports 733 if ( this->isBundle() && this->hasHiddenExports() ) { 734 // look in self for needed symbol 735 sym = this->ImageLoaderMachO::findExportedSymbol(symbolName, false, foundIn); 736 if ( sym != NULL ) 737 return (*foundIn)->getExportedSymbolAddress(sym, context, this, runResolver); 738 } 739 if ( weak_import ) { 740 // definition can't be found anywhere, ok because it is weak, just return 0 741 return 0; 742 } 743 throwSymbolNotFound(context, symbolName, this->getPath(), "flat namespace"); 744} 745 746 747uintptr_t ImageLoaderMachOCompressed::resolveTwolevel(const LinkContext& context, const ImageLoader* targetImage, bool weak_import, 748 const char* symbolName, bool runResolver, const ImageLoader** foundIn) 749{ 750 // two level lookup 751 const Symbol* sym = targetImage->findExportedSymbol(symbolName, true, foundIn); 752 if ( sym != NULL ) { 753 return (*foundIn)->getExportedSymbolAddress(sym, context, this, runResolver); 754 } 755 756 if ( weak_import ) { 757 // definition can't be found anywhere, ok because it is weak, just return 0 758 return 0; 759 } 760 761 // nowhere to be found 762 throwSymbolNotFound(context, symbolName, this->getPath(), targetImage->getPath()); 763} 764 765 766uintptr_t ImageLoaderMachOCompressed::resolve(const LinkContext& context, const char* symbolName, 767 uint8_t symboFlags, int libraryOrdinal, const ImageLoader** targetImage, 768 LastLookup* last, bool runResolver) 769{ 770 *targetImage = NULL; 771 772 // only clients that benefit from caching last lookup pass in a LastLookup struct 773 if ( last != NULL ) { 774 if ( (last->ordinal == libraryOrdinal) 775 && (last->flags == symboFlags) 776 && (last->name == symbolName) ) { 777 *targetImage = last->foundIn; 778 return last->result; 779 } 780 } 781 782 bool weak_import = (symboFlags & BIND_SYMBOL_FLAGS_WEAK_IMPORT); 783 uintptr_t symbolAddress; 784 if ( context.bindFlat || (libraryOrdinal == BIND_SPECIAL_DYLIB_FLAT_LOOKUP) ) { 785 symbolAddress = this->resolveFlat(context, symbolName, weak_import, runResolver, targetImage); 786 } 787 else { 788 if ( libraryOrdinal == BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE ) { 789 *targetImage = context.mainExecutable; 790 } 791 else if ( libraryOrdinal == BIND_SPECIAL_DYLIB_SELF ) { 792 *targetImage = this; 793 } 794 else if ( libraryOrdinal <= 0 ) { 795 dyld::throwf("bad mach-o binary, unknown special library ordinal (%u) too big for symbol %s in %s", 796 libraryOrdinal, symbolName, this->getPath()); 797 } 798 else if ( (unsigned)libraryOrdinal <= libraryCount() ) { 799 *targetImage = libImage(libraryOrdinal-1); 800 } 801 else { 802 dyld::throwf("bad mach-o binary, library ordinal (%u) too big (max %u) for symbol %s in %s", 803 libraryOrdinal, libraryCount(), symbolName, this->getPath()); 804 } 805 if ( *targetImage == NULL ) { 806 if ( weak_import ) { 807 // if target library not loaded and reference is weak or library is weak return 0 808 symbolAddress = 0; 809 } 810 else { 811 dyld::throwf("can't resolve symbol %s in %s because dependent dylib #%d could not be loaded", 812 symbolName, this->getPath(), libraryOrdinal); 813 } 814 } 815 else { 816 symbolAddress = resolveTwolevel(context, *targetImage, weak_import, symbolName, runResolver, targetImage); 817 } 818 } 819 820 // save off lookup results if client wants 821 if ( last != NULL ) { 822 last->ordinal = libraryOrdinal; 823 last->flags = symboFlags; 824 last->name = symbolName; 825 last->foundIn = *targetImage; 826 last->result = symbolAddress; 827 } 828 829 return symbolAddress; 830} 831 832uintptr_t ImageLoaderMachOCompressed::bindAt(const LinkContext& context, uintptr_t addr, uint8_t type, const char* symbolName, 833 uint8_t symboFlags, intptr_t addend, int libraryOrdinal, const char* msg, 834 LastLookup* last, bool runResolver) 835{ 836 const ImageLoader* targetImage; 837 uintptr_t symbolAddress; 838 839 // resolve symbol 840 symbolAddress = this->resolve(context, symbolName, symboFlags, libraryOrdinal, &targetImage, last, runResolver); 841 842 // do actual update 843 return this->bindLocation(context, addr, symbolAddress, targetImage, type, symbolName, addend, msg); 844} 845 846void ImageLoaderMachOCompressed::throwBadBindingAddress(uintptr_t address, uintptr_t segmentEndAddress, int segmentIndex, 847 const uint8_t* startOpcodes, const uint8_t* endOpcodes, const uint8_t* pos) 848{ 849 dyld::throwf("malformed binding opcodes (%ld/%ld): address 0x%08lX is beyond end of segment %s (0x%08lX -> 0x%08lX)", 850 (intptr_t)(pos-startOpcodes), (intptr_t)(endOpcodes-startOpcodes), address, segName(segmentIndex), 851 segActualLoadAddress(segmentIndex), segmentEndAddress); 852} 853 854 855void ImageLoaderMachOCompressed::doBind(const LinkContext& context, bool forceLazysBound) 856{ 857 CRSetCrashLogMessage2(this->getPath()); 858 859 // if prebound and loaded at prebound address, and all libraries are same as when this was prebound, then no need to bind 860 // note: flat-namespace binaries need to have imports rebound (even if correctly prebound) 861 if ( this->usablePrebinding(context) ) { 862 // don't need to bind 863 } 864 else { 865 866 #if TEXT_RELOC_SUPPORT 867 // if there are __TEXT fixups, temporarily make __TEXT writable 868 if ( fTextSegmentBinds ) 869 this->makeTextSegmentWritable(context, true); 870 #endif 871 872 // run through all binding opcodes 873 eachBind(context, &ImageLoaderMachOCompressed::bindAt); 874 875 #if TEXT_RELOC_SUPPORT 876 // if there were __TEXT fixups, restore write protection 877 if ( fTextSegmentBinds ) 878 this->makeTextSegmentWritable(context, false); 879 #endif 880 881 // if this image is in the shared cache, but depends on something no longer in the shared cache, 882 // there is no way to reset the lazy pointers, so force bind them now 883 if ( forceLazysBound || fInSharedCache ) 884 this->doBindJustLazies(context); 885 886 // this image is in cache, but something below it is not. If 887 // this image has lazy pointer to a resolver function, then 888 // the stub may have been altered to point to a shared lazy pointer. 889 if ( fInSharedCache ) 890 this->updateOptimizedLazyPointers(context); 891 892 // tell kernel we are done with chunks of LINKEDIT 893 if ( !context.preFetchDisabled ) 894 this->markFreeLINKEDIT(context); 895 } 896 897 // set up dyld entry points in image 898 // do last so flat main executables will have __dyld or __program_vars set up 899 this->setupLazyPointerHandler(context); 900 CRSetCrashLogMessage2(NULL); 901} 902 903 904void ImageLoaderMachOCompressed::doBindJustLazies(const LinkContext& context) 905{ 906 eachLazyBind(context, &ImageLoaderMachOCompressed::bindAt); 907} 908 909void ImageLoaderMachOCompressed::eachBind(const LinkContext& context, bind_handler handler) 910{ 911 try { 912 uint8_t type = 0; 913 int segmentIndex = 0; 914 uintptr_t address = segActualLoadAddress(0); 915 uintptr_t segmentEndAddress = segActualEndAddress(0); 916 const char* symbolName = NULL; 917 uint8_t symboFlags = 0; 918 int libraryOrdinal = 0; 919 intptr_t addend = 0; 920 uint32_t count; 921 uint32_t skip; 922 LastLookup last = { 0, 0, NULL, 0, NULL }; 923 const uint8_t* const start = fLinkEditBase + fDyldInfo->bind_off; 924 const uint8_t* const end = &start[fDyldInfo->bind_size]; 925 const uint8_t* p = start; 926 bool done = false; 927 while ( !done && (p < end) ) { 928 uint8_t immediate = *p & BIND_IMMEDIATE_MASK; 929 uint8_t opcode = *p & BIND_OPCODE_MASK; 930 ++p; 931 switch (opcode) { 932 case BIND_OPCODE_DONE: 933 done = true; 934 break; 935 case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: 936 libraryOrdinal = immediate; 937 break; 938 case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: 939 libraryOrdinal = read_uleb128(p, end); 940 break; 941 case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: 942 // the special ordinals are negative numbers 943 if ( immediate == 0 ) 944 libraryOrdinal = 0; 945 else { 946 int8_t signExtended = BIND_OPCODE_MASK | immediate; 947 libraryOrdinal = signExtended; 948 } 949 break; 950 case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: 951 symbolName = (char*)p; 952 symboFlags = immediate; 953 while (*p != '\0') 954 ++p; 955 ++p; 956 break; 957 case BIND_OPCODE_SET_TYPE_IMM: 958 type = immediate; 959 break; 960 case BIND_OPCODE_SET_ADDEND_SLEB: 961 addend = read_sleb128(p, end); 962 break; 963 case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: 964 segmentIndex = immediate; 965 if ( segmentIndex > fSegmentsCount ) 966 dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is too large (%d)\n", 967 segmentIndex, fSegmentsCount); 968 address = segActualLoadAddress(segmentIndex) + read_uleb128(p, end); 969 segmentEndAddress = segActualEndAddress(segmentIndex); 970 break; 971 case BIND_OPCODE_ADD_ADDR_ULEB: 972 address += read_uleb128(p, end); 973 break; 974 case BIND_OPCODE_DO_BIND: 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 += sizeof(intptr_t); 979 break; 980 case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: 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 += read_uleb128(p, end) + sizeof(intptr_t); 985 break; 986 case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: 987 if ( address >= segmentEndAddress ) 988 throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p); 989 (this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "", &last, false); 990 address += immediate*sizeof(intptr_t) + sizeof(intptr_t); 991 break; 992 case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: 993 count = read_uleb128(p, end); 994 skip = read_uleb128(p, end); 995 for (uint32_t i=0; i < count; ++i) { 996 if ( address >= segmentEndAddress ) 997 throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p); 998 (this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "", &last, false); 999 address += skip + sizeof(intptr_t); 1000 } 1001 break; 1002 default: 1003 dyld::throwf("bad bind opcode %d in bind info", *p); 1004 } 1005 } 1006 } 1007 catch (const char* msg) { 1008 const char* newMsg = dyld::mkstringf("%s in %s", msg, this->getPath()); 1009 free((void*)msg); 1010 throw newMsg; 1011 } 1012} 1013 1014void ImageLoaderMachOCompressed::eachLazyBind(const LinkContext& context, bind_handler handler) 1015{ 1016 try { 1017 uint8_t type = BIND_TYPE_POINTER; 1018 int segmentIndex = 0; 1019 uintptr_t address = segActualLoadAddress(0); 1020 uintptr_t segmentEndAddress = segActualEndAddress(0); 1021 const char* symbolName = NULL; 1022 uint8_t symboFlags = 0; 1023 int libraryOrdinal = 0; 1024 intptr_t addend = 0; 1025 const uint8_t* const start = fLinkEditBase + fDyldInfo->lazy_bind_off; 1026 const uint8_t* const end = &start[fDyldInfo->lazy_bind_size]; 1027 const uint8_t* p = start; 1028 bool done = false; 1029 while ( !done && (p < end) ) { 1030 uint8_t immediate = *p & BIND_IMMEDIATE_MASK; 1031 uint8_t opcode = *p & BIND_OPCODE_MASK; 1032 ++p; 1033 switch (opcode) { 1034 case BIND_OPCODE_DONE: 1035 // there is BIND_OPCODE_DONE at end of each lazy bind, don't stop until end of whole sequence 1036 break; 1037 case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: 1038 libraryOrdinal = immediate; 1039 break; 1040 case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: 1041 libraryOrdinal = read_uleb128(p, end); 1042 break; 1043 case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: 1044 // the special ordinals are negative numbers 1045 if ( immediate == 0 ) 1046 libraryOrdinal = 0; 1047 else { 1048 int8_t signExtended = BIND_OPCODE_MASK | immediate; 1049 libraryOrdinal = signExtended; 1050 } 1051 break; 1052 case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: 1053 symbolName = (char*)p; 1054 symboFlags = immediate; 1055 while (*p != '\0') 1056 ++p; 1057 ++p; 1058 break; 1059 case BIND_OPCODE_SET_TYPE_IMM: 1060 type = immediate; 1061 break; 1062 case BIND_OPCODE_SET_ADDEND_SLEB: 1063 addend = read_sleb128(p, end); 1064 break; 1065 case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: 1066 segmentIndex = immediate; 1067 if ( segmentIndex > fSegmentsCount ) 1068 dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is too large (%d)\n", 1069 segmentIndex, fSegmentsCount); 1070 address = segActualLoadAddress(segmentIndex) + read_uleb128(p, end); 1071 segmentEndAddress = segActualEndAddress(segmentIndex); 1072 break; 1073 case BIND_OPCODE_ADD_ADDR_ULEB: 1074 address += read_uleb128(p, end); 1075 break; 1076 case BIND_OPCODE_DO_BIND: 1077 if ( address >= segmentEndAddress ) 1078 throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p); 1079 (this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "forced lazy ", NULL, false); 1080 address += sizeof(intptr_t); 1081 break; 1082 case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: 1083 case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: 1084 case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: 1085 default: 1086 dyld::throwf("bad lazy bind opcode %d", *p); 1087 } 1088 } 1089 } 1090 1091 catch (const char* msg) { 1092 const char* newMsg = dyld::mkstringf("%s in %s", msg, this->getPath()); 1093 free((void*)msg); 1094 throw newMsg; 1095 } 1096} 1097 1098// A program built targeting 10.5 will have hybrid stubs. When used with weak symbols 1099// the classic lazy loader is used even when running on 10.6 1100uintptr_t ImageLoaderMachOCompressed::doBindLazySymbol(uintptr_t* lazyPointer, const LinkContext& context) 1101{ 1102 // only works with compressed LINKEDIT if classic symbol table is also present 1103 const macho_nlist* symbolTable = NULL; 1104 const char* symbolTableStrings = NULL; 1105 const dysymtab_command* dynSymbolTable = NULL; 1106 const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; 1107 const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; 1108 const struct load_command* cmd = cmds; 1109 for (uint32_t i = 0; i < cmd_count; ++i) { 1110 switch (cmd->cmd) { 1111 case LC_SYMTAB: 1112 { 1113 const struct symtab_command* symtab = (struct symtab_command*)cmd; 1114 symbolTableStrings = (const char*)&fLinkEditBase[symtab->stroff]; 1115 symbolTable = (macho_nlist*)(&fLinkEditBase[symtab->symoff]); 1116 } 1117 break; 1118 case LC_DYSYMTAB: 1119 dynSymbolTable = (struct dysymtab_command*)cmd; 1120 break; 1121 } 1122 cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); 1123 } 1124 // no symbol table => no lookup by address 1125 if ( (symbolTable == NULL) || (dynSymbolTable == NULL) ) 1126 dyld::throwf("classic lazy binding used with compressed LINKEDIT at %p in image %s", lazyPointer, this->getPath()); 1127 1128 // scan for all lazy-pointer sections 1129 const bool twoLevel = this->usesTwoLevelNameSpace(); 1130 const uint32_t* const indirectTable = (uint32_t*)&fLinkEditBase[dynSymbolTable->indirectsymoff]; 1131 cmd = cmds; 1132 for (uint32_t i = 0; i < cmd_count; ++i) { 1133 switch (cmd->cmd) { 1134 case LC_SEGMENT_COMMAND: 1135 { 1136 const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; 1137 const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command)); 1138 const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects]; 1139 for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { 1140 const uint8_t type = sect->flags & SECTION_TYPE; 1141 uint32_t symbolIndex = INDIRECT_SYMBOL_LOCAL; 1142 if ( type == S_LAZY_SYMBOL_POINTERS ) { 1143 const uint32_t pointerCount = sect->size / sizeof(uintptr_t); 1144 uintptr_t* const symbolPointers = (uintptr_t*)(sect->addr + fSlide); 1145 if ( (lazyPointer >= symbolPointers) && (lazyPointer < &symbolPointers[pointerCount]) ) { 1146 const uint32_t indirectTableOffset = sect->reserved1; 1147 const uint32_t lazyIndex = lazyPointer - symbolPointers; 1148 symbolIndex = indirectTable[indirectTableOffset + lazyIndex]; 1149 } 1150 } 1151 if ( (symbolIndex != INDIRECT_SYMBOL_ABS) && (symbolIndex != INDIRECT_SYMBOL_LOCAL) ) { 1152 const macho_nlist* symbol = &symbolTable[symbolIndex]; 1153 const char* symbolName = &symbolTableStrings[symbol->n_un.n_strx]; 1154 int libraryOrdinal = GET_LIBRARY_ORDINAL(symbol->n_desc); 1155 if ( !twoLevel || context.bindFlat ) 1156 libraryOrdinal = BIND_SPECIAL_DYLIB_FLAT_LOOKUP; 1157 uintptr_t ptrToBind = (uintptr_t)lazyPointer; 1158 uintptr_t symbolAddr = bindAt(context, ptrToBind, BIND_TYPE_POINTER, symbolName, 0, 0, libraryOrdinal, "lazy ", NULL); 1159 ++fgTotalLazyBindFixups; 1160 return symbolAddr; 1161 } 1162 } 1163 } 1164 break; 1165 } 1166 cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); 1167 } 1168 dyld::throwf("lazy pointer not found at address %p in image %s", lazyPointer, this->getPath()); 1169} 1170 1171 1172uintptr_t ImageLoaderMachOCompressed::doBindFastLazySymbol(uint32_t lazyBindingInfoOffset, const LinkContext& context, 1173 void (*lock)(), void (*unlock)()) 1174{ 1175 // <rdar://problem/8663923> race condition with flat-namespace lazy binding 1176 if ( this->usesTwoLevelNameSpace() ) { 1177 // two-level namespace lookup does not require lock because dependents can't be unloaded before this image 1178 } 1179 else { 1180 // acquire dyld global lock 1181 if ( lock != NULL ) 1182 lock(); 1183 } 1184 1185 const uint8_t* const start = fLinkEditBase + fDyldInfo->lazy_bind_off; 1186 const uint8_t* const end = &start[fDyldInfo->lazy_bind_size]; 1187 if ( lazyBindingInfoOffset > fDyldInfo->lazy_bind_size ) { 1188 dyld::throwf("fast lazy bind offset out of range (%u, max=%u) in image %s", 1189 lazyBindingInfoOffset, fDyldInfo->lazy_bind_size, this->getPath()); 1190 } 1191 1192 uint8_t type = BIND_TYPE_POINTER; 1193 uintptr_t address = 0; 1194 const char* symbolName = NULL; 1195 uint8_t symboFlags = 0; 1196 int libraryOrdinal = 0; 1197 bool done = false; 1198 uintptr_t result = 0; 1199 const uint8_t* p = &start[lazyBindingInfoOffset]; 1200 while ( !done && (p < end) ) { 1201 uint8_t immediate = *p & BIND_IMMEDIATE_MASK; 1202 uint8_t opcode = *p & BIND_OPCODE_MASK; 1203 ++p; 1204 switch (opcode) { 1205 case BIND_OPCODE_DONE: 1206 done = true; 1207 break; 1208 case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: 1209 libraryOrdinal = immediate; 1210 break; 1211 case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: 1212 libraryOrdinal = read_uleb128(p, end); 1213 break; 1214 case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: 1215 // the special ordinals are negative numbers 1216 if ( immediate == 0 ) 1217 libraryOrdinal = 0; 1218 else { 1219 int8_t signExtended = BIND_OPCODE_MASK | immediate; 1220 libraryOrdinal = signExtended; 1221 } 1222 break; 1223 case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: 1224 symbolName = (char*)p; 1225 symboFlags = immediate; 1226 while (*p != '\0') 1227 ++p; 1228 ++p; 1229 break; 1230 case BIND_OPCODE_SET_TYPE_IMM: 1231 type = immediate; 1232 break; 1233 case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: 1234 if ( immediate > fSegmentsCount ) 1235 dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is too large (%d)\n", 1236 immediate, fSegmentsCount); 1237 address = segActualLoadAddress(immediate) + read_uleb128(p, end); 1238 break; 1239 case BIND_OPCODE_DO_BIND: 1240 1241 1242 result = this->bindAt(context, address, type, symbolName, 0, 0, libraryOrdinal, "lazy ", NULL, true); 1243 break; 1244 case BIND_OPCODE_SET_ADDEND_SLEB: 1245 case BIND_OPCODE_ADD_ADDR_ULEB: 1246 case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: 1247 case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: 1248 case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: 1249 default: 1250 dyld::throwf("bad lazy bind opcode %d", *p); 1251 } 1252 } 1253 1254 if ( !this->usesTwoLevelNameSpace() ) { 1255 // release dyld global lock 1256 if ( unlock != NULL ) 1257 unlock(); 1258 } 1259 return result; 1260} 1261 1262void ImageLoaderMachOCompressed::initializeCoalIterator(CoalIterator& it, unsigned int loadOrder) 1263{ 1264 it.image = this; 1265 it.symbolName = " "; 1266 it.loadOrder = loadOrder; 1267 it.weakSymbol = false; 1268 it.symbolMatches = false; 1269 it.done = false; 1270 it.curIndex = 0; 1271 it.endIndex = this->fDyldInfo->weak_bind_size; 1272 it.address = 0; 1273 it.type = 0; 1274 it.addend = 0; 1275} 1276 1277 1278bool ImageLoaderMachOCompressed::incrementCoalIterator(CoalIterator& it) 1279{ 1280 if ( it.done ) 1281 return false; 1282 1283 if ( this->fDyldInfo->weak_bind_size == 0 ) { 1284 /// hmmm, ld set MH_WEAK_DEFINES or MH_BINDS_TO_WEAK, but there is no weak binding info 1285 it.done = true; 1286 it.symbolName = "~~~"; 1287 return true; 1288 } 1289 const uint8_t* start = fLinkEditBase + fDyldInfo->weak_bind_off; 1290 const uint8_t* p = start + it.curIndex; 1291 const uint8_t* end = fLinkEditBase + fDyldInfo->weak_bind_off + this->fDyldInfo->weak_bind_size; 1292 uint32_t count; 1293 uint32_t skip; 1294 while ( p < end ) { 1295 uint8_t immediate = *p & BIND_IMMEDIATE_MASK; 1296 uint8_t opcode = *p & BIND_OPCODE_MASK; 1297 ++p; 1298 switch (opcode) { 1299 case BIND_OPCODE_DONE: 1300 it.done = true; 1301 it.curIndex = p - start; 1302 it.symbolName = "~~~"; // sorts to end 1303 return true; 1304 case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: 1305 it.symbolName = (char*)p; 1306 it.weakSymbol = ((immediate & BIND_SYMBOL_FLAGS_NON_WEAK_DEFINITION) == 0); 1307 it.symbolMatches = false; 1308 while (*p != '\0') 1309 ++p; 1310 ++p; 1311 it.curIndex = p - start; 1312 return false; 1313 case BIND_OPCODE_SET_TYPE_IMM: 1314 it.type = immediate; 1315 break; 1316 case BIND_OPCODE_SET_ADDEND_SLEB: 1317 it.addend = read_sleb128(p, end); 1318 break; 1319 case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: 1320 if ( immediate > fSegmentsCount ) 1321 dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is too large (%d)\n", 1322 immediate, fSegmentsCount); 1323 it.address = segActualLoadAddress(immediate) + read_uleb128(p, end); 1324 break; 1325 case BIND_OPCODE_ADD_ADDR_ULEB: 1326 it.address += read_uleb128(p, end); 1327 break; 1328 case BIND_OPCODE_DO_BIND: 1329 it.address += sizeof(intptr_t); 1330 break; 1331 case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: 1332 it.address += read_uleb128(p, end) + sizeof(intptr_t); 1333 break; 1334 case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: 1335 it.address += immediate*sizeof(intptr_t) + sizeof(intptr_t); 1336 break; 1337 case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: 1338 count = read_uleb128(p, end); 1339 skip = read_uleb128(p, end); 1340 for (uint32_t i=0; i < count; ++i) { 1341 it.address += skip + sizeof(intptr_t); 1342 } 1343 break; 1344 default: 1345 dyld::throwf("bad weak bind opcode '%d' found after processing %d bytes in '%s'", *p, (int)(p-start), this->getPath()); 1346 } 1347 } 1348 /// hmmm, BIND_OPCODE_DONE is missing... 1349 it.done = true; 1350 it.symbolName = "~~~"; 1351 //dyld::log("missing BIND_OPCODE_DONE for image %s\n", this->getPath()); 1352 return true; 1353} 1354 1355uintptr_t ImageLoaderMachOCompressed::getAddressCoalIterator(CoalIterator& it, const LinkContext& context) 1356{ 1357 //dyld::log("looking for %s in %s\n", it.symbolName, this->getPath()); 1358 const ImageLoader* foundIn = NULL; 1359 const ImageLoader::Symbol* sym = this->findExportedSymbol(it.symbolName, &foundIn); 1360 if ( sym != NULL ) { 1361 //dyld::log("sym=%p, foundIn=%p\n", sym, foundIn); 1362 return foundIn->getExportedSymbolAddress(sym, context, this); 1363 } 1364 return 0; 1365} 1366 1367 1368void ImageLoaderMachOCompressed::updateUsesCoalIterator(CoalIterator& it, uintptr_t value, ImageLoader* targetImage, const LinkContext& context) 1369{ 1370 // <rdar://problem/6570879> weak binding done too early with inserted libraries 1371 if ( this->getState() < dyld_image_state_bound ) 1372 return; 1373 1374 const uint8_t* start = fLinkEditBase + fDyldInfo->weak_bind_off; 1375 const uint8_t* p = start + it.curIndex; 1376 const uint8_t* end = fLinkEditBase + fDyldInfo->weak_bind_off + this->fDyldInfo->weak_bind_size; 1377 1378 uint8_t type = it.type; 1379 uintptr_t address = it.address; 1380 const char* symbolName = it.symbolName; 1381 intptr_t addend = it.addend; 1382 uint32_t count; 1383 uint32_t skip; 1384 bool done = false; 1385 bool boundSomething = false; 1386 while ( !done && (p < end) ) { 1387 uint8_t immediate = *p & BIND_IMMEDIATE_MASK; 1388 uint8_t opcode = *p & BIND_OPCODE_MASK; 1389 ++p; 1390 switch (opcode) { 1391 case BIND_OPCODE_DONE: 1392 done = true; 1393 break; 1394 case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: 1395 done = true; 1396 break; 1397 case BIND_OPCODE_SET_TYPE_IMM: 1398 type = immediate; 1399 break; 1400 case BIND_OPCODE_SET_ADDEND_SLEB: 1401 addend = read_sleb128(p, end); 1402 break; 1403 case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: 1404 if ( immediate > fSegmentsCount ) 1405 dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is too large (%d)\n", 1406 immediate, fSegmentsCount); 1407 address = segActualLoadAddress(immediate) + read_uleb128(p, end); 1408 break; 1409 case BIND_OPCODE_ADD_ADDR_ULEB: 1410 address += read_uleb128(p, end); 1411 break; 1412 case BIND_OPCODE_DO_BIND: 1413 bindLocation(context, address, value, targetImage, type, symbolName, addend, "weak "); 1414 boundSomething = true; 1415 address += sizeof(intptr_t); 1416 break; 1417 case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: 1418 bindLocation(context, address, value, targetImage, type, symbolName, addend, "weak "); 1419 boundSomething = true; 1420 address += read_uleb128(p, end) + sizeof(intptr_t); 1421 break; 1422 case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: 1423 bindLocation(context, address, value, targetImage, type, symbolName, addend, "weak "); 1424 boundSomething = true; 1425 address += immediate*sizeof(intptr_t) + sizeof(intptr_t); 1426 break; 1427 case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: 1428 count = read_uleb128(p, end); 1429 skip = read_uleb128(p, end); 1430 for (uint32_t i=0; i < count; ++i) { 1431 bindLocation(context, address, value, targetImage, type, symbolName, addend, "weak "); 1432 boundSomething = true; 1433 address += skip + sizeof(intptr_t); 1434 } 1435 break; 1436 default: 1437 dyld::throwf("bad bind opcode %d in weak binding info", *p); 1438 } 1439 } 1440 // C++ weak coalescing cannot be tracked by reference counting. Error on side of never unloading. 1441 if ( boundSomething && (targetImage != this) ) 1442 context.addDynamicReference(this, targetImage); 1443} 1444 1445uintptr_t ImageLoaderMachOCompressed::interposeAt(const LinkContext& context, uintptr_t addr, uint8_t type, const char*, 1446 uint8_t, intptr_t, int, const char*, LastLookup*, bool runResolver) 1447{ 1448 if ( type == BIND_TYPE_POINTER ) { 1449 uintptr_t* fixupLocation = (uintptr_t*)addr; 1450 uintptr_t value = *fixupLocation; 1451 for (std::vector<InterposeTuple>::iterator it=fgInterposingTuples.begin(); it != fgInterposingTuples.end(); it++) { 1452 // replace all references to 'replacee' with 'replacement' 1453 if ( (value == it->replacee) && (this != it->replacementImage) ) { 1454 if ( context.verboseInterposing ) { 1455 dyld::log("dyld: interposing: at %p replace 0x%lX with 0x%lX in %s\n", 1456 fixupLocation, it->replacee, it->replacement, this->getPath()); 1457 } 1458 *fixupLocation = it->replacement; 1459 } 1460 } 1461 } 1462 return 0; 1463} 1464 1465void ImageLoaderMachOCompressed::doInterpose(const LinkContext& context) 1466{ 1467 if ( context.verboseInterposing ) 1468 dyld::log("dyld: interposing %lu tuples onto image: %s\n", fgInterposingTuples.size(), this->getPath()); 1469 1470 // update prebound symbols 1471 eachBind(context, &ImageLoaderMachOCompressed::interposeAt); 1472 eachLazyBind(context, &ImageLoaderMachOCompressed::interposeAt); 1473} 1474 1475 1476 1477 1478const char* ImageLoaderMachOCompressed::findClosestSymbol(const void* addr, const void** closestAddr) const 1479{ 1480 // called by dladdr() 1481 // only works with compressed LINKEDIT if classic symbol table is also present 1482 const macho_nlist* symbolTable = NULL; 1483 const char* symbolTableStrings = NULL; 1484 const dysymtab_command* dynSymbolTable = NULL; 1485 const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; 1486 const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; 1487 const struct load_command* cmd = cmds; 1488 for (uint32_t i = 0; i < cmd_count; ++i) { 1489 switch (cmd->cmd) { 1490 case LC_SYMTAB: 1491 { 1492 const struct symtab_command* symtab = (struct symtab_command*)cmd; 1493 symbolTableStrings = (const char*)&fLinkEditBase[symtab->stroff]; 1494 symbolTable = (macho_nlist*)(&fLinkEditBase[symtab->symoff]); 1495 } 1496 break; 1497 case LC_DYSYMTAB: 1498 dynSymbolTable = (struct dysymtab_command*)cmd; 1499 break; 1500 } 1501 cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); 1502 } 1503 // no symbol table => no lookup by address 1504 if ( (symbolTable == NULL) || (dynSymbolTable == NULL) ) 1505 return NULL; 1506 1507 uintptr_t targetAddress = (uintptr_t)addr - fSlide; 1508 const struct macho_nlist* bestSymbol = NULL; 1509 // first walk all global symbols 1510 const struct macho_nlist* const globalsStart = &symbolTable[dynSymbolTable->iextdefsym]; 1511 const struct macho_nlist* const globalsEnd= &globalsStart[dynSymbolTable->nextdefsym]; 1512 for (const struct macho_nlist* s = globalsStart; s < globalsEnd; ++s) { 1513 if ( (s->n_type & N_TYPE) == N_SECT ) { 1514 if ( bestSymbol == NULL ) { 1515 if ( s->n_value <= targetAddress ) 1516 bestSymbol = s; 1517 } 1518 else if ( (s->n_value <= targetAddress) && (bestSymbol->n_value < s->n_value) ) { 1519 bestSymbol = s; 1520 } 1521 } 1522 } 1523 // next walk all local symbols 1524 const struct macho_nlist* const localsStart = &symbolTable[dynSymbolTable->ilocalsym]; 1525 const struct macho_nlist* const localsEnd= &localsStart[dynSymbolTable->nlocalsym]; 1526 for (const struct macho_nlist* s = localsStart; s < localsEnd; ++s) { 1527 if ( ((s->n_type & N_TYPE) == N_SECT) && ((s->n_type & N_STAB) == 0) ) { 1528 if ( bestSymbol == NULL ) { 1529 if ( s->n_value <= targetAddress ) 1530 bestSymbol = s; 1531 } 1532 else if ( (s->n_value <= targetAddress) && (bestSymbol->n_value < s->n_value) ) { 1533 bestSymbol = s; 1534 } 1535 } 1536 } 1537 if ( bestSymbol != NULL ) { 1538#if __arm__ 1539 if (bestSymbol->n_desc & N_ARM_THUMB_DEF) 1540 *closestAddr = (void*)((bestSymbol->n_value | 1) + fSlide); 1541 else 1542 *closestAddr = (void*)(bestSymbol->n_value + fSlide); 1543#else 1544 *closestAddr = (void*)(bestSymbol->n_value + fSlide); 1545#endif 1546 return &symbolTableStrings[bestSymbol->n_un.n_strx]; 1547 } 1548 return NULL; 1549} 1550 1551 1552#if PREBOUND_IMAGE_SUPPORT 1553void ImageLoaderMachOCompressed::resetPreboundLazyPointers(const LinkContext& context) 1554{ 1555 // no way to back off a prebound compress image 1556} 1557#endif 1558 1559 1560#if __arm__ || __x86_64__ 1561void ImageLoaderMachOCompressed::updateAlternateLazyPointer(uint8_t* stub, void** originalLazyPointerAddr, const LinkContext& context) 1562{ 1563#if __arm__ 1564 uint32_t* instructions = (uint32_t*)stub; 1565 // sanity check this is a stub we understand 1566 if ( (instructions[0] != 0xe59fc004) || (instructions[1] != 0xe08fc00c) || (instructions[2] != 0xe59cf000) ) 1567 return; 1568 1569 void** lazyPointerAddr = (void**)(instructions[3] + (stub + 12)); 1570#endif 1571#if __x86_64__ 1572 // sanity check this is a stub we understand 1573 if ( (stub[0] != 0xFF) || (stub[1] != 0x25) ) 1574 return; 1575 int32_t ripOffset = *((int32_t*)(&stub[2])); 1576 void** lazyPointerAddr = (void**)(ripOffset + stub + 6); 1577#endif 1578 1579 // if stub does not use original lazy pointer (meaning it was optimized by update_dyld_shared_cache) 1580 if ( lazyPointerAddr != originalLazyPointerAddr ) { 1581 // <rdar://problem/12928448> only de-optimization lazy pointers if they are part of shared cache not loaded (because overridden) 1582 const ImageLoader* lazyPointerImage = context.findImageContainingAddress(lazyPointerAddr); 1583 if ( lazyPointerImage != NULL ) 1584 return; 1585 1586 // copy newly re-bound lazy pointer value to shared lazy pointer 1587 *lazyPointerAddr = *originalLazyPointerAddr; 1588 1589 if ( context.verboseBind ) 1590 dyld::log("dyld: alter bind: %s: *0x%08lX = 0x%08lX \n", 1591 this->getShortName(), (long)lazyPointerAddr, (long)*originalLazyPointerAddr); 1592 } 1593} 1594#endif 1595 1596 1597// <rdar://problem/8890875> overriding shared cache dylibs with resolvers fails 1598void ImageLoaderMachOCompressed::updateOptimizedLazyPointers(const LinkContext& context) 1599{ 1600#if __arm__ || __x86_64__ 1601 // find stubs and lazy pointer sections 1602 const struct macho_section* stubsSection = NULL; 1603 const struct macho_section* lazyPointerSection = NULL; 1604 const dysymtab_command* dynSymbolTable = NULL; 1605 const macho_header* mh = (macho_header*)fMachOData; 1606 const uint32_t cmd_count = mh->ncmds; 1607 const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; 1608 const struct load_command* cmd = cmds; 1609 for (uint32_t i = 0; i < cmd_count; ++i) { 1610 if (cmd->cmd == LC_SEGMENT_COMMAND) { 1611 const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; 1612 const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command)); 1613 const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects]; 1614 for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { 1615 const uint8_t type = sect->flags & SECTION_TYPE; 1616 if ( type == S_SYMBOL_STUBS ) 1617 stubsSection = sect; 1618 else if ( type == S_LAZY_SYMBOL_POINTERS ) 1619 lazyPointerSection = sect; 1620 } 1621 } 1622 else if ( cmd->cmd == LC_DYSYMTAB ) { 1623 dynSymbolTable = (struct dysymtab_command*)cmd; 1624 } 1625 cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); 1626 } 1627 1628 // sanity check 1629 if ( dynSymbolTable == NULL ) 1630 return; 1631 if ( (stubsSection == NULL) || (lazyPointerSection == NULL) ) 1632 return; 1633 const uint32_t stubsCount = stubsSection->size / stubsSection->reserved2; 1634 const uint32_t lazyPointersCount = lazyPointerSection->size / sizeof(void*); 1635 if ( stubsCount != lazyPointersCount ) 1636 return; 1637 const uint32_t stubsIndirectTableOffset = stubsSection->reserved1; 1638 const uint32_t lazyPointersIndirectTableOffset = lazyPointerSection->reserved1; 1639 if ( (stubsIndirectTableOffset+stubsCount) > dynSymbolTable->nindirectsyms ) 1640 return; 1641 if ( (lazyPointersIndirectTableOffset+lazyPointersCount) > dynSymbolTable->nindirectsyms ) 1642 return; 1643 1644 // walk stubs and lazy pointers 1645 const uint32_t* const indirectTable = (uint32_t*)&fLinkEditBase[dynSymbolTable->indirectsymoff]; 1646 void** const lazyPointersStartAddr = (void**)(lazyPointerSection->addr + this->fSlide); 1647 uint8_t* const stubsStartAddr = (uint8_t*)(stubsSection->addr + this->fSlide); 1648 uint8_t* stub = stubsStartAddr; 1649 void** lpa = lazyPointersStartAddr; 1650 for(uint32_t i=0; i < stubsCount; ++i, stub += stubsSection->reserved2, ++lpa) { 1651 // sanity check symbol index of stub and lazy pointer match 1652 if ( indirectTable[stubsIndirectTableOffset+i] != indirectTable[lazyPointersIndirectTableOffset+i] ) 1653 continue; 1654 this->updateAlternateLazyPointer(stub, lpa, context); 1655 } 1656 1657#endif 1658} 1659 1660 1661