#include #include #include #include #include #include #include "objc/runtime.h" #include "objc/blocks_runtime.h" #include "blocks_runtime.h" #include "lock.h" #include "visibility.h" #define PAGE_SIZE 4096 static void *executeBuffer; static void *writeBuffer; static ptrdiff_t offset; static mutex_t trampoline_lock; static char *tmpPattern; struct wx_buffer { void *w; void *x; }; PRIVATE void init_trampolines(void) { INIT_LOCK(trampoline_lock); char *tmp = getenv("TMPDIR"); if (NULL == tmp) { tmp = "/tmp/"; } if (0 > asprintf(&tmpPattern, "%s/objc_trampolinesXXXXXXXXXXX", tmp)) { abort(); } } static struct wx_buffer alloc_buffer(size_t size) { LOCK_FOR_SCOPE(&trampoline_lock); if ((0 == offset) || (offset + size >= PAGE_SIZE)) { int fd = mkstemp(tmpPattern); unlink(tmpPattern); ftruncate(fd, PAGE_SIZE); void *w = mmap(NULL, PAGE_SIZE, PROT_WRITE, MAP_SHARED, fd, 0); executeBuffer = mmap(NULL, PAGE_SIZE, PROT_READ|PROT_EXEC, MAP_SHARED, fd, 0); *((void**)w) = writeBuffer; writeBuffer = w; offset = sizeof(void*); } struct wx_buffer b = { writeBuffer + offset, executeBuffer + offset }; offset += size; return b; } extern void __objc_block_trampoline; extern void __objc_block_trampoline_end; extern void __objc_block_trampoline_sret; extern void __objc_block_trampoline_end_sret; IMP imp_implementationWithBlock(void *block) { struct block_literal *b = block; void *start; void *end; if ((b->flags & BLOCK_USE_SRET) == BLOCK_USE_SRET) { start = &__objc_block_trampoline_sret; end = &__objc_block_trampoline_end_sret; } else { start = &__objc_block_trampoline; end = &__objc_block_trampoline_end; } size_t trampolineSize = end - start; // If we don't have a trampoline intrinsic for this architecture, return a // null IMP. if (0 >= trampolineSize) { return 0; } struct wx_buffer buf = alloc_buffer(trampolineSize + 2*sizeof(void*)); void **out = buf.w; out[0] = (void*)b->invoke; out[1] = Block_copy(b); memcpy(&out[2], start, trampolineSize); out = buf.x; return (IMP)&out[2]; } static void* isBlockIMP(void *anIMP) { LOCK(&trampoline_lock); void *e = executeBuffer; void *w = writeBuffer; UNLOCK(&trampoline_lock); while (e) { if ((anIMP > e) && (anIMP < e + PAGE_SIZE)) { return ((char*)w) + ((char*)anIMP - (char*)e); } e = *(void**)e; w = *(void**)w; } return 0; } void *imp_getBlock(IMP anImp) { if (0 == isBlockIMP((void*)anImp)) { return 0; } return *(((void**)anImp) - 1); } BOOL imp_removeBlock(IMP anImp) { void *w = isBlockIMP((void*)anImp); if (0 == w) { return NO; } Block_release(((void**)anImp) - 1); return YES; }