kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

jit_tls_posix.c (3881B)


      1 /* pthread_key-backed KitJitTls. Backs `kit run` on Mach-O targets:
      2  * every JIT image with TLS gets one pthread_key, the per-thread block is
      3  * allocated lazily on first access, and freed via the key's destructor
      4  * when the thread exits.
      5  *
      6  * The ctx layout is fixed by the contract in src/jit/tlv_thunk.h: the
      7  * first 8 bytes MUST be a function pointer the asm thunk calls with
      8  * x0 = ctx and expects back an x0 = TLS block. We satisfy this by making
      9  * `get_block` the first field. */
     10 
     11 #include <pthread.h>
     12 #include <stdio.h>
     13 #include <stdlib.h>
     14 #include <string.h>
     15 
     16 #include "env_posix.h"
     17 
     18 typedef struct JitTlsCtx {
     19   void* (*get_block)(void* ctx); /* first; matches tlv_thunk's expectation */
     20   pthread_key_t key;
     21   size_t image_size;
     22   size_t image_filesz;
     23   size_t align;
     24   void* init_bytes; /* heap-owned copy of init bytes, or NULL if all BSS */
     25 } JitTlsCtx;
     26 
     27 static void jit_tls_thread_dtor(void* block) {
     28   /* POSIX pthread_key destructor: called when a thread that touched the
     29    * TLV exits. `block` is the void* set by pthread_setspecific, never
     30    * NULL (POSIX skips the destructor if it was NULL). */
     31   free(block);
     32 }
     33 
     34 static void* jit_tls_alloc_block(JitTlsCtx* ctx) {
     35   /* macOS aligned_alloc requires alignment >= sizeof(void*); bump
     36    * smaller request alignments up. Size must be a multiple of
     37    * alignment too. */
     38   size_t a = ctx->align ? ctx->align : sizeof(void*);
     39   size_t sz;
     40   void* block;
     41   if (a < sizeof(void*)) a = sizeof(void*);
     42   sz = (ctx->image_size + a - 1u) & ~(a - 1u);
     43   if (sz == 0) sz = a; /* zero-size TLS image still needs a non-NULL block */
     44   block = aligned_alloc(a, sz);
     45   if (!block) return NULL;
     46   if (ctx->image_filesz && ctx->init_bytes)
     47     memcpy(block, ctx->init_bytes, ctx->image_filesz);
     48   if (ctx->image_size > ctx->image_filesz)
     49     memset((char*)block + ctx->image_filesz, 0,
     50            ctx->image_size - ctx->image_filesz);
     51   return block;
     52 }
     53 
     54 static void* jit_tls_get_block(void* ctx_v) {
     55   JitTlsCtx* ctx = (JitTlsCtx*)ctx_v;
     56   void* block = pthread_getspecific(ctx->key);
     57   if (block) return block;
     58   block = jit_tls_alloc_block(ctx);
     59   if (!block) {
     60     fprintf(stderr, "kit run: out of memory allocating per-thread TLS block\n");
     61     abort();
     62   }
     63   if (pthread_setspecific(ctx->key, block) != 0) {
     64     fprintf(stderr, "kit run: pthread_setspecific failed in TLV thunk\n");
     65     abort();
     66   }
     67   return block;
     68 }
     69 
     70 static void* jit_tls_ctx_new(void* user, const void* init_bytes,
     71                              size_t image_filesz, size_t image_size,
     72                              size_t align) {
     73   JitTlsCtx* ctx;
     74   (void)user;
     75   ctx = (JitTlsCtx*)malloc(sizeof(*ctx));
     76   if (!ctx) return NULL;
     77   ctx->get_block = jit_tls_get_block;
     78   ctx->image_size = image_size;
     79   ctx->image_filesz = image_filesz;
     80   ctx->align = align ? align : sizeof(void*);
     81   ctx->init_bytes = NULL;
     82   if (image_filesz && init_bytes) {
     83     ctx->init_bytes = malloc(image_filesz);
     84     if (!ctx->init_bytes) {
     85       free(ctx);
     86       return NULL;
     87     }
     88     memcpy(ctx->init_bytes, init_bytes, image_filesz);
     89   }
     90   if (pthread_key_create(&ctx->key, jit_tls_thread_dtor) != 0) {
     91     free(ctx->init_bytes);
     92     free(ctx);
     93     return NULL;
     94   }
     95   return ctx;
     96 }
     97 
     98 static void jit_tls_ctx_destroy(void* user, void* ctx_v) {
     99   JitTlsCtx* ctx = (JitTlsCtx*)ctx_v;
    100   void* my_block;
    101   (void)user;
    102   if (!ctx) return;
    103   /* Free the calling thread's block (POSIX won't run our destructor for
    104    * it; pthread_key_delete also doesn't fire destructors for live
    105    * threads). Other threads' blocks are reaped when those threads exit. */
    106   my_block = pthread_getspecific(ctx->key);
    107   if (my_block) {
    108     pthread_setspecific(ctx->key, NULL);
    109     free(my_block);
    110   }
    111   pthread_key_delete(ctx->key);
    112   free(ctx->init_bytes);
    113   free(ctx);
    114 }
    115 
    116 KitJitTls g_jit_tls_posix = {
    117     .ctx_new = jit_tls_ctx_new,
    118     .ctx_destroy = jit_tls_ctx_destroy,
    119     .user = NULL,
    120 };