+const char* ImageLoaderMachO::findClosestSymbol(const mach_header* mh, const void* addr, const void** closestAddr)
+{
+ // called by dladdr()
+ // only works with compressed LINKEDIT if classic symbol table is also present
+ const dysymtab_command* dynSymbolTable = NULL;
+ const symtab_command* symtab = NULL;
+ const macho_segment_command* seg;
+ const uint8_t* unslidLinkEditBase = NULL;
+ bool linkEditBaseFound = false;
+ intptr_t slide = 0;
+ const uint32_t cmd_count = mh->ncmds;
+ const load_command* const cmds = (load_command*)((char*)mh + sizeof(macho_header));
+ const load_command* cmd = cmds;
+ for (uint32_t i = 0; i < cmd_count; ++i) {
+ switch (cmd->cmd) {
+ case LC_SEGMENT_COMMAND:
+ seg = (macho_segment_command*)cmd;
+ if ( strcmp(seg->segname, "__LINKEDIT") == 0 ) {
+ unslidLinkEditBase = (uint8_t*)(seg->vmaddr - seg->fileoff);
+ linkEditBaseFound = true;
+ }
+ else if ( strcmp(seg->segname, "__TEXT") == 0 ) {
+ slide = (uintptr_t)mh - seg->vmaddr;
+ }
+ break;
+ case LC_SYMTAB:
+ symtab = (symtab_command*)cmd;
+ break;
+ case LC_DYSYMTAB:
+ dynSymbolTable = (dysymtab_command*)cmd;
+ break;
+ }
+ cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
+ }
+ // no symbol table => no lookup by address
+ if ( (symtab == NULL) || (dynSymbolTable == NULL) || !linkEditBaseFound )
+ return NULL;
+
+ const uint8_t* linkEditBase = unslidLinkEditBase + slide;
+ const char* symbolTableStrings = (const char*)&linkEditBase[symtab->stroff];
+ const macho_nlist* symbolTable = (macho_nlist*)(&linkEditBase[symtab->symoff]);
+
+ uintptr_t targetAddress = (uintptr_t)addr - slide;
+ const struct macho_nlist* bestSymbol = NULL;
+ // first walk all global symbols
+ const struct macho_nlist* const globalsStart = &symbolTable[dynSymbolTable->iextdefsym];
+ const struct macho_nlist* const globalsEnd= &globalsStart[dynSymbolTable->nextdefsym];
+ for (const struct macho_nlist* s = globalsStart; s < globalsEnd; ++s) {
+ if ( (s->n_type & N_TYPE) == N_SECT ) {
+ if ( bestSymbol == NULL ) {
+ if ( s->n_value <= targetAddress )
+ bestSymbol = s;
+ }
+ else if ( (s->n_value <= targetAddress) && (bestSymbol->n_value < s->n_value) ) {
+ bestSymbol = s;
+ }
+ }
+ }
+ // next walk all local symbols
+ const struct macho_nlist* const localsStart = &symbolTable[dynSymbolTable->ilocalsym];
+ const struct macho_nlist* const localsEnd= &localsStart[dynSymbolTable->nlocalsym];
+ for (const struct macho_nlist* s = localsStart; s < localsEnd; ++s) {
+ if ( ((s->n_type & N_TYPE) == N_SECT) && ((s->n_type & N_STAB) == 0) ) {
+ if ( bestSymbol == NULL ) {
+ if ( s->n_value <= targetAddress )
+ bestSymbol = s;
+ }
+ else if ( (s->n_value <= targetAddress) && (bestSymbol->n_value < s->n_value) ) {
+ bestSymbol = s;
+ }
+ }
+ }
+ if ( bestSymbol != NULL ) {
+#if __arm__
+ if (bestSymbol->n_desc & N_ARM_THUMB_DEF)
+ *closestAddr = (void*)((bestSymbol->n_value | 1) + slide);
+ else
+ *closestAddr = (void*)(bestSymbol->n_value + slide);
+#else
+ *closestAddr = (void*)(bestSymbol->n_value + slide);
+#endif
+ return &symbolTableStrings[bestSymbol->n_un.n_strx];
+ }
+ return NULL;
+}
+
+bool ImageLoaderMachO::getLazyBindingInfo(uint32_t& lazyBindingInfoOffset, const uint8_t* lazyInfoStart, const uint8_t* lazyInfoEnd,
+ uint8_t* segIndex, uintptr_t* segOffset, int* ordinal, const char** symbolName, bool* doneAfterBind)
+{
+ if ( lazyBindingInfoOffset > (lazyInfoEnd-lazyInfoStart) )
+ return false;
+ uint8_t type = BIND_TYPE_POINTER;
+ uint8_t symboFlags = 0;
+ bool done = false;
+ const uint8_t* p = &lazyInfoStart[lazyBindingInfoOffset];
+ while ( !done && (p < lazyInfoEnd) ) {
+ uint8_t immediate = *p & BIND_IMMEDIATE_MASK;
+ uint8_t opcode = *p & BIND_OPCODE_MASK;
+ ++p;
+ switch (opcode) {
+ case BIND_OPCODE_DONE:
+ *doneAfterBind = false;
+ return true;
+ break;
+ case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM:
+ *ordinal = immediate;
+ break;
+ case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB:
+ *ordinal = (int)read_uleb128(p, lazyInfoEnd);
+ break;
+ case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM:
+ // the special ordinals are negative numbers
+ if ( immediate == 0 )
+ *ordinal = 0;
+ else {
+ int8_t signExtended = BIND_OPCODE_MASK | immediate;
+ *ordinal = signExtended;
+ }
+ break;
+ case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM:
+ *symbolName = (char*)p;
+ symboFlags = immediate;
+ while (*p != '\0')
+ ++p;
+ ++p;
+ break;
+ case BIND_OPCODE_SET_TYPE_IMM:
+ type = immediate;
+ break;
+ case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
+ *segIndex = immediate;
+ *segOffset = read_uleb128(p, lazyInfoEnd);
+ break;
+ case BIND_OPCODE_DO_BIND:
+ *doneAfterBind = ((*p & BIND_OPCODE_MASK) == BIND_OPCODE_DONE);
+ lazyBindingInfoOffset += p - &lazyInfoStart[lazyBindingInfoOffset];
+ return true;
+ break;
+ case BIND_OPCODE_SET_ADDEND_SLEB:
+ case BIND_OPCODE_ADD_ADDR_ULEB:
+ case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
+ case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED:
+ case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB:
+ default:
+ return false;
+ }
+ }
+ return false;
+}
+
+const dyld_info_command* ImageLoaderMachO::findDyldInfoLoadCommand(const mach_header* mh)
+{
+ const uint32_t cmd_count = mh->ncmds;
+ const load_command* const cmds = (load_command*)((char*)mh + sizeof(macho_header));
+ const load_command* cmd = cmds;
+ for (uint32_t i = 0; i < cmd_count; ++i) {
+ switch (cmd->cmd) {
+ case LC_DYLD_INFO:
+ case LC_DYLD_INFO_ONLY:
+ return (dyld_info_command*)cmd;
+ }
+ cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
+ }
+ return NULL;
+}
+
+
+uintptr_t ImageLoaderMachO::segPreferredAddress(const mach_header* mh, unsigned segIndex)
+{
+ const uint32_t cmd_count = mh->ncmds;
+ const load_command* const cmds = (load_command*)((char*)mh + sizeof(macho_header));
+ const load_command* cmd = cmds;
+ unsigned curSegIndex = 0;
+ for (uint32_t i = 0; i < cmd_count; ++i) {
+ if ( cmd->cmd == LC_SEGMENT_COMMAND ) {
+ if ( segIndex == curSegIndex ) {
+ const macho_segment_command* segCmd = (macho_segment_command*)cmd;
+ return segCmd->vmaddr;
+ }
+ ++curSegIndex;
+ }
+ cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
+ }
+ return 0;
+}
+
+
+
+uintptr_t ImageLoaderMachO::imageBaseAddress() const {
+ //printf("imageBaseAddress: %s %d->%d\n", getPath(), 0, segmentCount());
+ for (unsigned int i = 0, e = segmentCount(); i != e; ++i) {
+ if ( (segFileOffset(i) == 0) && (segFileSize(i) != 0) )
+ return segPreferredLoadAddress(i);
+ }
+ return 0;
+}