aboutsummaryrefslogtreecommitdiff
path: root/lib/darwin/find-syms.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/darwin/find-syms.c')
-rw-r--r--lib/darwin/find-syms.c87
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;
}