kit

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

obj_tls.c (8699B)


      1 /* Format-aware thread-local storage emission.
      2  *
      3  * `_Thread_local` storage has the same source-level shape across object
      4  * formats (a global with a per-thread instance), but the on-disk
      5  * representation diverges sharply:
      6  *
      7  *   ELF   : one symbol in `.tdata` / `.tbss`; access via direct
      8  *           TP-relative offset (TLSLE relocs).
      9  *   Mach-O: storage and access are split.  The bytes live under a
     10  *           private `<name>$tlv$init` symbol in `__DATA,__thread_data`
     11  *           / `__DATA,__thread_bss`.  The user-visible symbol points
     12  *           at a 24-byte *descriptor* in `__DATA,__thread_vars`:
     13  *             +0  : ptr to `_tlv_bootstrap` (BIND; dyld rewrites to a
     14  *                   per-descriptor thunk after allocating a pthread key)
     15  *             +8  : pthread key (0 on disk; filled by dyld)
     16  *             +16 : ptr to the data symbol (REBASE; link_macho rewrites
     17  *                   the literal to (target_vaddr - tls_image_vaddr))
     18  *           Access from compiled code is an indirect call through the
     19  *           descriptor's slot[0]; the TLVP_LOAD_PAGE21/PAGEOFF12 reloc
     20  *           pair targets the descriptor symbol, not the data.
     21  *
     22  * Centralizing the split here keeps the frontend (parse_init.c) and the
     23  * codegen (per-arch ops.c) format-agnostic — they pass the canonical
     24  * SK_TLS symbol plus byte buffer to `obj_define_tls`, and consult
     25  * `obj_format_tls_via_descriptor` when choosing an access sequence.
     26  *
     27  * The `_tlv_bootstrap` undef extern is cached on ObjBuilder so multiple
     28  * TLV vars in one TU share one entry; the linker dedupes across TUs by
     29  * name. */
     30 
     31 #include <string.h>
     32 
     33 #include "core/core.h"
     34 #include "core/heap.h"
     35 #include "core/pool.h"
     36 #include "core/slice.h"
     37 #include "obj/format.h"
     38 #include "obj/obj.h"
     39 
     40 /* ObjBuilder is opaque outside obj.c; obj_tls.c reaches the bootstrap
     41  * cache via these accessors defined in obj.c.  Declared here to avoid
     42  * exposing the field in obj.h's public ObjBuilder layout. */
     43 ObjSymId obj_tlv_bootstrap_get(const ObjBuilder*);
     44 void obj_tlv_bootstrap_set(ObjBuilder*, ObjSymId);
     45 
     46 /* TLS-access model for the active (format, OS): COFF -> Windows TEB,
     47  * Mach-O -> per-variable descriptor + thunk, everything else -> ELF
     48  * Local-Exec / Initial-Exec.  The single source of truth for the
     49  * TLS-access decision. */
     50 ObjTlsModel obj_format_tls_model(const Compiler* c) {
     51   if (!c) return OBJ_TLS_ELF_LE;
     52   if (c->target.obj == KIT_OBJ_COFF) return OBJ_TLS_WINDOWS_TEB;
     53   if (c->target.obj == KIT_OBJ_MACHO) return OBJ_TLS_MACHO_DESCRIPTOR;
     54   return OBJ_TLS_ELF_LE;
     55 }
     56 
     57 /* Thin wrapper kept for existing callers (per-arch ops.c, NativeDirect,
     58  * internal define_tls dispatch); later waves migrate them to
     59  * obj_format_tls_model directly. */
     60 int obj_format_tls_via_descriptor(const Compiler* c) {
     61   return obj_format_tls_model(c) == OBJ_TLS_MACHO_DESCRIPTOR;
     62 }
     63 
     64 /* In-process JIT: 1 when a reference to symbol `name` is dropped because the
     65  * access idiom that materializes it is relaxed to in-image addressing. The
     66  * COFF Windows TEB model loads a per-module `_tls_index`; the JIT rewrites that
     67  * load away, so its relocs must not be applied. None elsewhere. Sits beside
     68  * obj_format_tls_model as the TLS-mechanism authority, so src/link stays free
     69  * of the format-specific pseudo-symbol name. */
     70 int obj_format_jit_drops_symbol_ref(const Compiler* c, Sym name) {
     71   Slice nm;
     72   if (!c || name == 0) return 0;
     73   if (obj_format_tls_model(c) != OBJ_TLS_WINDOWS_TEB) return 0;
     74   nm = pool_slice(c->global, name);
     75   return nm.len == 10u && memcmp(nm.s, "_tls_index", 10u) == 0;
     76 }
     77 
     78 static void define_tls_elf(ObjBuilder* ob, Compiler* c, ObjSymId sym,
     79                            const u8* data, u32 size, int has_nonzero_init,
     80                            u32 align, const ObjTlsReloc* relocs, u32 nrelocs) {
     81   u32 a = align ? align : 1u;
     82   if (!data || !has_nonzero_init) {
     83     Sym sname = obj_secname_tbss(c);
     84     ObjSecId sec =
     85         obj_section_ex(ob, sname, SEC_BSS, SSEM_NOBITS,
     86                        SF_ALLOC | SF_WRITE | SF_TLS, a, 0, OBJ_SEC_NONE, 0);
     87     u32 base = obj_align_to(ob, sec, a);
     88     obj_reserve_bss(ob, sec, base + size, a);
     89     obj_symbol_define(ob, sym, sec, base, size);
     90     return;
     91   }
     92   Sym sname = obj_secname_tdata(c);
     93   ObjSecId sec =
     94       obj_section(ob, sname, SEC_DATA, SF_ALLOC | SF_WRITE | SF_TLS, a);
     95   u32 base = obj_align_to(ob, sec, a);
     96   {
     97     u8* dst = obj_reserve(ob, sec, size);
     98     if (dst) memcpy(dst, data, size);
     99   }
    100   obj_symbol_define(ob, sym, sec, base, size);
    101   for (u32 i = 0; i < nrelocs; ++i) {
    102     obj_reloc(ob, sec, base + relocs[i].offset, relocs[i].kind,
    103               relocs[i].target, relocs[i].addend);
    104   }
    105 }
    106 
    107 static ObjSymId tlv_bootstrap(ObjBuilder* ob, Compiler* c) {
    108   ObjSymId s = obj_tlv_bootstrap_get(ob);
    109   if (s != OBJ_SYM_NONE) return s;
    110   /* On-disk name carries the Mach-O leading underscore: source-level
    111    * `_tlv_bootstrap` becomes `__tlv_bootstrap`. */
    112   Sym name = pool_intern_slice(c->global, SLICE_LIT("__tlv_bootstrap"));
    113   s = obj_symbol(ob, name, SB_GLOBAL, SK_UNDEF, OBJ_SEC_NONE, 0, 0);
    114   obj_tlv_bootstrap_set(ob, s);
    115   return s;
    116 }
    117 
    118 static ObjSymId mint_init_sym(ObjBuilder* ob, Compiler* c, Sym desc_name) {
    119   Slice nm_s = pool_slice(c->global, desc_name);
    120   const char* nm = nm_s.s;
    121   size_t nlen = nm_s.len;
    122   static const char suffix[] = "$tlv$init";
    123   size_t slen = sizeof(suffix) - 1u;
    124   Heap* h = (Heap*)c->ctx->heap;
    125   char* buf = (char*)h->alloc(h, nlen + slen + 1u, 1);
    126   if (!buf)
    127     compiler_panic(c, (SrcLoc){0, 0, 0},
    128                    "obj_define_tls: oom interning init name");
    129   if (nlen) memcpy(buf, nm, nlen);
    130   memcpy(buf + nlen, suffix, slen);
    131   buf[nlen + slen] = 0;
    132   Sym n = pool_intern_slice(c->global,
    133                             (Slice){.s = buf, .len = (u32)(nlen + slen)});
    134   h->free(h, buf, nlen + slen + 1u);
    135   return obj_symbol(ob, n, SB_LOCAL, SK_TLS, OBJ_SEC_NONE, 0, 0);
    136 }
    137 
    138 static void define_tls_macho(ObjBuilder* ob, Compiler* c, ObjSymId sym,
    139                              const u8* data, u32 size, int has_nonzero_init,
    140                              u32 align, const ObjTlsReloc* relocs,
    141                              u32 nrelocs) {
    142   const ObjSym* desc_os = obj_symbol_get(ob, sym);
    143   if (!desc_os)
    144     compiler_panic(c, (SrcLoc){0, 0, 0},
    145                    "obj_define_tls: descriptor sym not found");
    146   ObjSymId data_sym = mint_init_sym(ob, c, desc_os->name);
    147 
    148   /* Storage section: __thread_data (initialized) or __thread_bss (BSS).
    149    * Same SF_TLS flag as ELF — macho_emit's section_flags_for maps SF_TLS
    150    * + sectname to the right S_THREAD_LOCAL_* type. */
    151   u32 a = align ? align : 1u;
    152   if (!data || !has_nonzero_init) {
    153     Sym sname = obj_secname_tbss(c);
    154     ObjSecId sec =
    155         obj_section_ex(ob, sname, SEC_BSS, SSEM_NOBITS,
    156                        SF_ALLOC | SF_WRITE | SF_TLS, a, 0, OBJ_SEC_NONE, 0);
    157     u32 base = obj_align_to(ob, sec, a);
    158     obj_reserve_bss(ob, sec, base + size, a);
    159     obj_symbol_define(ob, data_sym, sec, base, size);
    160   } else {
    161     Sym sname = obj_secname_tdata(c);
    162     ObjSecId sec =
    163         obj_section(ob, sname, SEC_DATA, SF_ALLOC | SF_WRITE | SF_TLS, a);
    164     u32 base = obj_align_to(ob, sec, a);
    165     {
    166       u8* dst = obj_reserve(ob, sec, size);
    167       if (dst) memcpy(dst, data, size);
    168     }
    169     obj_symbol_define(ob, data_sym, sec, base, size);
    170     for (u32 i = 0; i < nrelocs; ++i) {
    171       obj_reloc(ob, sec, base + relocs[i].offset, relocs[i].kind,
    172                 relocs[i].target, relocs[i].addend);
    173     }
    174   }
    175 
    176   /* Descriptor in __DATA,__thread_vars: 24 bytes aligned 8.
    177    * The user-visible `sym` lives here; the TLVP relocs in code target
    178    * this symbol so the linker can route them through __thread_ptrs. */
    179   Sym vars_name =
    180       pool_intern_slice(c->global, SLICE_LIT("__DATA,__thread_vars"));
    181   ObjSecId vars_sec =
    182       obj_section(ob, vars_name, SEC_DATA, SF_ALLOC | SF_WRITE | SF_TLS, 8u);
    183   u32 desc_base = obj_align_to(ob, vars_sec, 8u);
    184   {
    185     u8* dst = obj_reserve(ob, vars_sec, 24u);
    186     if (dst) memset(dst, 0, 24u);
    187   }
    188   obj_symbol_define(ob, sym, vars_sec, desc_base, 24u);
    189   obj_reloc(ob, vars_sec, desc_base + 0u, R_ABS64, tlv_bootstrap(ob, c), 0);
    190   obj_reloc(ob, vars_sec, desc_base + 16u, R_ABS64, data_sym, 0);
    191 }
    192 
    193 void obj_define_tls(Compiler* c, ObjBuilder* ob, ObjSymId sym, const u8* data,
    194                     u32 size, int has_nonzero_init, u32 align,
    195                     const ObjTlsReloc* relocs, u32 nrelocs) {
    196   if (obj_format_tls_via_descriptor(c)) {
    197     define_tls_macho(ob, c, sym, data, size, has_nonzero_init, align, relocs,
    198                      nrelocs);
    199     return;
    200   }
    201   define_tls_elf(ob, c, sym, data, size, has_nonzero_init, align, relocs,
    202                  nrelocs);
    203 }