From 9e51aabc983dd4c4b7f9254c5cafd0aba4ea45ed Mon Sep 17 00:00:00 2001 From: David Chisnall Date: Thu, 28 Jan 2016 18:59:43 +0000 Subject: [PATCH] Fix block trampolines to work when mixed with Thumb-2 code. --- block_to_imp.c | 17 ++++++++++++++++- block_trampolines.S | 31 +++++++++++++++++++++++-------- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/block_to_imp.c b/block_to_imp.c index ee4a36b..ca402fd 100644 --- a/block_to_imp.c +++ b/block_to_imp.c @@ -109,6 +109,15 @@ static struct trampoline_set *alloc_trampolines(char *start, char *end) { struct trampoline_set *metadata = calloc(1, sizeof(struct trampoline_set)); metadata->buffers = valloc(sizeof(struct trampoline_buffers)); +#if ((__ARM_ARCH >= 7) || defined (__ARM_ARCH_6T2__)) + // If the trampoline is Thumb-2 code, then the linker will set this symbol + // to something that you can jump to with a b[l]x instruction, not to the + // actual start address. This code is safe on all supported architectures + // (as we don't have anything with 1-byte alignment requirements), but it + // is a couple of nops everywhere else, so don't bother with it. + start = (void*)((uintptr_t)start & ~1); + end = (void*)((uintptr_t)end & ~1); +#endif for (int i=0 ; ibuffers->headers[i].fnptr = (void(*)(void))invalid; @@ -167,7 +176,13 @@ IMP imp_implementationWithBlock(void *block) assert(set->first_free >= -1); h->fnptr = (void(*)(void))b->invoke; h->block = b; - return (IMP)&set->buffers->rx_buffer[i*sizeof(struct block_header)]; + uintptr_t addr = (uintptr_t)&set->buffers->rx_buffer[i*sizeof(struct block_header)]; +#if ((__ARM_ARCH >= 7) || defined (__ARM_ARCH_6T2__)) + // If the trampoline is Thumb-2 code, then we must set the low bit + // to 1 so that b[l]x instructions put the CPU in the correct mode. + addr |= 1; +#endif + return (IMP)addr; } } UNREACHABLE("Failed to allocate block"); diff --git a/block_trampolines.S b/block_trampolines.S index bd6417f..9ebbeaf 100644 --- a/block_trampolines.S +++ b/block_trampolines.S @@ -116,17 +116,32 @@ CDECL(__objc_block_trampoline_sret): trampoline x1, x2 CDECL(__objc_block_trampoline_end_sret): #elif __arm__ -CDECL(__objc_block_trampoline): +#if 0 && ((__ARM_ARCH >= 7) || defined (__ARM_ARCH_6T2__)) +// If we're on a target that supports Thumb 2, then we need slightly more +// instructions to support Thumb/ARM code for the IMP and so we need to make +// the trampolines thumb to be able to fit them in 16 bytes (they fit exactly +// when assembled as Thumb-2). +.thumb +.macro trampoline arg0, arg1 + sub r12, pc, #4095 + mov \arg0, \arg1 // Move self over _cmd + ldr \arg0, [r12, #-5] // Load the block pointer over self + ldr r12, [r12, #-1] // Jump to the block function + bx r12 +.endm +#else +.macro trampoline arg0, arg1 sub r12, pc, #4096 - mov r1, r0 // Move self over _cmd - ldr r0, [r12, #-8] // Load the block pointer over self - ldr pc, [r12, #-4] // Jump to the block function + mov \arg0, \arg1 // Move self over _cmd + ldr \arg0, [r12, #-8] // Load the block pointer over self + ldr pc, [r12, #-4] // Jump to the block function +.endm +#endif // ((__ARM_ARCH >= 7) || defined (__ARM_ARCH_6T2__)) +CDECL(__objc_block_trampoline): + trampoline r0, r1 CDECL(__objc_block_trampoline_end): CDECL(__objc_block_trampoline_sret): - sub r12, pc, #4096 - mov r2, r1 // Move self over _cmd - ldr r1, [r12, #-8] // Load the block pointer over self - ldr pc, [r12, #-4] // Jump to the block function + trampoline r1, r2 CDECL(__objc_block_trampoline_end_sret): #else #warning imp_implementationWithBlock() not implemented for your architecture