void MachObject::doBindSymbols() { /* * Parsing the compressed dyld info section. */ if (fIsClassic) { this->classicBindIndirect(); this->classicBindExterns(); } else { if (!fDyldInfo) { lnk::halt("no dyld info section"); } /* Bind opcodes */ this->readBind(addUintPtr2(fLinkEditBase, fDyldInfo->bind_off), addUintPtr3(fLinkEditBase, fDyldInfo->bind_off, fDyldInfo->bind_size)); this->readWeakBind(addUintPtr2(fLinkEditBase, fDyldInfo->weak_bind_off), addUintPtr3(fLinkEditBase, fDyldInfo->weak_bind_off, fDyldInfo->weak_bind_size)); this->readLazyBind(addUintPtr2(fLinkEditBase, fDyldInfo->lazy_bind_off), addUintPtr3(fLinkEditBase, fDyldInfo->lazy_bind_off, fDyldInfo->lazy_bind_size)); } }
uintptr_t MachObject::exportedSymbolAddressCompressed(Symbol *sym) { const uint8_t* exportNode = (uint8_t*)(sym->addr); const uint8_t* start = addUintPtr2(fLinkEditBase, fDyldInfo->export_off); const uint8_t* end = addUintPtr3(fLinkEditBase, fDyldInfo->export_off, fDyldInfo->export_size); bool runResolver = true; uintptr_t result = 0; if ((exportNode < start) || (exportNode > end)) lnk::halt("symbol not in a trie"); uint32_t flags = read_uleb128(exportNode, end); if ((flags & EXPORT_SYMBOL_FLAGS_KIND_MASK) == EXPORT_SYMBOL_FLAGS_KIND_REGULAR) { if ( runResolver && (flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER) ) { lnk::halt("XXX: resolvers not implemented, fix macho loader on line %d", __LINE__); return result; } return read_uleb128(exportNode, end) + (uintptr_t)fHeader; } else if ((flags & EXPORT_SYMBOL_FLAGS_KIND_MASK) == EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL) { if (flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER) lnk::halt("unsupported local exported symbol kind. flags=%d at node=%p", flags, sym); return read_uleb128(exportNode, end) + (uintptr_t)fHeader; } else { lnk::halt("unsupported exported symbol kind. flags=%d at node=%p", flags, sym); } }
void MachObject::doRebase() { if (fIsClassic) { if (!fIsSplitSeg) { /* * Split segs do not need rebasing. */ classicRebase(); } } else { this->readRebase(addUintPtr2(fLinkEditBase, fDyldInfo->rebase_off), addUintPtr3(fLinkEditBase, fDyldInfo->rebase_off, fDyldInfo->rebase_size)); } }
bool MachObject::findExportedSymbolCompressed(const char* symbol, Symbol* sym) { /* This is a slightly tidier version of 'findExportedSymbol' from dyld. Still no f*****g idea what the semantics of it are since I suck at CS (lol, wtf is a trie?!). */ /* export table sanity */ if (fDyldInfo->export_size == 0) return false; const uint8_t* start = addUintPtr2(fLinkEditBase, fDyldInfo->export_off); const uint8_t* end = addUintPtr3(fLinkEditBase, fDyldInfo->export_off, fDyldInfo->export_size); const uint8_t* foundNodeStart = trie_walk(start, end, symbol); if (foundNodeStart != NULL) { const uint8_t* p = foundNodeStart; const uint32_t flags = read_uleb128(p, end); if (flags & EXPORT_SYMBOL_FLAGS_REEXPORT) { lnk::halt("no f*****g idea, honestly"); return false; } else { sym->addr = (void*)foundNodeStart; sym->inImage = (void*)this; return true; } } else { return false; } }
/* * This is used by the sliding function to read and execute * the compressed reloc table. */ static void rebaseCompressed(struct dyld_info_command* dyldInfo, macho_segment_command* linkEdit, uintptr_t slide, macho_segment_command** segments, int segCount) { /* * HARD MODE!!! */ const uint8_t* base = (uint8_t*)((linkEdit->vmaddr + slide) - linkEdit->fileoff); const uint8_t* start = addUintPtr2(dyldInfo->rebase_off, base); const uint8_t* end = addUintPtr3(dyldInfo->rebase_size, dyldInfo->rebase_off, base); const uint8_t* p = start; /* * If you want a better documented version of this code, see 'MachObject.cpp' */ uint8_t type = 0; int segmentIndex = 0; uintptr_t address = (segments[0]->vmaddr + slide); uintptr_t segmentEndAddress = (segments[0]->vmaddr + slide + segments[0]->vmsize); uint32_t count; uint32_t skip; bool done = false; while ( !done && (p < end) ) { uint8_t immediate = *p & REBASE_IMMEDIATE_MASK; uint8_t opcode = *p & REBASE_OPCODE_MASK; ++p; switch (opcode) { case REBASE_OPCODE_DONE: done = true; break; case REBASE_OPCODE_SET_TYPE_IMM: type = immediate; break; case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: segmentIndex = immediate; if (segmentIndex > segCount) lnk::ldbg::printText("LNK_RELOC_ERROR: baaaad whatever \n"); address = (segments[segmentIndex]->vmaddr + slide) + read_uleb128(p, end); segmentEndAddress = (segments[segmentIndex]->vmaddr + segments[segmentIndex]->vmsize + slide); break; case REBASE_OPCODE_ADD_ADDR_ULEB: address += read_uleb128(p, end); break; case REBASE_OPCODE_ADD_ADDR_IMM_SCALED: address += immediate*sizeof(uintptr_t); break; case REBASE_OPCODE_DO_REBASE_IMM_TIMES: for (int i=0; i < immediate; ++i) { if ( address >= segmentEndAddress ) lnk::ldbg::printText("LNK_RELOC_ERROR: baaaad REBASE_OPCODE_DO_REBASE_IMM_TIMES \n"); rebaseAt(address, slide, type); address += sizeof(uintptr_t); } break; case REBASE_OPCODE_DO_REBASE_ULEB_TIMES: count = read_uleb128(p, end); for (uint32_t i=0; i < count; ++i) { if ( address >= segmentEndAddress ) lnk::ldbg::printText("LNK_RELOC_ERROR: baaaad REBASE_OPCODE_DO_REBASE_ULEB_TIMES \n"); rebaseAt(address, slide, type); address += sizeof(uintptr_t); } break; case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB: if ( address >= segmentEndAddress ) lnk::ldbg::printText("LNK_RELOC_ERROR: baaaad REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB \n"); rebaseAt(address, slide, type); address += read_uleb128(p, end) + sizeof(uintptr_t); break; case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB: count = read_uleb128(p, end); skip = read_uleb128(p, end); for (uint32_t i=0; i < count; ++i) { if ( address >= segmentEndAddress ) lnk::ldbg::printText("LNK_RELOC_ERROR: baaaad REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB \n"); rebaseAt(address, slide, type); address += skip + sizeof(uintptr_t); } break; default: _printAbrt(); } } }
MachObject* MachObject::instantiateFromFile(const char* path, const char* origPath, int fd, const struct stat* info) { macho_header header; uint32_t ncmds = 0; void* cmd_base = NULL; uint32_t loader_bias = 0; size_t offset = 0; uint32_t map_size = 0; macho_segment_command* segments[64]; uint32_t segment_count = 0; void* actual_map_address = NULL; uint32_t file_size = 0; if (gVerboseLog) { lnk::log("institating a MachObject from file\n" " path : %s\n" " fd : %p" , path, fd); } /* Read mach-o header */ memset(&header, 0, sizeof(header)); read_at(fd, &header, sizeof(header), 0); /* Check sanity */ if (header.magic != MH_MAGIC) { lnk::halt("instantiateFromFile(%s): invalid magic (%p instead of %p)", path, header.magic, MH_MAGIC); } /* Read load commands into a buffer */ cmd_base = malloc(header.sizeofcmds); read_at(fd, cmd_base, header.sizeofcmds, sizeof(macho_header)); /* Set stuff */ ncmds = header.ncmds; /* Build array of segment load commands */ for (int i = 0; i < ncmds; i++) { struct load_command *lcp = (struct load_command *)((size_t)cmd_base + offset); offset += lcp->cmdsize; if (lcp->cmd == LC_SEGMENT) { segments[segment_count] = (macho_segment_command*)lcp; map_size += segments[segment_count]->vmsize; segment_count++; } } /* Check segment sanity */ if (segment_count < 1) { lnk::halt("instantiateFromFile(%s): no segments", path); } /* Is this a fixed-load dylib? */ if (segments[0]->vmaddr != 0) { /* Fixed, so not reserving */ loader_bias = 0; } else { /* Reserve a continous range */ loader_bias = (uint32_t)lnk::mm::reserve(map_size); } /* Map the segments */ for (int i = 0; i < segment_count; i++) { macho_segment_command* seg = segments[i]; uint32_t delta = 0; vm_size_t mm_filesize = round_page(seg->filesize); vm_size_t mm_vmsize = round_page(seg->vmsize); actual_map_address = lnk::mm::wire(fd, pageAlignPtr((void*)addUintPtr2(seg->vmaddr, loader_bias)), mm_filesize, seg->fileoff); if (gVerboseLog) { lnk::log("(%s:%s): wired at %p (sz: %p)", path, seg->segname, actual_map_address, mm_filesize); } delta = mm_vmsize - mm_filesize; if (delta && !loader_bias) { /* * We need to zero-fill the memory as this space * isn't backed up be reserved memory. */ void* anon_load = (void*)(addUintPtr3(seg->vmaddr, loader_bias, mm_filesize)); anon_load = pageAlignPtr(anon_load); /* map anonymous memory to fill the rest */ actual_map_address = lnk::mm::wire_anon(anon_load, delta); if (gVerboseLog) { lnk::log("(%s:%s): wired anon at %p (sz: %p)", path, seg->segname, actual_map_address, delta); } } } /* Add __TEXT to the linker table */ linker_image_table_add((uintptr_t)addUintPtr2(segments[0]->vmaddr, loader_bias), (size_t)segments[0]->vmsize, path); /* Instiate a new object */ MachObject* obj = new MachObject(); /* Set the object properties */ obj->fSlide = (uintptr_t)loader_bias; obj->fHeader = (macho_header*)addUintPtr2(segments[0]->vmaddr, loader_bias); obj->fModuleName = NULL; obj->fFilePath = path; obj->fOrigFilePath = origPath; obj->fIsSplitSeg = (loader_bias == 0); return obj; }