From c25b9e2337aad02073a199619f6f754f15cccd38 Mon Sep 17 00:00:00 2001 From: comex Date: Mon, 19 Jan 2015 19:12:32 -0500 Subject: more reorganization - move OS X/iOS specific stuff into its own directory --- Makefile | 17 ++-- lib/darwin/find-syms.c | 142 ++++++++++++++++++++++++++ lib/darwin/interpose.c | 224 ++++++++++++++++++++++++++++++++++++++++++ lib/darwin/objc-asm.S | 72 ++++++++++++++ lib/darwin/objc.c | 205 ++++++++++++++++++++++++++++++++++++++ lib/darwin/objc.h | 23 +++++ lib/darwin/substrate-compat.c | 55 +++++++++++ lib/find-syms.c | 142 -------------------------- lib/interpose.c | 224 ------------------------------------------ lib/objc-asm.S | 72 -------------- lib/objc.c | 205 -------------------------------------- lib/objc.h | 23 ----- lib/substrate-compat.c | 55 ----------- test/test-imp-forwarding.m | 2 +- 14 files changed, 731 insertions(+), 730 deletions(-) create mode 100644 lib/darwin/find-syms.c create mode 100644 lib/darwin/interpose.c create mode 100644 lib/darwin/objc-asm.S create mode 100644 lib/darwin/objc.c create mode 100644 lib/darwin/objc.h create mode 100644 lib/darwin/substrate-compat.c delete mode 100644 lib/find-syms.c delete mode 100644 lib/interpose.c delete mode 100644 lib/objc-asm.S delete mode 100644 lib/objc.c delete mode 100644 lib/objc.h delete mode 100644 lib/substrate-compat.c diff --git a/Makefile b/Makefile index 4004dfd..d8d5cdf 100644 --- a/Makefile +++ b/Makefile @@ -1,9 +1,10 @@ # todo CC := clang CXX := clang++ -CFLAGS := -O3 -Wall -Wextra -Werror -arch x86_64 -override CC := $(CC) $(CFLAGS) -override CXX := $(CXX) $(CFLAGS) -fno-exceptions -fno-asynchronous-unwind-tables +ARCH := -arch x86_64 +XCFLAGS := -O3 -Wall -Wextra -Werror -Ilib $(ARCH) +override CC := $(CC) $(XCFLAGS) $(CFLAGS) +override CXX := $(CXX) $(XCFLAGS) $(CFLAGS) -fno-exceptions -fno-asynchronous-unwind-tables LIB_LDFLAGS := -lobjc -dynamiclib -fvisibility=hidden IMAON2 := /Users/comex/c/imaon2 GEN_JS := node --harmony --harmony_arrow_functions $(IMAON2)/tables/gen.js @@ -36,11 +37,11 @@ out/jump-dis.o: $(GENERATED) out/transform-dis.o: $(GENERATED) LIB_OBJS := \ - out/find-syms.o \ - out/interpose.o \ - out/objc.o \ - out/objc-asm.o \ - out/substrate-compat.o \ + out/darwin/find-syms.o \ + out/darwin/interpose.o \ + out/darwin/objc-asm.o \ + out/darwin/objc.o \ + out/darwin/substrate-compat.o \ out/jump-dis.o \ out/transform-dis.o 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 +#include +#include + +#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 +#include + +#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 +#include +#include +#include +#include +#include + +/* 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 +#include + +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 diff --git a/lib/find-syms.c b/lib/find-syms.c deleted file mode 100644 index 44adf78..0000000 --- a/lib/find-syms.c +++ /dev/null @@ -1,142 +0,0 @@ -#ifdef __APPLE__ - -#include -#include -#include - -#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/interpose.c b/lib/interpose.c deleted file mode 100644 index 06a357f..0000000 --- a/lib/interpose.c +++ /dev/null @@ -1,224 +0,0 @@ -#ifdef __APPLE__ - -#include -#include - -#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/objc-asm.S b/lib/objc-asm.S deleted file mode 100644 index bebce80..0000000 --- a/lib/objc-asm.S +++ /dev/null @@ -1,72 +0,0 @@ -#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/objc.c b/lib/objc.c deleted file mode 100644 index 398e3e7..0000000 --- a/lib/objc.c +++ /dev/null @@ -1,205 +0,0 @@ -#if defined(__APPLE__) -#include "substitute.h" -#include "substitute-internal.h" -#include "objc.h" -#include -#include -#include -#include -#include -#include - -/* 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/objc.h b/lib/objc.h deleted file mode 100644 index 9628c35..0000000 --- a/lib/objc.h +++ /dev/null @@ -1,23 +0,0 @@ -#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/substrate-compat.c b/lib/substrate-compat.c deleted file mode 100644 index 3658ac7..0000000 --- a/lib/substrate-compat.c +++ /dev/null @@ -1,55 +0,0 @@ -#include "substitute.h" -#include "substitute-internal.h" -#include -#include - -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 diff --git a/test/test-imp-forwarding.m b/test/test-imp-forwarding.m index 98898e1..94f0d36 100644 --- a/test/test-imp-forwarding.m +++ b/test/test-imp-forwarding.m @@ -1,4 +1,4 @@ -#include "../lib/objc.c" +#include "../lib/darwin/objc.c" #include #include #include -- cgit v1.2.3