diff options
Diffstat (limited to 'lib/darwin/find-syms.c')
-rw-r--r-- | lib/darwin/find-syms.c | 87 |
1 files changed, 60 insertions, 27 deletions
diff --git a/lib/darwin/find-syms.c b/lib/darwin/find-syms.c index 44adf78..f2261f0 100644 --- a/lib/darwin/find-syms.c +++ b/lib/darwin/find-syms.c @@ -6,6 +6,7 @@ #include "substitute.h" #include "substitute-internal.h" +#include "find-syms.h" extern const struct dyld_all_image_infos *_dyld_get_all_image_infos(); @@ -22,11 +23,23 @@ static void *sym_to_ptr(substitute_sym *sym, intptr_t slide) { return (void *) addr; } -static void find_syms_raw(const void *hdr, intptr_t *slide, const char **names, substitute_sym **syms, size_t count) { - memset(syms, 0, sizeof(*syms) * count); +static bool find_slide(const mach_header_x *mh, intptr_t *slidep) { + uint32_t ncmds = mh->ncmds; + struct load_command *lc = (void *) (mh + 1); + for (uint32_t i = 0; i < ncmds; i++) { + if (lc->cmd == LC_SEGMENT_X) { + segment_command_x *sc = (void *) lc; + if (sc->fileoff == 0) { + *slidep = (uintptr_t) mh - sc->vmaddr; + return true; + } + } + lc = (void *) lc + lc->cmdsize; + } + return false; +} - /* note: no verification at all */ - const mach_header_x *mh = hdr; +bool find_symtab_data(const mach_header_x *mh, struct symtab_data *data) { uint32_t ncmds = mh->ncmds; struct load_command *lc = (void *) (mh + 1); struct symtab_command syc; @@ -37,38 +50,45 @@ static void find_syms_raw(const void *hdr, intptr_t *slide, const char **names, } lc = (void *) lc + lc->cmdsize; } - return; /* no symtab, no symbols */ + return false; /* no symtab, no symbols */ ok: ; - substitute_sym *symtab = NULL; - const char *strtab = NULL; lc = (void *) (mh + 1); for (uint32_t i = 0; i < ncmds; i++) { if (lc->cmd == LC_SEGMENT_X) { segment_command_x *sc = (void *) lc; - if (syc.symoff - sc->fileoff < sc->filesize) - symtab = (void *) sc->vmaddr + syc.symoff - sc->fileoff; - if (syc.stroff - sc->fileoff < sc->filesize) - strtab = (void *) sc->vmaddr + syc.stroff - sc->fileoff; - if (*slide == -1 && sc->fileoff == 0) { - // used only for dyld - *slide = (uintptr_t) hdr - sc->vmaddr; + uint32_t seg_symoff = syc.symoff - sc->fileoff; + uint32_t seg_stroff = syc.stroff - sc->fileoff; + if (seg_symoff < sc->filesize && + syc.nsyms <= (sc->filesize - seg_symoff) / sizeof(substitute_sym) && + seg_stroff < sc->filesize && + syc.strsize <= sc->filesize - seg_stroff) { + data->linkedit_vmaddr = sc->vmaddr; + data->linkedit_symoff = seg_symoff; + data->nsyms = syc.nsyms; + data->linkedit_stroff = seg_stroff; + data->strsize = syc.strsize; + return true; } - if (symtab && strtab) - goto ok2; } lc = (void *) lc + lc->cmdsize; } - return; /* uh... weird */ -ok2: ; - symtab = (void *) symtab + *slide; - strtab = (void *) strtab + *slide; + return false; /* not in any segment? */ +} + +void find_syms_raw(const struct symtab_data *restrict data, void *linkedit, + const char **restrict names, substitute_sym **restrict syms, + size_t count) { + memset(syms, 0, count * sizeof(*syms)); + substitute_sym *symtab = linkedit + data->linkedit_symoff; + const char *strtab = linkedit + data->linkedit_stroff; /* This could be optimized for efficiency with a large number of names... */ - for (uint32_t i = 0; i < syc.nsyms; i++) { + for (uint32_t i = 0; i < data->nsyms; i++) { substitute_sym *sym = &symtab[i]; uint32_t strx = sym->n_un.n_strx; - const char *name = strx == 0 ? "" : strtab + strx; + const char *name = (strx == 0 || strx >= data->strsize) ? "" : strtab + strx; + size_t maxlen = data->strsize - strx; for (size_t j = 0; j < count; j++) { - if (!strcmp(name, names[j])) { + if (!strncmp(name, names[j], maxlen)) { syms[j] = sym; break; } @@ -93,14 +113,18 @@ static void inspect_dyld() { const char *names[2] = { "__ZNK16ImageLoaderMachO8getSlideEv", "__ZNK16ImageLoaderMachO10machHeaderEv" }; substitute_sym *syms[2]; intptr_t dyld_slide = -1; - find_syms_raw(dyld_hdr, &dyld_slide, names, syms, 2); + struct symtab_data symtab_data; + if (!find_slide(dyld_hdr, &dyld_slide) || + !find_symtab_data(dyld_hdr, &symtab_data)) + substitute_panic("couldn't find ImageLoader methods\n"); + void *linkedit = (void *) (symtab_data.linkedit_vmaddr + dyld_slide); + find_syms_raw(&symtab_data, linkedit, names, syms, 2); if (!syms[0] || !syms[1]) substitute_panic("couldn't find ImageLoader methods\n"); ImageLoaderMachO_getSlide = sym_to_ptr(syms[0], dyld_slide); ImageLoaderMachO_machHeader = sym_to_ptr(syms[1], dyld_slide); } -/* 'dlopen_header' keeps the image alive */ EXPORT struct substitute_image *substitute_open_image(const char *filename) { pthread_once(&dyld_inspect_once, inspect_dyld); @@ -129,8 +153,17 @@ void substitute_close_image(struct substitute_image *im) { EXPORT int substitute_find_private_syms(struct substitute_image *im, const char **names, - substitute_sym **syms, size_t nsyms) { - find_syms_raw(im->image_header, &im->slide, names, syms, nsyms); + void **syms, size_t nsyms) { + struct symtab_data symtab_data; + if (!find_symtab_data(im->image_header, &symtab_data)) + return SUBSTITUTE_OK; + void *linkedit = (void *) (symtab_data.linkedit_vmaddr + im->slide); + substitute_sym **ssyms = (void *) syms; + find_syms_raw(&symtab_data, linkedit, names, ssyms, nsyms); + for (size_t i = 0; i < nsyms; i++) { + if (ssyms[i]) + syms[i] = sym_to_ptr(ssyms[i], im->slide); + } return SUBSTITUTE_OK; } |