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 = dyld_page_trunc(start); 378 end = dyld_page_round(end); 379 380 // do nothing if only one page of rebase/bind info 381 if ( (end-start) <= dyld_page_size ) 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 uintptr_t count; 437 uintptr_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 (0..%d)", 454 segmentIndex, fSegmentsCount-1); 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 uintptr_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 uintptr_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 uintptr_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 uintptr_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((unsigned int)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 uintptr_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 uintptr_t interposedStub = interposedAddress(context, stub, requestor); 649 if ( interposedStub != stub ) 650 return interposedStub; 651 // stub was not interposed, so run resolver 652 typedef uintptr_t (*ResolverProc)(void); 653 ResolverProc resolver = (ResolverProc)(read_uleb128(exportNode, exportTrieEnd) + (uintptr_t)fMachOData); 654 uintptr_t result = (*resolver)(); 655 if ( context.verboseBind ) 656 dyld::log("dyld: resolver at %p returned 0x%08lX\n", resolver, result); 657 return result; 658 } 659 return read_uleb128(exportNode, exportTrieEnd) + (uintptr_t)fMachOData; 660 case EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL: 661 if ( flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER ) 662 dyld::throwf("unsupported exported symbol kind. flags=%lu at node=%p", flags, symbol); 663 return read_uleb128(exportNode, exportTrieEnd) + (uintptr_t)fMachOData; 664 case EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE: 665 if ( flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER ) 666 dyld::throwf("unsupported exported symbol kind. flags=%lu at node=%p", flags, symbol); 667 return read_uleb128(exportNode, exportTrieEnd); 668 default: 669 dyld::throwf("unsupported exported symbol kind. flags=%lu at node=%p", flags, symbol); 670 } 671} 672 673bool 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 uintptr_t flags = read_uleb128(exportNode, exportTrieEnd); 681 return ( flags & EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION ); 682} 683 684 685const char* ImageLoaderMachOCompressed::exportedSymbolName(const Symbol* symbol) const 686{ 687 throw "NSNameOfSymbol() not supported with compressed LINKEDIT"; 688} 689 690unsigned int ImageLoaderMachOCompressed::exportedSymbolCount() const 691{ 692 throw "NSSymbolDefinitionCountInObjectFileImage() not supported with compressed LINKEDIT"; 693} 694 695const ImageLoader::Symbol* ImageLoaderMachOCompressed::exportedSymbolIndexed(unsigned int index) const 696{ 697 throw "NSSymbolDefinitionNameInObjectFileImage() not supported with compressed LINKEDIT"; 698} 699 700unsigned int ImageLoaderMachOCompressed::importedSymbolCount() const 701{ 702 throw "NSSymbolReferenceCountInObjectFileImage() not supported with compressed LINKEDIT"; 703} 704 705const ImageLoader::Symbol* ImageLoaderMachOCompressed::importedSymbolIndexed(unsigned int index) const 706{ 707 throw "NSSymbolReferenceCountInObjectFileImage() not supported with compressed LINKEDIT"; 708} 709 710const char* ImageLoaderMachOCompressed::importedSymbolName(const Symbol* symbol) const 711{ 712 throw "NSSymbolReferenceNameInObjectFileImage() not supported with compressed LINKEDIT"; 713} 714 715 716 717uintptr_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 ) 723 context.addDynamicReference(this, const_cast<ImageLoader*>(*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 741uintptr_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, check if maybe this image is too new for this OS 756 char versMismatch[256]; 757 versMismatch[0] = '\0'; 758 uint32_t imageMinOS = this->minOSVersion(); 759 // dyld is always built for the current OS, so we can get the current OS version 760 // from the load command in dyld itself. 761 extern const mach_header __dso_handle; 762 uint32_t dyldMinOS = ImageLoaderMachO::minOSVersion(&__dso_handle); 763 if ( imageMinOS > dyldMinOS ) { 764#if __MAC_OS_X_VERSION_MIN_REQUIRED 765 const char* msg = dyld::mkstringf(" (which was built for Mac OS X %d.%d)", imageMinOS >> 16, (imageMinOS >> 8) & 0xFF); 766#else 767 const char* msg = dyld::mkstringf(" (which was built for iOS %d.%d)", imageMinOS >> 16, (imageMinOS >> 8) & 0xFF); 768#endif 769 strcpy(versMismatch, msg); 770 ::free((void*)msg); 771 } 772 throwSymbolNotFound(context, symbolName, this->getPath(), versMismatch, targetImage->getPath()); 773} 774 775 776uintptr_t ImageLoaderMachOCompressed::resolve(const LinkContext& context, const char* symbolName, 777 uint8_t symboFlags, long libraryOrdinal, const ImageLoader** targetImage, 778 LastLookup* last, bool runResolver) 779{ 780 *targetImage = NULL; 781 782 // only clients that benefit from caching last lookup pass in a LastLookup struct 783 if ( last != NULL ) { 784 if ( (last->ordinal == libraryOrdinal) 785 && (last->flags == symboFlags) 786 && (last->name == symbolName) ) { 787 *targetImage = last->foundIn; 788 return last->result; 789 } 790 } 791 792 bool weak_import = (symboFlags & BIND_SYMBOL_FLAGS_WEAK_IMPORT); 793 uintptr_t symbolAddress; 794 if ( context.bindFlat || (libraryOrdinal == BIND_SPECIAL_DYLIB_FLAT_LOOKUP) ) { 795 symbolAddress = this->resolveFlat(context, symbolName, weak_import, runResolver, targetImage); 796 } 797 else { 798 if ( libraryOrdinal == BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE ) { 799 *targetImage = context.mainExecutable; 800 } 801 else if ( libraryOrdinal == BIND_SPECIAL_DYLIB_SELF ) { 802 *targetImage = this; 803 } 804 else if ( libraryOrdinal <= 0 ) { 805 dyld::throwf("bad mach-o binary, unknown special library ordinal (%ld) too big for symbol %s in %s", 806 libraryOrdinal, symbolName, this->getPath()); 807 } 808 else if ( (unsigned)libraryOrdinal <= libraryCount() ) { 809 *targetImage = libImage((unsigned int)libraryOrdinal-1); 810 } 811 else { 812 dyld::throwf("bad mach-o binary, library ordinal (%ld) too big (max %u) for symbol %s in %s", 813 libraryOrdinal, libraryCount(), symbolName, this->getPath()); 814 } 815 if ( *targetImage == NULL ) { 816 if ( weak_import ) { 817 // if target library not loaded and reference is weak or library is weak return 0 818 symbolAddress = 0; 819 } 820 else { 821 dyld::throwf("can't resolve symbol %s in %s because dependent dylib #%ld could not be loaded", 822 symbolName, this->getPath(), libraryOrdinal); 823 } 824 } 825 else { 826 symbolAddress = resolveTwolevel(context, *targetImage, weak_import, symbolName, runResolver, targetImage); 827 } 828 } 829 830 // save off lookup results if client wants 831 if ( last != NULL ) { 832 last->ordinal = libraryOrdinal; 833 last->flags = symboFlags; 834 last->name = symbolName; 835 last->foundIn = *targetImage; 836 last->result = symbolAddress; 837 } 838 839 return symbolAddress; 840} 841 842uintptr_t ImageLoaderMachOCompressed::bindAt(const LinkContext& context, uintptr_t addr, uint8_t type, const char* symbolName, 843 uint8_t symboFlags, intptr_t addend, long libraryOrdinal, const char* msg, 844 LastLookup* last, bool runResolver) 845{ 846 const ImageLoader* targetImage; 847 uintptr_t symbolAddress; 848 849 // resolve symbol 850 symbolAddress = this->resolve(context, symbolName, symboFlags, libraryOrdinal, &targetImage, last, runResolver); 851 852 // do actual update 853 return this->bindLocation(context, addr, symbolAddress, targetImage, type, symbolName, addend, msg); 854} 855 856void ImageLoaderMachOCompressed::throwBadBindingAddress(uintptr_t address, uintptr_t segmentEndAddress, int segmentIndex, 857 const uint8_t* startOpcodes, const uint8_t* endOpcodes, const uint8_t* pos) 858{ 859 dyld::throwf("malformed binding opcodes (%ld/%ld): address 0x%08lX is beyond end of segment %s (0x%08lX -> 0x%08lX)", 860 (intptr_t)(pos-startOpcodes), (intptr_t)(endOpcodes-startOpcodes), address, segName(segmentIndex), 861 segActualLoadAddress(segmentIndex), segmentEndAddress); 862} 863 864 865void ImageLoaderMachOCompressed::doBind(const LinkContext& context, bool forceLazysBound) 866{ 867 CRSetCrashLogMessage2(this->getPath()); 868 869 // if prebound and loaded at prebound address, and all libraries are same as when this was prebound, then no need to bind 870 // note: flat-namespace binaries need to have imports rebound (even if correctly prebound) 871 if ( this->usablePrebinding(context) ) { 872 // don't need to bind 873 } 874 else { 875 876 #if TEXT_RELOC_SUPPORT 877 // if there are __TEXT fixups, temporarily make __TEXT writable 878 if ( fTextSegmentBinds ) 879 this->makeTextSegmentWritable(context, true); 880 #endif 881 882 // run through all binding opcodes 883 eachBind(context, &ImageLoaderMachOCompressed::bindAt); 884 885 #if TEXT_RELOC_SUPPORT 886 // if there were __TEXT fixups, restore write protection 887 if ( fTextSegmentBinds ) 888 this->makeTextSegmentWritable(context, false); 889 #endif 890 891 // if this image is in the shared cache, but depends on something no longer in the shared cache, 892 // there is no way to reset the lazy pointers, so force bind them now 893 if ( forceLazysBound || fInSharedCache ) 894 this->doBindJustLazies(context); 895 896 // this image is in cache, but something below it is not. If 897 // this image has lazy pointer to a resolver function, then 898 // the stub may have been altered to point to a shared lazy pointer. 899 if ( fInSharedCache ) 900 this->updateOptimizedLazyPointers(context); 901 902 // tell kernel we are done with chunks of LINKEDIT 903 if ( !context.preFetchDisabled ) 904 this->markFreeLINKEDIT(context); 905 } 906 907 // set up dyld entry points in image 908 // do last so flat main executables will have __dyld or __program_vars set up 909 this->setupLazyPointerHandler(context); 910 CRSetCrashLogMessage2(NULL); 911} 912 913 914void ImageLoaderMachOCompressed::doBindJustLazies(const LinkContext& context) 915{ 916 eachLazyBind(context, &ImageLoaderMachOCompressed::bindAt); 917} 918 919void ImageLoaderMachOCompressed::eachBind(const LinkContext& context, bind_handler handler) 920{ 921 try { 922 uint8_t type = 0; 923 int segmentIndex = 0; 924 uintptr_t address = segActualLoadAddress(0); 925 uintptr_t segmentEndAddress = segActualEndAddress(0); 926 const char* symbolName = NULL; 927 uint8_t symboFlags = 0; 928 long libraryOrdinal = 0; 929 intptr_t addend = 0; 930 uintptr_t count; 931 uintptr_t skip; 932 LastLookup last = { 0, 0, NULL, 0, NULL }; 933 const uint8_t* const start = fLinkEditBase + fDyldInfo->bind_off; 934 const uint8_t* const end = &start[fDyldInfo->bind_size]; 935 const uint8_t* p = start; 936 bool done = false; 937 while ( !done && (p < end) ) { 938 uint8_t immediate = *p & BIND_IMMEDIATE_MASK; 939 uint8_t opcode = *p & BIND_OPCODE_MASK; 940 ++p; 941 switch (opcode) { 942 case BIND_OPCODE_DONE: 943 done = true; 944 break; 945 case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: 946 libraryOrdinal = immediate; 947 break; 948 case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: 949 libraryOrdinal = read_uleb128(p, end); 950 break; 951 case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: 952 // the special ordinals are negative numbers 953 if ( immediate == 0 ) 954 libraryOrdinal = 0; 955 else { 956 int8_t signExtended = BIND_OPCODE_MASK | immediate; 957 libraryOrdinal = signExtended; 958 } 959 break; 960 case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: 961 symbolName = (char*)p; 962 symboFlags = immediate; 963 while (*p != '\0') 964 ++p; 965 ++p; 966 break; 967 case BIND_OPCODE_SET_TYPE_IMM: 968 type = immediate; 969 break; 970 case BIND_OPCODE_SET_ADDEND_SLEB: 971 addend = read_sleb128(p, end); 972 break; 973 case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: 974 segmentIndex = immediate; 975 if ( segmentIndex >= fSegmentsCount ) 976 dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is too large (0..%d)", 977 segmentIndex, fSegmentsCount-1); 978 address = segActualLoadAddress(segmentIndex) + read_uleb128(p, end); 979 segmentEndAddress = segActualEndAddress(segmentIndex); 980 break; 981 case BIND_OPCODE_ADD_ADDR_ULEB: 982 address += read_uleb128(p, end); 983 break; 984 case BIND_OPCODE_DO_BIND: 985 if ( address >= segmentEndAddress ) 986 throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p); 987 (this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "", &last, false); 988 address += sizeof(intptr_t); 989 break; 990 case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: 991 if ( address >= segmentEndAddress ) 992 throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p); 993 (this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "", &last, false); 994 address += read_uleb128(p, end) + sizeof(intptr_t); 995 break; 996 case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: 997 if ( address >= segmentEndAddress ) 998 throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p); 999 (this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "", &last, false); 1000 address += immediate*sizeof(intptr_t) + sizeof(intptr_t); 1001 break; 1002 case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: 1003 count = read_uleb128(p, end); 1004 skip = read_uleb128(p, end); 1005 for (uint32_t i=0; i < count; ++i) { 1006 if ( address >= segmentEndAddress ) 1007 throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p); 1008 (this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "", &last, false); 1009 address += skip + sizeof(intptr_t); 1010 } 1011 break; 1012 default: 1013 dyld::throwf("bad bind opcode %d in bind info", *p); 1014 } 1015 } 1016 } 1017 catch (const char* msg) { 1018 const char* newMsg = dyld::mkstringf("%s in %s", msg, this->getPath()); 1019 free((void*)msg); 1020 throw newMsg; 1021 } 1022} 1023 1024void ImageLoaderMachOCompressed::eachLazyBind(const LinkContext& context, bind_handler handler) 1025{ 1026 try { 1027 uint8_t type = BIND_TYPE_POINTER; 1028 int segmentIndex = 0; 1029 uintptr_t address = segActualLoadAddress(0); 1030 uintptr_t segmentEndAddress = segActualEndAddress(0); 1031 const char* symbolName = NULL; 1032 uint8_t symboFlags = 0; 1033 long libraryOrdinal = 0; 1034 intptr_t addend = 0; 1035 const uint8_t* const start = fLinkEditBase + fDyldInfo->lazy_bind_off; 1036 const uint8_t* const end = &start[fDyldInfo->lazy_bind_size]; 1037 const uint8_t* p = start; 1038 bool done = false; 1039 while ( !done && (p < end) ) { 1040 uint8_t immediate = *p & BIND_IMMEDIATE_MASK; 1041 uint8_t opcode = *p & BIND_OPCODE_MASK; 1042 ++p; 1043 switch (opcode) { 1044 case BIND_OPCODE_DONE: 1045 // there is BIND_OPCODE_DONE at end of each lazy bind, don't stop until end of whole sequence 1046 break; 1047 case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: 1048 libraryOrdinal = immediate; 1049 break; 1050 case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: 1051 libraryOrdinal = read_uleb128(p, end); 1052 break; 1053 case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: 1054 // the special ordinals are negative numbers 1055 if ( immediate == 0 ) 1056 libraryOrdinal = 0; 1057 else { 1058 int8_t signExtended = BIND_OPCODE_MASK | immediate; 1059 libraryOrdinal = signExtended; 1060 } 1061 break; 1062 case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: 1063 symbolName = (char*)p; 1064 symboFlags = immediate; 1065 while (*p != '\0') 1066 ++p; 1067 ++p; 1068 break; 1069 case BIND_OPCODE_SET_TYPE_IMM: 1070 type = immediate; 1071 break; 1072 case BIND_OPCODE_SET_ADDEND_SLEB: 1073 addend = read_sleb128(p, end); 1074 break; 1075 case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: 1076 segmentIndex = immediate; 1077 if ( segmentIndex >= fSegmentsCount ) 1078 dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is too large (0..%d)", 1079 segmentIndex, fSegmentsCount-1); 1080 address = segActualLoadAddress(segmentIndex) + read_uleb128(p, end); 1081 segmentEndAddress = segActualEndAddress(segmentIndex); 1082 break; 1083 case BIND_OPCODE_ADD_ADDR_ULEB: 1084 address += read_uleb128(p, end); 1085 break; 1086 case BIND_OPCODE_DO_BIND: 1087 if ( address >= segmentEndAddress ) 1088 throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p); 1089 (this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "forced lazy ", NULL, false); 1090 address += sizeof(intptr_t); 1091 break; 1092 case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: 1093 case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: 1094 case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: 1095 default: 1096 dyld::throwf("bad lazy bind opcode %d", *p); 1097 } 1098 } 1099 } 1100 1101 catch (const char* msg) { 1102 const char* newMsg = dyld::mkstringf("%s in %s", msg, this->getPath()); 1103 free((void*)msg); 1104 throw newMsg; 1105 } 1106} 1107 1108// A program built targeting 10.5 will have hybrid stubs. When used with weak symbols 1109// the classic lazy loader is used even when running on 10.6 1110uintptr_t ImageLoaderMachOCompressed::doBindLazySymbol(uintptr_t* lazyPointer, const LinkContext& context) 1111{ 1112 // only works with compressed LINKEDIT if classic symbol table is also present 1113 const macho_nlist* symbolTable = NULL; 1114 const char* symbolTableStrings = NULL; 1115 const dysymtab_command* dynSymbolTable = NULL; 1116 const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; 1117 const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; 1118 const struct load_command* cmd = cmds; 1119 for (uint32_t i = 0; i < cmd_count; ++i) { 1120 switch (cmd->cmd) { 1121 case LC_SYMTAB: 1122 { 1123 const struct symtab_command* symtab = (struct symtab_command*)cmd; 1124 symbolTableStrings = (const char*)&fLinkEditBase[symtab->stroff]; 1125 symbolTable = (macho_nlist*)(&fLinkEditBase[symtab->symoff]); 1126 } 1127 break; 1128 case LC_DYSYMTAB: 1129 dynSymbolTable = (struct dysymtab_command*)cmd; 1130 break; 1131 } 1132 cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); 1133 } 1134 // no symbol table => no lookup by address 1135 if ( (symbolTable == NULL) || (dynSymbolTable == NULL) ) 1136 dyld::throwf("classic lazy binding used with compressed LINKEDIT at %p in image %s", lazyPointer, this->getPath()); 1137 1138 // scan for all lazy-pointer sections 1139 const bool twoLevel = this->usesTwoLevelNameSpace(); 1140 const uint32_t* const indirectTable = (uint32_t*)&fLinkEditBase[dynSymbolTable->indirectsymoff]; 1141 cmd = cmds; 1142 for (uint32_t i = 0; i < cmd_count; ++i) { 1143 switch (cmd->cmd) { 1144 case LC_SEGMENT_COMMAND: 1145 { 1146 const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; 1147 const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command)); 1148 const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects]; 1149 for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { 1150 const uint8_t type = sect->flags & SECTION_TYPE; 1151 uint32_t symbolIndex = INDIRECT_SYMBOL_LOCAL; 1152 if ( type == S_LAZY_SYMBOL_POINTERS ) { 1153 const size_t pointerCount = sect->size / sizeof(uintptr_t); 1154 uintptr_t* const symbolPointers = (uintptr_t*)(sect->addr + fSlide); 1155 if ( (lazyPointer >= symbolPointers) && (lazyPointer < &symbolPointers[pointerCount]) ) { 1156 const uint32_t indirectTableOffset = sect->reserved1; 1157 const size_t lazyIndex = lazyPointer - symbolPointers; 1158 symbolIndex = indirectTable[indirectTableOffset + lazyIndex]; 1159 } 1160 } 1161 if ( (symbolIndex != INDIRECT_SYMBOL_ABS) && (symbolIndex != INDIRECT_SYMBOL_LOCAL) ) { 1162 const macho_nlist* symbol = &symbolTable[symbolIndex]; 1163 const char* symbolName = &symbolTableStrings[symbol->n_un.n_strx]; 1164 int libraryOrdinal = GET_LIBRARY_ORDINAL(symbol->n_desc); 1165 if ( !twoLevel || context.bindFlat ) 1166 libraryOrdinal = BIND_SPECIAL_DYLIB_FLAT_LOOKUP; 1167 uintptr_t ptrToBind = (uintptr_t)lazyPointer; 1168 uintptr_t symbolAddr = bindAt(context, ptrToBind, BIND_TYPE_POINTER, symbolName, 0, 0, libraryOrdinal, "lazy ", NULL); 1169 ++fgTotalLazyBindFixups; 1170 return symbolAddr; 1171 } 1172 } 1173 } 1174 break; 1175 } 1176 cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); 1177 } 1178 dyld::throwf("lazy pointer not found at address %p in image %s", lazyPointer, this->getPath()); 1179} 1180 1181 1182uintptr_t ImageLoaderMachOCompressed::doBindFastLazySymbol(uint32_t lazyBindingInfoOffset, const LinkContext& context, 1183 void (*lock)(), void (*unlock)()) 1184{ 1185 // <rdar://problem/8663923> race condition with flat-namespace lazy binding 1186 if ( this->usesTwoLevelNameSpace() ) { 1187 // two-level namespace lookup does not require lock because dependents can't be unloaded before this image 1188 } 1189 else { 1190 // acquire dyld global lock 1191 if ( lock != NULL ) 1192 lock(); 1193 } 1194 1195 const uint8_t* const start = fLinkEditBase + fDyldInfo->lazy_bind_off; 1196 const uint8_t* const end = &start[fDyldInfo->lazy_bind_size]; 1197 if ( lazyBindingInfoOffset > fDyldInfo->lazy_bind_size ) { 1198 dyld::throwf("fast lazy bind offset out of range (%u, max=%u) in image %s", 1199 lazyBindingInfoOffset, fDyldInfo->lazy_bind_size, this->getPath()); 1200 } 1201 1202 uint8_t type = BIND_TYPE_POINTER; 1203 uintptr_t address = 0; 1204 const char* symbolName = NULL; 1205 uint8_t symboFlags = 0; 1206 long libraryOrdinal = 0; 1207 bool done = false; 1208 uintptr_t result = 0; 1209 const uint8_t* p = &start[lazyBindingInfoOffset]; 1210 while ( !done && (p < end) ) { 1211 uint8_t immediate = *p & BIND_IMMEDIATE_MASK; 1212 uint8_t opcode = *p & BIND_OPCODE_MASK; 1213 ++p; 1214 switch (opcode) { 1215 case BIND_OPCODE_DONE: 1216 done = true; 1217 break; 1218 case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: 1219 libraryOrdinal = immediate; 1220 break; 1221 case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: 1222 libraryOrdinal = read_uleb128(p, end); 1223 break; 1224 case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: 1225 // the special ordinals are negative numbers 1226 if ( immediate == 0 ) 1227 libraryOrdinal = 0; 1228 else { 1229 int8_t signExtended = BIND_OPCODE_MASK | immediate; 1230 libraryOrdinal = signExtended; 1231 } 1232 break; 1233 case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: 1234 symbolName = (char*)p; 1235 symboFlags = immediate; 1236 while (*p != '\0') 1237 ++p; 1238 ++p; 1239 break; 1240 case BIND_OPCODE_SET_TYPE_IMM: 1241 type = immediate; 1242 break; 1243 case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: 1244 if ( immediate >= fSegmentsCount ) 1245 dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is too large (0..%d)", 1246 immediate, fSegmentsCount-1); 1247 address = segActualLoadAddress(immediate) + read_uleb128(p, end); 1248 break; 1249 case BIND_OPCODE_DO_BIND: 1250 1251 1252 result = this->bindAt(context, address, type, symbolName, 0, 0, libraryOrdinal, "lazy ", NULL, true); 1253 break; 1254 case BIND_OPCODE_SET_ADDEND_SLEB: 1255 case BIND_OPCODE_ADD_ADDR_ULEB: 1256 case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: 1257 case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: 1258 case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: 1259 default: 1260 dyld::throwf("bad lazy bind opcode %d", *p); 1261 } 1262 } 1263 1264 if ( !this->usesTwoLevelNameSpace() ) { 1265 // release dyld global lock 1266 if ( unlock != NULL ) 1267 unlock(); 1268 } 1269 return result; 1270} 1271 1272void ImageLoaderMachOCompressed::initializeCoalIterator(CoalIterator& it, unsigned int loadOrder) 1273{ 1274 it.image = this; 1275 it.symbolName = " "; 1276 it.loadOrder = loadOrder; 1277 it.weakSymbol = false; 1278 it.symbolMatches = false; 1279 it.done = false; 1280 it.curIndex = 0; 1281 it.endIndex = this->fDyldInfo->weak_bind_size; 1282 it.address = 0; 1283 it.type = 0; 1284 it.addend = 0; 1285} 1286 1287 1288bool ImageLoaderMachOCompressed::incrementCoalIterator(CoalIterator& it) 1289{ 1290 if ( it.done ) 1291 return false; 1292 1293 if ( this->fDyldInfo->weak_bind_size == 0 ) { 1294 /// hmmm, ld set MH_WEAK_DEFINES or MH_BINDS_TO_WEAK, but there is no weak binding info 1295 it.done = true; 1296 it.symbolName = "~~~"; 1297 return true; 1298 } 1299 const uint8_t* start = fLinkEditBase + fDyldInfo->weak_bind_off; 1300 const uint8_t* p = start + it.curIndex; 1301 const uint8_t* end = fLinkEditBase + fDyldInfo->weak_bind_off + this->fDyldInfo->weak_bind_size; 1302 uintptr_t count; 1303 uintptr_t skip; 1304 while ( p < end ) { 1305 uint8_t immediate = *p & BIND_IMMEDIATE_MASK; 1306 uint8_t opcode = *p & BIND_OPCODE_MASK; 1307 ++p; 1308 switch (opcode) { 1309 case BIND_OPCODE_DONE: 1310 it.done = true; 1311 it.curIndex = p - start; 1312 it.symbolName = "~~~"; // sorts to end 1313 return true; 1314 case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: 1315 it.symbolName = (char*)p; 1316 it.weakSymbol = ((immediate & BIND_SYMBOL_FLAGS_NON_WEAK_DEFINITION) == 0); 1317 it.symbolMatches = false; 1318 while (*p != '\0') 1319 ++p; 1320 ++p; 1321 it.curIndex = p - start; 1322 return false; 1323 case BIND_OPCODE_SET_TYPE_IMM: 1324 it.type = immediate; 1325 break; 1326 case BIND_OPCODE_SET_ADDEND_SLEB: 1327 it.addend = read_sleb128(p, end); 1328 break; 1329 case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: 1330 if ( immediate >= fSegmentsCount ) 1331 dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is too large (0..%d)", 1332 immediate, fSegmentsCount-1); 1333 it.address = segActualLoadAddress(immediate) + read_uleb128(p, end); 1334 break; 1335 case BIND_OPCODE_ADD_ADDR_ULEB: 1336 it.address += read_uleb128(p, end); 1337 break; 1338 case BIND_OPCODE_DO_BIND: 1339 it.address += sizeof(intptr_t); 1340 break; 1341 case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: 1342 it.address += read_uleb128(p, end) + sizeof(intptr_t); 1343 break; 1344 case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: 1345 it.address += immediate*sizeof(intptr_t) + sizeof(intptr_t); 1346 break; 1347 case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: 1348 count = read_uleb128(p, end); 1349 skip = read_uleb128(p, end); 1350 for (uint32_t i=0; i < count; ++i) { 1351 it.address += skip + sizeof(intptr_t); 1352 } 1353 break; 1354 default: 1355 dyld::throwf("bad weak bind opcode '%d' found after processing %d bytes in '%s'", *p, (int)(p-start), this->getPath()); 1356 } 1357 } 1358 /// hmmm, BIND_OPCODE_DONE is missing... 1359 it.done = true; 1360 it.symbolName = "~~~"; 1361 //dyld::log("missing BIND_OPCODE_DONE for image %s\n", this->getPath()); 1362 return true; 1363} 1364 1365uintptr_t ImageLoaderMachOCompressed::getAddressCoalIterator(CoalIterator& it, const LinkContext& context) 1366{ 1367 //dyld::log("looking for %s in %s\n", it.symbolName, this->getPath()); 1368 const ImageLoader* foundIn = NULL; 1369 const ImageLoader::Symbol* sym = this->findExportedSymbol(it.symbolName, &foundIn); 1370 if ( sym != NULL ) { 1371 //dyld::log("sym=%p, foundIn=%p\n", sym, foundIn); 1372 return foundIn->getExportedSymbolAddress(sym, context, this); 1373 } 1374 return 0; 1375} 1376 1377 1378void ImageLoaderMachOCompressed::updateUsesCoalIterator(CoalIterator& it, uintptr_t value, ImageLoader* targetImage, const LinkContext& context) 1379{ 1380 // <rdar://problem/6570879> weak binding done too early with inserted libraries 1381 if ( this->getState() < dyld_image_state_bound ) 1382 return; 1383 1384 const uint8_t* start = fLinkEditBase + fDyldInfo->weak_bind_off; 1385 const uint8_t* p = start + it.curIndex; 1386 const uint8_t* end = fLinkEditBase + fDyldInfo->weak_bind_off + this->fDyldInfo->weak_bind_size; 1387 1388 uint8_t type = it.type; 1389 uintptr_t address = it.address; 1390 const char* symbolName = it.symbolName; 1391 intptr_t addend = it.addend; 1392 uintptr_t count; 1393 uintptr_t skip; 1394 bool done = false; 1395 bool boundSomething = false; 1396 while ( !done && (p < end) ) { 1397 uint8_t immediate = *p & BIND_IMMEDIATE_MASK; 1398 uint8_t opcode = *p & BIND_OPCODE_MASK; 1399 ++p; 1400 switch (opcode) { 1401 case BIND_OPCODE_DONE: 1402 done = true; 1403 break; 1404 case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: 1405 done = true; 1406 break; 1407 case BIND_OPCODE_SET_TYPE_IMM: 1408 type = immediate; 1409 break; 1410 case BIND_OPCODE_SET_ADDEND_SLEB: 1411 addend = read_sleb128(p, end); 1412 break; 1413 case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: 1414 if ( immediate >= fSegmentsCount ) 1415 dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is too large (0..%d)", 1416 immediate, fSegmentsCount-1); 1417 address = segActualLoadAddress(immediate) + read_uleb128(p, end); 1418 break; 1419 case BIND_OPCODE_ADD_ADDR_ULEB: 1420 address += read_uleb128(p, end); 1421 break; 1422 case BIND_OPCODE_DO_BIND: 1423 bindLocation(context, address, value, targetImage, type, symbolName, addend, "weak "); 1424 boundSomething = true; 1425 address += sizeof(intptr_t); 1426 break; 1427 case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: 1428 bindLocation(context, address, value, targetImage, type, symbolName, addend, "weak "); 1429 boundSomething = true; 1430 address += read_uleb128(p, end) + sizeof(intptr_t); 1431 break; 1432 case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: 1433 bindLocation(context, address, value, targetImage, type, symbolName, addend, "weak "); 1434 boundSomething = true; 1435 address += immediate*sizeof(intptr_t) + sizeof(intptr_t); 1436 break; 1437 case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: 1438 count = read_uleb128(p, end); 1439 skip = read_uleb128(p, end); 1440 for (uint32_t i=0; i < count; ++i) { 1441 bindLocation(context, address, value, targetImage, type, symbolName, addend, "weak "); 1442 boundSomething = true; 1443 address += skip + sizeof(intptr_t); 1444 } 1445 break; 1446 default: 1447 dyld::throwf("bad bind opcode %d in weak binding info", *p); 1448 } 1449 } 1450 // C++ weak coalescing cannot be tracked by reference counting. Error on side of never unloading. 1451 if ( boundSomething && (targetImage != this) ) 1452 context.addDynamicReference(this, targetImage); 1453} 1454 1455uintptr_t ImageLoaderMachOCompressed::interposeAt(const LinkContext& context, uintptr_t addr, uint8_t type, const char*, 1456 uint8_t, intptr_t, long, const char*, LastLookup*, bool runResolver) 1457{ 1458 if ( type == BIND_TYPE_POINTER ) { 1459 uintptr_t* fixupLocation = (uintptr_t*)addr; 1460 uintptr_t curValue = *fixupLocation; 1461 uintptr_t newValue = interposedAddress(context, curValue, this); 1462 if ( newValue != curValue) 1463 *fixupLocation = newValue; 1464 } 1465 return 0; 1466} 1467 1468void ImageLoaderMachOCompressed::doInterpose(const LinkContext& context) 1469{ 1470 if ( context.verboseInterposing ) 1471 dyld::log("dyld: interposing %lu tuples onto image: %s\n", fgInterposingTuples.size(), this->getPath()); 1472 1473 // update prebound symbols 1474 eachBind(context, &ImageLoaderMachOCompressed::interposeAt); 1475 eachLazyBind(context, &ImageLoaderMachOCompressed::interposeAt); 1476} 1477 1478 1479uintptr_t ImageLoaderMachOCompressed::dynamicInterposeAt(const LinkContext& context, uintptr_t addr, uint8_t type, const char* symbolName, 1480 uint8_t, intptr_t, long, const char*, LastLookup*, bool runResolver) 1481{ 1482 if ( type == BIND_TYPE_POINTER ) { 1483 uintptr_t* fixupLocation = (uintptr_t*)addr; 1484 uintptr_t value = *fixupLocation; 1485 // don't apply interposing to table entries. 1486 if ( (context.dynamicInterposeArray <= (void*)addr) && ((void*)addr < &context.dynamicInterposeArray[context.dynamicInterposeCount]) ) 1487 return 0; 1488 for(size_t i=0; i < context.dynamicInterposeCount; ++i) { 1489 if ( value == (uintptr_t)context.dynamicInterposeArray[i].replacee ) { 1490 if ( context.verboseInterposing ) { 1491 dyld::log("dyld: dynamic interposing: at %p replace %p with %p in %s\n", 1492 fixupLocation, context.dynamicInterposeArray[i].replacee, context.dynamicInterposeArray[i].replacement, this->getPath()); 1493 } 1494 *fixupLocation = (uintptr_t)context.dynamicInterposeArray[i].replacement; 1495 } 1496 } 1497 } 1498 return 0; 1499} 1500 1501void ImageLoaderMachOCompressed::dynamicInterpose(const LinkContext& context) 1502{ 1503 if ( context.verboseInterposing ) 1504 dyld::log("dyld: dynamic interposing %lu tuples onto image: %s\n", context.dynamicInterposeCount, this->getPath()); 1505 1506 // update already bound references to symbols 1507 eachBind(context, &ImageLoaderMachOCompressed::dynamicInterposeAt); 1508 eachLazyBind(context, &ImageLoaderMachOCompressed::dynamicInterposeAt); 1509} 1510 1511 1512const char* ImageLoaderMachOCompressed::findClosestSymbol(const void* addr, const void** closestAddr) const 1513{ 1514 // called by dladdr() 1515 // only works with compressed LINKEDIT if classic symbol table is also present 1516 const macho_nlist* symbolTable = NULL; 1517 const char* symbolTableStrings = NULL; 1518 const dysymtab_command* dynSymbolTable = NULL; 1519 const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; 1520 const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; 1521 const struct load_command* cmd = cmds; 1522 for (uint32_t i = 0; i < cmd_count; ++i) { 1523 switch (cmd->cmd) { 1524 case LC_SYMTAB: 1525 { 1526 const struct symtab_command* symtab = (struct symtab_command*)cmd; 1527 symbolTableStrings = (const char*)&fLinkEditBase[symtab->stroff]; 1528 symbolTable = (macho_nlist*)(&fLinkEditBase[symtab->symoff]); 1529 } 1530 break; 1531 case LC_DYSYMTAB: 1532 dynSymbolTable = (struct dysymtab_command*)cmd; 1533 break; 1534 } 1535 cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); 1536 } 1537 // no symbol table => no lookup by address 1538 if ( (symbolTable == NULL) || (dynSymbolTable == NULL) ) 1539 return NULL; 1540 1541 uintptr_t targetAddress = (uintptr_t)addr - fSlide; 1542 const struct macho_nlist* bestSymbol = NULL; 1543 // first walk all global symbols 1544 const struct macho_nlist* const globalsStart = &symbolTable[dynSymbolTable->iextdefsym]; 1545 const struct macho_nlist* const globalsEnd= &globalsStart[dynSymbolTable->nextdefsym]; 1546 for (const struct macho_nlist* s = globalsStart; s < globalsEnd; ++s) { 1547 if ( (s->n_type & N_TYPE) == N_SECT ) { 1548 if ( bestSymbol == NULL ) { 1549 if ( s->n_value <= targetAddress ) 1550 bestSymbol = s; 1551 } 1552 else if ( (s->n_value <= targetAddress) && (bestSymbol->n_value < s->n_value) ) { 1553 bestSymbol = s; 1554 } 1555 } 1556 } 1557 // next walk all local symbols 1558 const struct macho_nlist* const localsStart = &symbolTable[dynSymbolTable->ilocalsym]; 1559 const struct macho_nlist* const localsEnd= &localsStart[dynSymbolTable->nlocalsym]; 1560 for (const struct macho_nlist* s = localsStart; s < localsEnd; ++s) { 1561 if ( ((s->n_type & N_TYPE) == N_SECT) && ((s->n_type & N_STAB) == 0) ) { 1562 if ( bestSymbol == NULL ) { 1563 if ( s->n_value <= targetAddress ) 1564 bestSymbol = s; 1565 } 1566 else if ( (s->n_value <= targetAddress) && (bestSymbol->n_value < s->n_value) ) { 1567 bestSymbol = s; 1568 } 1569 } 1570 } 1571 if ( bestSymbol != NULL ) { 1572#if __arm__ 1573 if (bestSymbol->n_desc & N_ARM_THUMB_DEF) 1574 *closestAddr = (void*)((bestSymbol->n_value | 1) + fSlide); 1575 else 1576 *closestAddr = (void*)(bestSymbol->n_value + fSlide); 1577#else 1578 *closestAddr = (void*)(bestSymbol->n_value + fSlide); 1579#endif 1580 return &symbolTableStrings[bestSymbol->n_un.n_strx]; 1581 } 1582 return NULL; 1583} 1584 1585 1586#if PREBOUND_IMAGE_SUPPORT 1587void ImageLoaderMachOCompressed::resetPreboundLazyPointers(const LinkContext& context) 1588{ 1589 // no way to back off a prebound compress image 1590} 1591#endif 1592 1593 1594#if __arm__ || __x86_64__ 1595void ImageLoaderMachOCompressed::updateAlternateLazyPointer(uint8_t* stub, void** originalLazyPointerAddr, const LinkContext& context) 1596{ 1597#if __arm__ 1598 uint32_t* instructions = (uint32_t*)stub; 1599 // sanity check this is a stub we understand 1600 if ( (instructions[0] != 0xe59fc004) || (instructions[1] != 0xe08fc00c) || (instructions[2] != 0xe59cf000) ) 1601 return; 1602 1603 void** lazyPointerAddr = (void**)(instructions[3] + (stub + 12)); 1604#endif 1605#if __x86_64__ 1606 // sanity check this is a stub we understand 1607 if ( (stub[0] != 0xFF) || (stub[1] != 0x25) ) 1608 return; 1609 int32_t ripOffset = *((int32_t*)(&stub[2])); 1610 void** lazyPointerAddr = (void**)(ripOffset + stub + 6); 1611#endif 1612 1613 // if stub does not use original lazy pointer (meaning it was optimized by update_dyld_shared_cache) 1614 if ( lazyPointerAddr != originalLazyPointerAddr ) { 1615 // <rdar://problem/12928448> only de-optimization lazy pointers if they are part of shared cache not loaded (because overridden) 1616 const ImageLoader* lazyPointerImage = context.findImageContainingAddress(lazyPointerAddr); 1617 if ( lazyPointerImage != NULL ) 1618 return; 1619 1620 // copy newly re-bound lazy pointer value to shared lazy pointer 1621 *lazyPointerAddr = *originalLazyPointerAddr; 1622 1623 if ( context.verboseBind ) 1624 dyld::log("dyld: alter bind: %s: *0x%08lX = 0x%08lX \n", 1625 this->getShortName(), (long)lazyPointerAddr, (long)*originalLazyPointerAddr); 1626 } 1627} 1628#endif 1629 1630 1631// <rdar://problem/8890875> overriding shared cache dylibs with resolvers fails 1632void ImageLoaderMachOCompressed::updateOptimizedLazyPointers(const LinkContext& context) 1633{ 1634#if __arm__ || __x86_64__ 1635 // find stubs and lazy pointer sections 1636 const struct macho_section* stubsSection = NULL; 1637 const struct macho_section* lazyPointerSection = NULL; 1638 const dysymtab_command* dynSymbolTable = NULL; 1639 const macho_header* mh = (macho_header*)fMachOData; 1640 const uint32_t cmd_count = mh->ncmds; 1641 const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; 1642 const struct load_command* cmd = cmds; 1643 for (uint32_t i = 0; i < cmd_count; ++i) { 1644 if (cmd->cmd == LC_SEGMENT_COMMAND) { 1645 const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; 1646 const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command)); 1647 const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects]; 1648 for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { 1649 const uint8_t type = sect->flags & SECTION_TYPE; 1650 if ( type == S_SYMBOL_STUBS ) 1651 stubsSection = sect; 1652 else if ( type == S_LAZY_SYMBOL_POINTERS ) 1653 lazyPointerSection = sect; 1654 } 1655 } 1656 else if ( cmd->cmd == LC_DYSYMTAB ) { 1657 dynSymbolTable = (struct dysymtab_command*)cmd; 1658 } 1659 cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); 1660 } 1661 1662 // sanity check 1663 if ( dynSymbolTable == NULL ) 1664 return; 1665 if ( (stubsSection == NULL) || (lazyPointerSection == NULL) ) 1666 return; 1667 const uint32_t stubsCount = stubsSection->size / stubsSection->reserved2; 1668 const uint32_t lazyPointersCount = lazyPointerSection->size / sizeof(void*); 1669 if ( stubsCount != lazyPointersCount ) 1670 return; 1671 const uint32_t stubsIndirectTableOffset = stubsSection->reserved1; 1672 const uint32_t lazyPointersIndirectTableOffset = lazyPointerSection->reserved1; 1673 if ( (stubsIndirectTableOffset+stubsCount) > dynSymbolTable->nindirectsyms ) 1674 return; 1675 if ( (lazyPointersIndirectTableOffset+lazyPointersCount) > dynSymbolTable->nindirectsyms ) 1676 return; 1677 1678 // walk stubs and lazy pointers 1679 const uint32_t* const indirectTable = (uint32_t*)&fLinkEditBase[dynSymbolTable->indirectsymoff]; 1680 void** const lazyPointersStartAddr = (void**)(lazyPointerSection->addr + this->fSlide); 1681 uint8_t* const stubsStartAddr = (uint8_t*)(stubsSection->addr + this->fSlide); 1682 uint8_t* stub = stubsStartAddr; 1683 void** lpa = lazyPointersStartAddr; 1684 for(uint32_t i=0; i < stubsCount; ++i, stub += stubsSection->reserved2, ++lpa) { 1685 // sanity check symbol index of stub and lazy pointer match 1686 if ( indirectTable[stubsIndirectTableOffset+i] != indirectTable[lazyPointersIndirectTableOffset+i] ) 1687 continue; 1688 this->updateAlternateLazyPointer(stub, lpa, context); 1689 } 1690 1691#endif 1692} 1693 1694 1695