# HG changeset patch # User Tassilo Philipp # Date 1491896234 -7200 # Node ID 912efa3cfd4beefd583efca27b49b5bc362bdd1e # Parent cb56f077fd37f50f9092126ca0aef9bf05eaae5c - mach-o symbol iteration stability fixes diff -r cb56f077fd37 -r 912efa3cfd4b dynload/dynload_syms_mach-o.c --- a/dynload/dynload_syms_mach-o.c Thu Apr 06 13:35:50 2017 +0200 +++ b/dynload/dynload_syms_mach-o.c Tue Apr 11 09:37:14 2017 +0200 @@ -3,12 +3,12 @@ Package: dyncall Library: dynload File: dynload/dynload_syms_mach-o.c - Description: + Description: License: Copyright (c) 2007-2015 Olivier Chafik , - 2017 refactored for stability, API consistency - and portability by Tassilo Philipp. + 2017 refactored completely for stability, API + consistency and portability by Tassilo Philipp. Permission to use, copy, modify, and distribute this software for any purpose with or without fee is hereby granted, provided that the above @@ -27,7 +27,7 @@ /* - + dynamic symbol resolver for Mach-O */ @@ -44,10 +44,12 @@ #if defined(ARCH_X64) || defined(ARCH_PPC64) || defined(ARCH_ARM64) //@@@ use dyncall_macros.h #define MACH_HEADER_TYPE mach_header_64 +#define SEGMENT_COMMAND segment_command_64 #define NLIST_TYPE nlist_64 #else #define MACH_HEADER_TYPE mach_header #define NLIST_TYPE nlist +#define SEGMENT_COMMAND segment_command #endif @@ -64,8 +66,9 @@ { DLLib* pLib; DLSyms* pSyms; - uint32_t iImage, nImages; + uint32_t i, n; struct stat st0; + const struct MACH_HEADER_TYPE* pHeader = NULL; if(stat(libPath, &st0) == -1) return NULL; @@ -74,51 +77,62 @@ if(!pLib) return NULL; - // Loop over all dynamically linked images. - for (iImage = 0, nImages = _dyld_image_count(); iImage < nImages; iImage++) + // Loop over all dynamically linked images to find ours. + for(i = 0, n = _dyld_image_count(); i < n; ++i) { struct stat st1; - const char* name = _dyld_get_image_name(iImage); + const char* name = _dyld_get_image_name(i); if(name && (stat(name, &st1) != -1)) { - // Don't rely on name comparison alone, as libPath might be relative, symlink, differently + // Don't rely on name comparison alone, as libPath might be relative, symlink, differently // cased, etc., but compare inode number with the one of the mapped dyld image. - if (st0.st_ino == st1.st_ino/*!strcmp(name, libPath)*/) + if(st0.st_ino == st1.st_ino/*!strcmp(name, libPath)*/) { - const struct MACH_HEADER_TYPE* pHeader = (const struct MACH_HEADER_TYPE*) _dyld_get_image_header(iImage); - const char* pBase = ((const char*)pHeader); - if (pHeader->filetype != MH_DYLIB) - return NULL; - if (pHeader->flags & MH_SPLIT_SEGS) - return NULL; - - if (pHeader) - { - uint32_t iCmd, nCmds = pHeader->ncmds; - const struct load_command* cmd = (const struct load_command*)(pBase + sizeof(struct MACH_HEADER_TYPE)); - - for (iCmd = 0; iCmd < nCmds; iCmd++) - { - if (cmd->cmd == LC_SYMTAB) - { - const struct symtab_command* scmd = (const struct symtab_command*)cmd; - - pSyms = (DLSyms*)( dlAllocMem(sizeof(DLSyms)) ); - pSyms->symbolCount = scmd->nsyms; - pSyms->pStringTable = pBase + scmd->stroff; - pSyms->pSymbolTable = (struct NLIST_TYPE*)(pBase + scmd->symoff); - pSyms->pLib = pLib; - - return pSyms; - } - cmd = (const struct load_command*)(((char*)cmd) + cmd->cmdsize); - } - } - break; + pHeader = (const struct MACH_HEADER_TYPE*) _dyld_get_image_header(i); + break; // found header } } } + + if(pHeader && (pHeader->filetype == MH_DYLIB) && !(pHeader->flags & MH_SPLIT_SEGS)) + { + const char* pBase = (const char*)pHeader; + uintptr_t slide = 0; + const struct load_command* cmd = (const struct load_command*)(pBase + sizeof(struct MACH_HEADER_TYPE)); + + for(i = 0, n = pHeader->ncmds; i < n; ++i, cmd = (const struct load_command*)((const char*)cmd + cmd->cmdsize)) + { + if(cmd->cmd == LC_SEGMENT) + { + const struct SEGMENT_COMMAND* seg = (struct SEGMENT_COMMAND*)cmd; + if((seg->fileoff == 0) && (seg->filesize != 0)) // Count segment sizes to slide over...@@@? + slide = (uintptr_t)pHeader - seg->vmaddr; + if(strcmp(seg->segname, "__LINKEDIT") == 0) + pBase = (const char*)(seg->vmaddr - seg->fileoff + slide); // Adjust pBase depending on __LINKEDIT segment + } + else if(cmd->cmd == LC_SYMTAB) + { + const struct symtab_command* scmd = (const struct symtab_command*)cmd; + + // cmd->cmdsize must be size of struct, otherwise something is off; abort + if(cmd->cmdsize != sizeof(struct symtab_command)) + break; + + pSyms = (DLSyms*)dlAllocMem(sizeof(DLSyms)); + pSyms->symbolCount = scmd->nsyms; + pSyms->pStringTable = pBase + scmd->stroff; + pSyms->pSymbolTable = (struct NLIST_TYPE*)(pBase + scmd->symoff); + pSyms->pLib = pLib; + + return pSyms; + } + //@@@ handle also LC_DYSYMTAB + } + } + + // Couldn't init syms, so free lib and return error. + dlFreeLibrary(pLib); return NULL; } @@ -136,25 +150,33 @@ return pSyms ? pSyms->symbolCount : 0; } -static const struct NLIST_TYPE* get_nlist(DLSyms* pSyms, int index) + +const char* dlSymsName(DLSyms* pSyms, int index) { const struct NLIST_TYPE* nl; - if (!pSyms) + unsigned char t; + +//@@@ mach-o ref: http://www.cilinder.be/docs/next/NeXTStep/3.3/nd/DevTools/14_MachO/MachO.htmld/index.html + + if(!pSyms) return NULL; nl = pSyms->pSymbolTable + index; - if (nl->n_un.n_strx <= 1) - return NULL; // would be empty string anyway + + // Mach-O manual: Symbols with an index into the string table of zero + // (n_un.n_strx == 0) are defined to have a null ("") name. + if(nl->n_un.n_strx == 0) + return NULL; //@@@ have return pointer to some static "" string? + + // Skip undefined symbols. @@@ should we? + t = nl->n_type & N_TYPE; + if(t == N_UNDF || t == N_PBUD) // @@@ check if N_PBUD is defined, it's not in the NeXT manual, but on Darwin 8.0.1 + return NULL; //TODO skip more symbols based on nl->n_desc and nl->n_type ? - return nl; -} - -const char* dlSymsName(DLSyms* pSyms, int index) -{ - const struct NLIST_TYPE* nl = get_nlist(pSyms, index); - return nl ? pSyms->pStringTable + nl->n_un.n_strx : NULL; + // Return name - handles lookup of indirect names. + return &pSyms->pStringTable[t == N_INDR ? nl->n_value : nl->n_un.n_strx]; }