tlv_thunk.h (2148B)
1 #ifndef KIT_JIT_TLV_THUNK_H 2 #define KIT_JIT_TLV_THUNK_H 3 4 /* The JIT-time TLV thunk for Mach-O thread-local access. 5 * 6 * kit's codegen emits the Apple TLV access sequence whenever a Mach-O 7 * target dereferences a `_Thread_local`: 8 * 9 * adrp x0, sym@TLVPPAGE 10 * ldr x0, [x0, sym@TLVPPAGEOFF] ; x0 = descriptor 11 * ldr x1, [x0] ; x1 = descriptor[+0] (thunk*) 12 * blr x1 ; thunk(x0=desc) -> x0=TLV addr 13 * 14 * The thunk's ABI is custom: x0 in/out as the descriptor / per-thread 15 * TLV address, every other GPR and SIMD register preserved. In an AOT 16 * Mach-O image dyld rewrites descriptor[+0] to a libdyld-supplied thunk 17 * after allocating a pthread key per descriptor; the JIT image is never 18 * walked by dyld, so we install our own thunk and patch every 19 * descriptor's slot[0]/[1]/[2] from `kit_jit_from_image`. 20 * 21 * Per-image descriptor convention (post-patch): 22 * [+0] : &kit_jit_tlv_thunk (entry below) 23 * [+8] : opaque KitJitTls ctx pointer (per JIT image) 24 * [+16] : byte offset within the per-thread TLS block (image-relative) 25 * 26 * Contract on the ctx pointer (set by KitJitTls.ctx_new): its first 8 27 * bytes are a function pointer `void* (*get_block)(void* ctx)` that 28 * returns the calling thread's TLS block (lazy-allocating + seeding 29 * from the image's init bytes on first per-thread call). 30 * 31 * The thunk does roughly: 32 * 33 * void* thunk(void* desc) { 34 * void* ctx = *(void**)((u8*)desc + 8); 35 * void* (*get_block)(void*) = *(void**)ctx; 36 * void* base = get_block(ctx); 37 * return (u8*)base + *(u64*)((u8*)desc + 16); 38 * } 39 * 40 * Calling `get_block` from a context that must preserve x1..x18 / q0..q7 41 * is the reason the thunk is implemented in asm — a normal C function 42 * call would clobber caller-saved regs the JITed access sequence has no 43 * idea about. */ 44 45 /* Declared as a function for &-of, but its calling convention is the 46 * custom one described above: callers must come through the access 47 * sequence, not a plain C call. */ 48 void kit_jit_tlv_thunk(void); 49 50 #endif