aboutsummaryrefslogtreecommitdiff
path: root/lib/darwin/find-syms.c
diff options
context:
space:
mode:
authorcomex2015-01-19 19:12:32 -0500
committercomex2015-01-19 19:12:32 -0500
commitc25b9e2337aad02073a199619f6f754f15cccd38 (patch)
tree603f3bcdc6eb687c11c077f350cde53fc1700898 /lib/darwin/find-syms.c
parentsome reorganization (diff)
downloadsubstitute-c25b9e2337aad02073a199619f6f754f15cccd38.tar.gz
more reorganization - move OS X/iOS specific stuff into its own directory
Diffstat (limited to 'lib/darwin/find-syms.c')
-rw-r--r--lib/darwin/find-syms.c142
1 files changed, 142 insertions, 0 deletions
diff --git a/lib/darwin/find-syms.c b/lib/darwin/find-syms.c
new file mode 100644
index 0000000..44adf78
--- /dev/null
+++ b/lib/darwin/find-syms.c
@@ -0,0 +1,142 @@
+#ifdef __APPLE__
+
+#include <stdbool.h>
+#include <dlfcn.h>
+#include <pthread.h>
+
+#include "substitute.h"
+#include "substitute-internal.h"
+
+extern const struct dyld_all_image_infos *_dyld_get_all_image_infos();
+
+static pthread_once_t dyld_inspect_once = PTHREAD_ONCE_INIT;
+/* and its fruits: */
+static uintptr_t (*ImageLoaderMachO_getSlide)(void *);
+static const struct mach_header *(*ImageLoaderMachO_machHeader)(void *);
+
+static void *sym_to_ptr(substitute_sym *sym, intptr_t slide) {
+ uintptr_t addr = sym->n_value;
+ addr += slide;
+ if (sym->n_desc & N_ARM_THUMB_DEF)
+ addr |= 1;
+ 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);
+
+ /* note: no verification at all */
+ const mach_header_x *mh = hdr;
+ uint32_t ncmds = mh->ncmds;
+ struct load_command *lc = (void *) (mh + 1);
+ struct symtab_command syc;
+ for (uint32_t i = 0; i < ncmds; i++) {
+ if (lc->cmd == LC_SYMTAB) {
+ syc = *(struct symtab_command *) lc;
+ goto ok;
+ }
+ lc = (void *) lc + lc->cmdsize;
+ }
+ return; /* 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;
+ }
+ if (symtab && strtab)
+ goto ok2;
+ }
+ lc = (void *) lc + lc->cmdsize;
+ }
+ return; /* uh... weird */
+ok2: ;
+ symtab = (void *) symtab + *slide;
+ strtab = (void *) strtab + *slide;
+ /* This could be optimized for efficiency with a large number of names... */
+ for (uint32_t i = 0; i < syc.nsyms; i++) {
+ substitute_sym *sym = &symtab[i];
+ uint32_t strx = sym->n_un.n_strx;
+ const char *name = strx == 0 ? "" : strtab + strx;
+ for (size_t j = 0; j < count; j++) {
+ if (!strcmp(name, names[j])) {
+ syms[j] = sym;
+ break;
+ }
+ }
+ }
+}
+
+/* This is a mess because the usual _dyld_image_count loop is not thread safe.
+ * Since it uses a std::vector and (a) erases from it (making it possible for a
+ * loop to skip entries) and (b) and doesn't even lock it in
+ * _dyld_get_image_header etc., this is true even if the image is guaranteed to
+ * be found, including the possibility to crash. How do we solve this?
+ * Inception - we steal dyld's private symbols... We could avoid the symbols
+ * by calling the vtable of dlopen handles, but that seems unstable. As is,
+ * the method used is somewhat convoluted in an attempt to maximize stability.
+ */
+
+static void inspect_dyld() {
+ const struct dyld_all_image_infos *aii = _dyld_get_all_image_infos();
+ const void *dyld_hdr = aii->dyldImageLoadAddress;
+
+ 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);
+ 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);
+
+ void *dlhandle = dlopen(filename, RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD);
+ if (!dlhandle)
+ return NULL;
+
+ const void *image_header = ImageLoaderMachO_machHeader(dlhandle);
+ intptr_t slide = ImageLoaderMachO_getSlide(dlhandle);
+
+ struct substitute_image *im = malloc(sizeof(*im));
+ if (!im)
+ return NULL;
+ im->slide = slide;
+ im->dlhandle = dlhandle;
+ im->image_header = image_header;
+ return im;
+}
+
+EXPORT
+void substitute_close_image(struct substitute_image *im) {
+ dlclose(im->dlhandle); /* ignore errors */
+ free(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);
+ return SUBSTITUTE_OK;
+}
+
+EXPORT
+void *substitute_sym_to_ptr(struct substitute_image *handle, substitute_sym *sym) {
+ return sym_to_ptr(sym, handle->slide);
+}
+
+#endif /* __APPLE__ */