diff options
author | comex | 2015-01-19 19:12:32 -0500 |
---|---|---|
committer | comex | 2015-01-19 19:12:32 -0500 |
commit | c25b9e2337aad02073a199619f6f754f15cccd38 (patch) | |
tree | 603f3bcdc6eb687c11c077f350cde53fc1700898 /lib/darwin | |
parent | some reorganization (diff) | |
download | substitute-c25b9e2337aad02073a199619f6f754f15cccd38.tar.gz |
more reorganization - move OS X/iOS specific stuff into its own directory
Diffstat (limited to 'lib/darwin')
-rw-r--r-- | lib/darwin/find-syms.c | 142 | ||||
-rw-r--r-- | lib/darwin/interpose.c | 224 | ||||
-rw-r--r-- | lib/darwin/objc-asm.S | 72 | ||||
-rw-r--r-- | lib/darwin/objc.c | 205 | ||||
-rw-r--r-- | lib/darwin/objc.h | 23 | ||||
-rw-r--r-- | lib/darwin/substrate-compat.c | 55 |
6 files changed, 721 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__ */ diff --git a/lib/darwin/interpose.c b/lib/darwin/interpose.c new file mode 100644 index 0000000..06a357f --- /dev/null +++ b/lib/darwin/interpose.c @@ -0,0 +1,224 @@ +#ifdef __APPLE__ + +#include <stdint.h> +#include <stdbool.h> + +#include "substitute.h" +#include "substitute-internal.h" + +struct interpose_state { + size_t nsegments; + segment_command_x **segments; + size_t max_segments; + uintptr_t slide; + const struct substitute_import_hook *hooks; + size_t nhooks; + segment_command_x *stack_segments[32]; +}; + +static uintptr_t read_leb128(void **ptr, void *end, bool is_signed) { + uintptr_t result = 0; + uint8_t *p = *ptr; + uint8_t bit; + unsigned int shift = 0; + do { + if (p >= (uint8_t *) end) + return 0; + bit = *p++; + uintptr_t k = bit & 0x7f; + if (shift < sizeof(uintptr_t) * 8) + result |= k << shift; + shift += 7; + } while (bit & 0x80); + if (is_signed && (bit & 0x40)) + result |= ~((uintptr_t) 0) << shift; + *ptr = p; + return result; +} + +static inline char *read_cstring(void **ptr, void *end) { + char *s = *ptr; + *ptr = s + strnlen(s, (char *) end - s); + return s; +} + +static int try_bind_section(void *bind, size_t size, const struct interpose_state *st, + bool lazy) { + void *ptr = bind, *end = bind + size; + const char *sym = NULL; + uint8_t type = lazy ? BIND_TYPE_POINTER : 0; + intptr_t addend = 0; + size_t offset = 0; + void *segment = NULL; + while (ptr < end) { + uint8_t byte = *(uint8_t *) ptr; + ptr++; + uint8_t immediate = byte & BIND_IMMEDIATE_MASK; + uint8_t opcode = byte & BIND_OPCODE_MASK; + + uintptr_t count, stride; + + switch(opcode) { + case BIND_OPCODE_DONE: + case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: + case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: + break; + case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: + read_leb128(&ptr, end, false); + break; + case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: + sym = read_cstring(&ptr, end); + /* ignoring flags for now */ + break; + case BIND_OPCODE_SET_TYPE_IMM: + type = immediate; + break; + case BIND_OPCODE_SET_ADDEND_SLEB: + addend = read_leb128(&ptr, end, true); + break; + case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: + if (immediate < st->nsegments) + segment = (void *) (st->segments[immediate]->vmaddr + st->slide); + offset = read_leb128(&ptr, end, false); + break; + case BIND_OPCODE_ADD_ADDR_ULEB: + offset += read_leb128(&ptr, end, false); + break; + case BIND_OPCODE_DO_BIND: + count = 1; + stride = sizeof(void *); + goto bind; + case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: + count = 1; + stride = read_leb128(&ptr, end, false) + sizeof(void *); + goto bind; + case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: + count = 1; + stride = immediate * sizeof(void *) + sizeof(void *); + goto bind; + case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: + count = read_leb128(&ptr, end, false); + stride = read_leb128(&ptr, end, false) + sizeof(void *); + goto bind; + bind: + if (segment && sym) { + const struct substitute_import_hook *h; + size_t i; + for (i = 0; i < st->nhooks; i++) { + h = &st->hooks[i]; + if (!strcmp(sym, h->name)) + break; + } + if (i != st->nhooks) { + while (count--) { + uintptr_t new = (uintptr_t) h->replacement + addend; + uintptr_t old; + void *p = (void *) (segment + offset); + switch (type) { + case BIND_TYPE_POINTER: { + old = __atomic_exchange_n((uintptr_t *) p, new, __ATOMIC_RELAXED); + break; + } + case BIND_TYPE_TEXT_ABSOLUTE32: { + if ((uint32_t) new != new) { + /* text rels should only show up on i386, where + * this is impossible... */ + panic("bad TEXT_ABSOLUTE32 rel\n"); + } + old = __atomic_exchange_n((uint32_t *) p, (uint32_t) new, __ATOMIC_RELAXED); + break; + } + case BIND_TYPE_TEXT_PCREL32: { + uintptr_t pc = (uintptr_t) p + 4; + uintptr_t rel = new - pc; + if ((uint32_t) rel != rel) { + /* ditto */ + panic("bad TEXT_ABSOLUTE32 rel\n"); + } + old = __atomic_exchange_n((uint32_t *) p, (uint32_t) rel, __ATOMIC_RELAXED); + old += pc; + break; + } + default: + panic("unknown relocation type\n"); + break; + } + if (h->old_ptr) + *(uintptr_t *) h->old_ptr = old - addend; + } + break; + } + } + offset += count * stride; + break; + } + } + return SUBSTITUTE_OK; +} + +static void *off_to_addr(const struct interpose_state *st, uint32_t off) { + for (size_t i = 0; i < st->nsegments; i++) { + const segment_command_x *sc = st->segments[i]; + if ((off - sc->fileoff) < sc->filesize) + return (void *) (sc->vmaddr + st->slide + off - sc->fileoff); + } + return NULL; +} + +EXPORT +int substitute_interpose_imports(const struct substitute_image *image, + const struct substitute_import_hook *hooks, + size_t nhooks, int options) { + int ret = SUBSTITUTE_OK; + + if (options != 0) + panic("%s: unrecognized options\n", __func__); + + struct interpose_state st; + st.slide = image->slide; + st.nsegments = 0; + st.hooks = hooks; + st.nhooks = nhooks; + st.segments = st.stack_segments; + st.max_segments = sizeof(st.stack_segments) / sizeof(*st.stack_segments); + + const mach_header_x *mh = image->image_header; + const struct load_command *lc = (void *) (mh + 1); + for (uint32_t i = 0; i < mh->ncmds; i++) { + if (lc->cmd == LC_SEGMENT_X) { + segment_command_x *sc = (void *) lc; + if (st.nsegments == st.max_segments) { + segment_command_x **new = calloc(st.nsegments * 2, sizeof(*st.segments)); + if (!new) + substitute_panic("%s: out of memory\n", __func__); + memcpy(new, st.segments, st.nsegments * sizeof(*st.segments)); + if (st.segments != st.stack_segments) + free(st.segments); + st.segments = new; + } + st.segments[st.nsegments++] = sc; + } + lc = (void *) lc + lc->cmdsize; + } + + lc = (void *) (mh + 1); + for (uint32_t i = 0; i < mh->ncmds; i++) { + if (lc->cmd == LC_DYLD_INFO || lc->cmd == LC_DYLD_INFO_ONLY) { + struct dyld_info_command *dc = (void *) lc; + int ret; + if ((ret = try_bind_section(off_to_addr(&st, dc->bind_off), dc->bind_size, &st, false)) || + (ret = try_bind_section(off_to_addr(&st, dc->weak_bind_off), dc->weak_bind_size, &st, false)) || + (ret = try_bind_section(off_to_addr(&st, dc->lazy_bind_off), dc->lazy_bind_size, &st, true))) + goto fail; + + break; + } + lc = (void *) lc + lc->cmdsize; + } +fail: + if (st.segments != st.stack_segments) + free(st.segments); + return ret; +} + +#endif /* __APPLE__ */ diff --git a/lib/darwin/objc-asm.S b/lib/darwin/objc-asm.S new file mode 100644 index 0000000..bebce80 --- /dev/null +++ b/lib/darwin/objc-asm.S @@ -0,0 +1,72 @@ +#include "objc.h" +.text +.align _PAGE_SHIFT +#ifdef __arm__ +.thumb_func _remap_start +.thumb +#endif +.globl _remap_start +_remap_start: + +.set i, 0 +#define my_rpe (0b + (_PAGE_SIZE - i * TRAMPOLINE_SIZE + i * TRAMP_INFO_PAGE_ENTRY_SIZE)) +.rept TRAMPOLINES_PER_PAGE +0: +#if defined(__x86_64__) + /* double push for align */ + push %rdi; push %rsi; push %rdx; push %rcx; push %r8; push %r9; push %r9 + lea my_rpe(%rip), %rdx + mov 8(%rdx), %rdi + mov 16(%rdx), %rsi + call *(%rdx) + pop %r9; pop %r9; pop %r8; pop %rcx; pop %rdx; pop %rsi; pop %rdi + jmp *%rax +#elif defined(__i386__) + call 1f +1: + pop %edx + lea my_rpe-1b(%edx), %edx + push 8(%edx) + push 4(%edx) + call *(%edx) + add $$8, %esp + jmp *%eax +#elif defined(__arm__) + push {r0-r4, lr} /* r4 for align */ + mov r3, #(my_rpe - (1f + 2)) + add r3, pc +1: + ldr r0, [r3, #4] + ldr r1, [r3, #8] + ldr r2, [r3] + blx r2 + mov r9, r0 + pop {r0-r4, lr} + bx r9 +#elif defined(__arm64__) + stp x30, x8, [sp, #-0x10]! + stp x7, x6, [sp, #-0x10]! + stp x5, x4, [sp, #-0x10]! + stp x3, x2, [sp, #-0x10]! + stp x1, x0, [sp, #-0x10]! + + ldr x0, my_rpe+8 + ldr x1, my_rpe+0x10 + ldr x2, my_rpe + blr x2 + mov x9, x0 + + ldp x1, x0, [sp], #0x10 + ldp x3, x2, [sp], #0x10 + ldp x5, x4, [sp], #0x10 + ldp x7, x6, [sp], #0x10 + ldp x30, x8, [sp], #0x10 + + br x9 +#else +#error No forwarding assembly definition for this arch +#endif + +.set i, i + 1 +.endr + diff --git a/lib/darwin/objc.c b/lib/darwin/objc.c new file mode 100644 index 0000000..398e3e7 --- /dev/null +++ b/lib/darwin/objc.c @@ -0,0 +1,205 @@ +#if defined(__APPLE__) +#include "substitute.h" +#include "substitute-internal.h" +#include "objc.h" +#include <stddef.h> +#include <pthread.h> +#include <mach/mach.h> +#include <sys/mman.h> +#include <sys/queue.h> +#include <errno.h> + +/* These trampolines will call e->func(e->arg1, e->arg2), and jump to there, + * preserving all arguments. imp_implementationWithBlock would be easier and + * maybe a bit faster, but it's impossible to avoid throwing away registers + * without having to ask whether the selector is stret or not. */ + +struct tramp_info_page_header { + uint32_t magic; + uint32_t version; + struct tramp_info_page_entry *first_free; + size_t nfree; + LIST_ENTRY(tramp_info_page_header) free_pages; +}; + +enum { + TRAMP_MAGIC = 0xf00df17e, + TRAMP_VERSION = 0, +}; + +struct tramp_info_page_entry { + union { + struct tramp_info_page_entry *next_free; + void *func; + }; + void *arg1; + void *arg2; +}; + +_Static_assert(TRAMP_INFO_PAGE_ENTRY_SIZE == sizeof(struct tramp_info_page_entry), + "TRAMP_INFO_PAGE_ENTRY_SIZE"); +_Static_assert(sizeof(struct tramp_info_page_header) + + TRAMPOLINES_PER_PAGE * sizeof(struct tramp_info_page_entry) <= _PAGE_SIZE, + "header+entries too big"); + +static pthread_mutex_t tramp_mutex = PTHREAD_MUTEX_INITIALIZER; +LIST_HEAD(tramp_info_page_list, tramp_info_page_header) + tramp_free_page_list = LIST_HEAD_INITIALIZER(tramp_info_page_list); + +extern char remap_start[]; + +static int get_trampoline(void *func, void *arg1, void *arg2, void *tramp_ptr) { + int ret, rerrno = 0; + pthread_mutex_lock(&tramp_mutex); + + struct tramp_info_page_header *header = LIST_FIRST(&tramp_free_page_list); + if (!header) { + if (PAGE_SIZE > _PAGE_SIZE) + panic("%s: strange PAGE_SIZE %x\n", __func__, PAGE_SIZE); + void *new_pages = mmap(NULL, _PAGE_SIZE * 2, PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_ANON, -1, 0); + if (new_pages == MAP_FAILED) { + ret = SUBSTITUTE_ERR_OOM; + rerrno = errno; + goto out; + } + vm_address_t tramp_page = (vm_address_t) new_pages; + vm_prot_t cur_prot, max_prot; + kern_return_t kr = vm_remap( + mach_task_self(), + &tramp_page, + _PAGE_SIZE, + _PAGE_SIZE - 1, + VM_FLAGS_OVERWRITE | VM_FLAGS_FIXED, + mach_task_self(), + (vm_address_t) remap_start, + FALSE, /* copy */ + &cur_prot, + &max_prot, + VM_INHERIT_NONE); + if (kr != KERN_SUCCESS || tramp_page != (vm_address_t) new_pages) { + ret = SUBSTITUTE_ERR_VM; + goto out; + } + header = new_pages + _PAGE_SIZE * 2 - sizeof(*header); + header->magic = TRAMP_MAGIC; + header->version = TRAMP_VERSION; + header->first_free = NULL; + header->nfree = TRAMPOLINES_PER_PAGE; + LIST_INSERT_HEAD(&tramp_free_page_list, header, free_pages); + } + + void *page = (void *) (((uintptr_t) header) & ~(_PAGE_SIZE - 1)); + struct tramp_info_page_entry *entries = page; + struct tramp_info_page_entry *entry = header->first_free; + if (entry == NULL) { + entry = &entries[TRAMPOLINES_PER_PAGE - header->nfree]; + entry->next_free = NULL; + } + + header->first_free = entry->next_free; + if (--header->nfree == 0) + LIST_REMOVE(header, free_pages); + + entry->func = func; + entry->arg1 = arg1; + entry->arg2 = arg2; + void *tramp = (page - PAGE_SIZE) + (entry - entries) * TRAMPOLINE_SIZE; +#ifdef __arm__ + tramp += 1; +#endif + *(void **) tramp_ptr = tramp; + ret = SUBSTITUTE_OK; +out: + pthread_mutex_unlock(&tramp_mutex); + errno = rerrno; + return ret; +} + +static void free_trampoline(void *tramp) { + pthread_mutex_lock(&tramp_mutex); + void *page = (void *) (((uintptr_t) tramp) & ~(_PAGE_SIZE - 1)); + size_t i = (tramp - page) / TRAMPOLINE_SIZE; + struct tramp_info_page_entry *entries = page + _PAGE_SIZE; + struct tramp_info_page_entry *entry = &entries[i]; + struct tramp_info_page_header *header = page + 2 * _PAGE_SIZE - sizeof(*header); + + if (header->magic != TRAMP_MAGIC) + panic("%s: bad pointer\n", __func__); + if (header->version != TRAMP_VERSION) { + /* shouldn't happen, but just in case multiple versions of this library + * are mixed up */ + return; + } + + entry->next_free = header->first_free; + header->first_free = entry; + header->nfree++; + if (header->nfree == 1) + LIST_INSERT_HEAD(&tramp_free_page_list, header, free_pages); + else if (header->nfree == TRAMPOLINES_PER_PAGE && + /* have others? */ + (LIST_FIRST(&tramp_free_page_list) != header || + LIST_NEXT(header, free_pages))) { + /* free the trampoline and info pages */ + LIST_REMOVE(header, free_pages); + munmap(page, 2 * _PAGE_SIZE); + } + + pthread_mutex_unlock(&tramp_mutex); +} + +static IMP dereference(IMP *old_ptr, UNUSED void *_) { + return *old_ptr; +} + +EXPORT +int substitute_hook_objc_message(Class class, SEL selector, void *replacement, + void *old_ptr, bool *created_imp_ptr) { + int ret; + Method meth = class_getInstanceMethod(class, selector); + if (meth == NULL) + return SUBSTITUTE_ERR_NO_SUCH_SELECTOR; + const char *types = method_getTypeEncoding(meth); + + if (created_imp_ptr) + *created_imp_ptr = false; + + /* temporary trampoline just tries again */ + IMP temp = NULL; + if (old_ptr) { + if ((ret = get_trampoline(dereference, old_ptr, NULL, &temp))) + return ret; + *(IMP *) old_ptr = temp; + } + + IMP old = class_replaceMethod(class, selector, replacement, types); + if (old) { + if (old_ptr) + *(IMP *) old_ptr = old; + } else { + if (old_ptr) { + Class super = class_getSuperclass(class); + if (!super) { + /* this ought to only be possible if the method was removed in + * the meantime, since we found the method above and it + * couldn't have been found in a superclass, but the objc2 + * runtime doesn't allow removing methods. */ + panic("%s: no superclass but the method didn't exist\n", __func__); + } + ret = get_trampoline(class_getMethodImplementation, super, selector, old_ptr); + if (created_imp_ptr) + *created_imp_ptr = true; + } + } + + if (temp) + free_trampoline(temp); + return SUBSTITUTE_OK; +} + +EXPORT +void substitute_free_created_imp(IMP imp) { + free_trampoline(imp); +} +#endif diff --git a/lib/darwin/objc.h b/lib/darwin/objc.h new file mode 100644 index 0000000..9628c35 --- /dev/null +++ b/lib/darwin/objc.h @@ -0,0 +1,23 @@ +#pragma once +/* PAGE_SIZE is not actually a constant on iOS */ +#if defined(__arm64__) +#define _PAGE_SHIFT 14 +#else +#define _PAGE_SHIFT 12 +#endif +#define _PAGE_SIZE (1 << _PAGE_SHIFT) +#if defined(__x86_64__) +#define TRAMPOLINE_SIZE 0x27 +#elif defined(__i386__) +#define TRAMPOLINE_SIZE 0x19 +#elif defined(__arm__) +#define TRAMPOLINE_SIZE 0x18 +#elif defined(__arm64__) +#define TRAMPOLINE_SIZE 0x40 +#endif +#ifdef __LP64__ +#define TRAMP_INFO_PAGE_ENTRY_SIZE 24 +#else +#define TRAMP_INFO_PAGE_ENTRY_SIZE 12 +#endif +#define TRAMPOLINES_PER_PAGE (_PAGE_SIZE / TRAMPOLINE_SIZE) diff --git a/lib/darwin/substrate-compat.c b/lib/darwin/substrate-compat.c new file mode 100644 index 0000000..3658ac7 --- /dev/null +++ b/lib/darwin/substrate-compat.c @@ -0,0 +1,55 @@ +#include "substitute.h" +#include "substitute-internal.h" +#include <syslog.h> +#include <mach-o/dyld.h> + +EXPORT +void *SubGetImageByName(const char *filename) __asm__("SubGetImageByName"); +void *SubGetImageByName(const char *filename) { + return substitute_open_image(filename); +} + +EXPORT +void *SubFindSymbol(void *image, const char *name) __asm__("SubFindSymbol"); +void *SubFindSymbol(void *image, const char *name) { + if (!image) { + const char *s = "SubFindSymbol: 'any image' specified, which is incredibly slow - like, 2ms on a fast x86. I'm going to do it since it seems to be somewhat common, but you should be ashamed of yourself."; + syslog(LOG_WARNING, "%s", s); + fprintf(stderr, "%s\n", s); + /* and it isn't thread safe, but neither is MS */ + for(uint32_t i = 0; i < _dyld_image_count(); i++) { + const char *im_name = _dyld_get_image_name(i); + struct substitute_image *im = substitute_open_image(im_name); + if (!im) { + fprintf(stderr, "(btw, couldn't open %s?)\n", im_name); + continue; + } + void *r = SubFindSymbol(im, name); + substitute_close_image(im); + if (r) + return r; + } + return NULL; + } + + substitute_sym *sym; + if (substitute_find_private_syms(image, &name, &sym, 1) || !sym) + return NULL; + return substitute_sym_to_ptr(image, sym); +} + +/* +EXPORT +void SubHookFunction(void *symbol, void *replace, void **result) __asm__("SubHookFunction"); +void SubHookFunction(void *symbol, void *replace, void **result) { + // ... +} +*/ + +#ifdef __APPLE__ +/*void SubHookMessageEx(Class _class, SEL sel, IMP imp, IMP *result) __asm__("SubHookMessageEx"); +void SubHookMessageEx(Class _class, SEL sel, IMP imp, IMP *result) { + +}*/ + +#endif |