kit

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

obj_secnames.c (14545B)


      1 /* Format-aware canonical section names.
      2  *
      3  * The kit-internal section model (obj/obj.h) is format-neutral: every
      4  * Section carries a single Sym name plus a SecKind tag.  Most sections
      5  * keep ELF-style dot-prefixed names ("`.text`", "`.data`", …) end-to-end
      6  * because the per-format writer translates them as it emits headers.
      7  *
      8  * A handful of *synthetic* sections — built by the linker rather than
      9  * the front end — diverge in name across formats.  Their names need to
     10  * be picked at synthesis time, before any writer sees them, because the
     11  * linker uses the name to drive layout, symbol-boundary emission, and
     12  * the writer's output-section bucketing.  This TU centralizes that
     13  * choice so callers don't sprinkle target-format switches through
     14  * link_layout.c / link_dyn.c.
     15  *
     16  * Phase 1: ELF returns the historical name; Mach-O
     17  * panics with a "TODO" until the macho writer lands in Phase 2/3.  COFF
     18  * panics in the same way and is filled in later. */
     19 
     20 #include <kit/cg.h>
     21 #include <string.h>
     22 
     23 #include "core/core.h"
     24 #include "core/heap.h"
     25 #include "core/pool.h"
     26 #include "core/slice.h"
     27 #include "obj/format.h"
     28 #include "obj/obj.h"
     29 
     30 /* The C-symbol prefix for the active object format, never NULL: a format
     31  * row with a NULL c_label_prefix (or no format match) is treated as "". */
     32 const char* obj_format_c_label_prefix(const Compiler* c) {
     33   const ObjFormatImpl* fmt = c ? obj_format_lookup(c->target.obj) : NULL;
     34   const char* p = fmt ? fmt->c_label_prefix : NULL;
     35   return p ? p : "";
     36 }
     37 
     38 int obj_macho_debug_sectname(const char* name, size_t len, char out[17]) {
     39   /* Only ".debug_*" sections translate here; ".eh_frame" lives in __TEXT
     40    * and is handled by the writer's generic SecKind path and the reader's
     41    * own candidate list, not this helper. */
     42   static const char kPrefix[] = ".debug_";
     43   const size_t plen = sizeof(kPrefix) - 1; /* 7 */
     44   size_t i;
     45   if (!name || len < plen || memcmp(name, kPrefix, plen) != 0) return 0;
     46   /* out = "__" + name-without-dot, capped at Mach-O's 16-byte sectname.
     47    * The cap yields Apple's spelling for the one overlong DWARF5 name
     48    * (".debug_str_offsets" -> "__debug_str_offs"). */
     49   out[0] = '_';
     50   out[1] = '_';
     51   for (i = 0; i + 1 < len && i < 14u; ++i) out[2 + i] = name[1 + i];
     52   out[2 + i] = '\0';
     53   return 1;
     54 }
     55 
     56 const char* obj_macho_canon_secname(SecKind kind) {
     57   /* Mirrors the SecKind cases of name_to_seg_sect (src/obj/macho/emit.c):
     58    * keep the two in lockstep so a section's text spelling and its binary
     59    * header land in the same Mach-O (segment,section). */
     60   switch (kind) {
     61     case SEC_TEXT:
     62       return "__TEXT,__text";
     63     case SEC_RODATA:
     64       return "__TEXT,__const";
     65     case SEC_DATA:
     66       return "__DATA,__data";
     67     case SEC_BSS:
     68       return "__DATA,__bss";
     69     default: /* SEC_OTHER / SEC_DEBUG: spelled from the section's own name. */
     70       return NULL;
     71   }
     72 }
     73 
     74 /* Inverse of obj_macho_canon_secname: classify a Mach-O native
     75  * "segname,sectname" spelling into a SecKind.  Mirrors the per-segment
     76  * rules of the Mach-O reader (sec_kind_from_seg_sect in macho/read.c)
     77  * for the canonical names, but is name-only (no S_TYPE flags) so a
     78  * format-neutral caller can classify without the raw section header. */
     79 int obj_macho_seckind_for_secname(const char* name, size_t len, SecKind* kind) {
     80   const char* comma;
     81   size_t seg_len, sect_off, sect_len;
     82   if (!name || len == 0) return 0;
     83   comma = (const char*)memchr(name, ',', len);
     84   if (!comma) return 0;
     85   seg_len = (size_t)(comma - name);
     86   sect_off = seg_len + 1u;
     87   sect_len = len - sect_off;
     88   {
     89     const char* seg = name;
     90     const char* sect = name + sect_off;
     91     SecKind k;
     92     if (seg_len == 7 && memcmp(seg, "__DWARF", 7) == 0) {
     93       k = SEC_DEBUG;
     94     } else if (seg_len == 6 && memcmp(seg, "__TEXT", 6) == 0) {
     95       k = (sect_len == 6 && memcmp(sect, "__text", 6) == 0) ? SEC_TEXT
     96                                                             : SEC_RODATA;
     97     } else if (seg_len == 6 && memcmp(seg, "__DATA", 6) == 0) {
     98       k = (sect_len == 5 && memcmp(sect, "__bss", 5) == 0) ? SEC_BSS : SEC_DATA;
     99     } else {
    100       return 0;
    101     }
    102     if (kind) *kind = k;
    103     return 1;
    104   }
    105 }
    106 
    107 /* Translate a kit-internal (ELF-spelled) section name to its Mach-O
    108  * native "segname,sectname" spelling.  Generalizes
    109  * obj_macho_debug_sectname: the ".debug_*" DWARF case routes to
    110  * "__DWARF,__debug_*" (truncated to Mach-O's 16-byte sectname), and
    111  * ".eh_frame" routes to "__TEXT,__eh_frame".  Returns 0 for any other
    112  * name (caller falls back to its own spelling). */
    113 int obj_macho_native_secname(const char* name, size_t len, char out[40]) {
    114   char ds[17];
    115   if (!name || len == 0) return 0;
    116   if (obj_macho_debug_sectname(name, len, ds)) {
    117     /* "__DWARF," + ds (already "__debug_*", <=16 chars). */
    118     size_t dl = slice_from_cstr(ds).len;
    119     memcpy(out, "__DWARF,", 8);
    120     memcpy(out + 8, ds, dl);
    121     out[8 + dl] = '\0';
    122     return 1;
    123   }
    124   if (len == 9 && memcmp(name, ".eh_frame", 9) == 0) {
    125     memcpy(out, "__TEXT,__eh_frame", 17);
    126     out[17] = '\0';
    127     return 1;
    128   }
    129   return 0;
    130 }
    131 
    132 static Sym secname_panic_unimpl(Compiler* c, const char* which) {
    133   SrcLoc l = {0, 0, 0};
    134   compiler_panic(c, l,
    135                  "obj section name '%.*s' for target obj=%u not yet "
    136                  "implemented",
    137                  SLICE_ARG(slice_from_cstr(which)), (unsigned)c->target.obj);
    138   return 0;
    139 }
    140 
    141 /* Resolve a synthetic-section name from the active object format's vtable.
    142  * A NULL field means the format can't represent that section, so panic with
    143  * the ELF-spelled `which` for the diagnostic. The per-format spellings (and
    144  * their rationale) live on ObjFormatImpl in src/obj/registry.c. */
    145 static Sym obj_secname_field(Compiler* c, const char* name, const char* which) {
    146   if (!name) return secname_panic_unimpl(c, which);
    147   return pool_intern_cstr(c->global, name);
    148 }
    149 
    150 Sym obj_secname_init_array(Compiler* c) {
    151   const ObjFormatImpl* fmt = obj_format_lookup(c->target.obj);
    152   return obj_secname_field(c, fmt ? fmt->secname_init_array : NULL,
    153                            ".init_array");
    154 }
    155 
    156 Sym obj_secname_fini_array(Compiler* c) {
    157   const ObjFormatImpl* fmt = obj_format_lookup(c->target.obj);
    158   return obj_secname_field(c, fmt ? fmt->secname_fini_array : NULL,
    159                            ".fini_array");
    160 }
    161 
    162 Sym obj_secname_preinit_array(Compiler* c) {
    163   const ObjFormatImpl* fmt = obj_format_lookup(c->target.obj);
    164   return obj_secname_field(c, fmt ? fmt->secname_preinit_array : NULL,
    165                            ".preinit_array");
    166 }
    167 
    168 Sym obj_secname_tdata(Compiler* c) {
    169   const ObjFormatImpl* fmt = obj_format_lookup(c->target.obj);
    170   return obj_secname_field(c, fmt ? fmt->secname_tdata : NULL, ".tdata");
    171 }
    172 
    173 Sym obj_secname_tbss(Compiler* c) {
    174   const ObjFormatImpl* fmt = obj_format_lookup(c->target.obj);
    175   return obj_secname_field(c, fmt ? fmt->secname_tbss : NULL, ".tbss");
    176 }
    177 
    178 int obj_format_extern_via_got(const Compiler* c) {
    179   /* Mach-O always binds extern data through __got / non-lazy pointers
    180    * — direct ADRP+ADD to an imported symbol isn't representable in
    181    * ld64's reloc set.
    182    *
    183    * ELF static link: extern data is resolved at link time, so direct
    184    * page-relative addressing works (linker patches the ADRP+ADD).
    185    *
    186    * ELF -fPIC / -fPIE: extern data may resolve to a symbol defined
    187    * in a DSO at runtime; the codegen must route through the GOT so
    188    * the loader can patch a single slot rather than touching .text. */
    189   /* Mach-O always binds extern data through its own static GOT / non-lazy
    190    * pointers — same property the builds_own_static_got field records. */
    191   if (obj_format_builds_own_static_got(c)) return 1;
    192   if (c->target.obj == KIT_OBJ_ELF &&
    193       (c->target.pic == KIT_PIC_PIC || c->target.pic == KIT_PIC_PIE))
    194     return 1;
    195   return 0;
    196 }
    197 
    198 int obj_symbol_extern_via_got(const Compiler* c, ObjBuilder* obj,
    199                               ObjSymId sym) {
    200   const ObjSym* s;
    201   if (!obj_format_extern_via_got(c)) return 0;
    202   s = obj_symbol_get(obj, sym);
    203   return s && s->section_id == OBJ_SEC_NONE;
    204 }
    205 
    206 int obj_format_split_sections_as_atoms(const Compiler* c) {
    207   const ObjFormatImpl* fmt;
    208   if (!c) return 0;
    209   fmt = obj_format_lookup(c->target.obj);
    210   return fmt && fmt->split_sections_as_atoms;
    211 }
    212 
    213 /* C-symbol mangling for the active object format.  Mach-O prepends a
    214  * single `_` to every C source-level symbol on disk (matching Apple cc
    215  * and decl.c): "main" → `_main`, "_start" → `__start`,
    216  * "__init_array_start" → `___init_array_start`.  ELF / COFF / Wasm
    217  * intern verbatim.  The temp buffer for the Mach-O case comes from
    218  * `c->ctx->heap`, the same allocator the existing call sites
    219  * (boundary_name, kit_jit_lookup, link_intern_c_name) already use. */
    220 Sym obj_format_c_mangle(Compiler* c, const char* name) {
    221   size_t n, plen;
    222   const char* prefix;
    223   Heap* h;
    224   char* buf;
    225   Sym s;
    226   SrcLoc loc = {0, 0, 0};
    227   if (!c || !name) return 0;
    228   prefix = obj_format_c_label_prefix(c);
    229   plen = slice_from_cstr(prefix).len;
    230   if (plen == 0) return pool_intern_cstr(c->global, name);
    231   n = slice_from_cstr(name).len;
    232   h = (Heap*)c->ctx->heap;
    233   buf = (char*)h->alloc(h, n + plen + 1u, 1);
    234   if (!buf)
    235     compiler_panic(c, loc, "obj_format_c_mangle: oom prefixing '%.*s'",
    236                    SLICE_ARG(slice_from_cstr(name)));
    237   memcpy(buf, prefix, plen);
    238   memcpy(buf + plen, name, n);
    239   buf[n + plen] = 0;
    240   s = pool_intern_slice(c->global, (Slice){.s = buf, .len = (u32)(n + plen)});
    241   h->free(h, buf, n + plen + 1u);
    242   return s;
    243 }
    244 
    245 /* Inverse of obj_format_c_mangle for diagnostic display.  Strips the
    246  * format's leading C-mangle byte from `*name` (advancing the pointer
    247  * and decrementing `*len`) so panic text shows the source-level name
    248  * regardless of target format.  No-op for formats with no prefix. */
    249 void obj_format_demangle_c(const Compiler* c, const char** name, size_t* len) {
    250   const char* prefix;
    251   size_t plen;
    252   if (!c || !name || !len || !*name) return;
    253   prefix = obj_format_c_label_prefix(c);
    254   plen = slice_from_cstr(prefix).len;
    255   if (plen == 0 || *len < plen) return;
    256   if (memcmp(*name, prefix, plen) != 0) return;
    257   *name += plen;
    258   *len -= plen;
    259 }
    260 
    261 /* Default entry symbol name baked into a freshly created Linker for
    262  * this object format.  Mach-O uses `_main` because LC_MAIN names main
    263  * directly (dyld owns C runtime startup); ELF / COFF / Wasm use the
    264  * historical `_start` produced by crt1.o.  Returned as a NUL-terminated
    265  * literal; caller interns. */
    266 const char* obj_format_default_entry_name(const Compiler* c) {
    267   /* Mach-O: `_main` (LC_MAIN names main; dyld owns startup).
    268    * COFF: `mainCRTStartup` (PE/Windows CRT entry sets up argc/argv and
    269    *   calls main; resolved against the user CRT archive, mingw's
    270    *   libmingwex.a — see doc/OBJ.md).
    271    * ELF / Wasm: the historical `_start` produced by crt1.o.
    272    * All driven by the per-format default_entry_name field; a row with a
    273    * NULL field (or no format match) falls back to "_start". */
    274   const ObjFormatImpl* fmt = c ? obj_format_lookup(c->target.obj) : NULL;
    275   const char* e = fmt ? fmt->default_entry_name : NULL;
    276   return e ? e : "_start";
    277 }
    278 
    279 int obj_format_carries_file_only_debug(const Compiler* c) {
    280   const ObjFormatImpl* fmt = c ? obj_format_lookup(c->target.obj) : NULL;
    281   return fmt && fmt->carries_file_only_debug;
    282 }
    283 
    284 int obj_format_builds_own_static_got(const Compiler* c) {
    285   const ObjFormatImpl* fmt = c ? obj_format_lookup(c->target.obj) : NULL;
    286   return fmt && fmt->builds_own_static_got;
    287 }
    288 
    289 int obj_format_weak_undef_pulls_archive_member(const Compiler* c) {
    290   const ObjFormatImpl* fmt = c ? obj_format_lookup(c->target.obj) : NULL;
    291   return fmt && fmt->weak_undef_pulls_archive_member;
    292 }
    293 
    294 int obj_format_global_archive_fixpoint(const Compiler* c) {
    295   const ObjFormatImpl* fmt = c ? obj_format_lookup(c->target.obj) : NULL;
    296   return fmt && fmt->global_archive_fixpoint;
    297 }
    298 
    299 int obj_format_weak_extern_underscore_alias(const Compiler* c) {
    300   const ObjFormatImpl* fmt = c ? obj_format_lookup(c->target.obj) : NULL;
    301   return fmt && fmt->weak_extern_underscore_alias;
    302 }
    303 
    304 int obj_format_supports_symbol_feature(const Compiler* c, int symfeat) {
    305   /* The only format-divergent feature axis today is TLS access: only ELF and
    306    * Mach-O can represent the ELF/Mach-O TLS-access features the CG layer mints.
    307    * COFF (Windows TEB model) and Wasm cannot. Every other (non-TLS) feature is
    308    * representable by every format. The per-format answer lives on the vtable.
    309    */
    310   switch (symfeat) {
    311     case KIT_CG_SYMFEAT_TLS_LOCAL_EXEC:
    312     case KIT_CG_SYMFEAT_TLS_INITIAL_EXEC:
    313     case KIT_CG_SYMFEAT_TLS_LOCAL_DYNAMIC:
    314     case KIT_CG_SYMFEAT_TLS_GENERAL_DYNAMIC: {
    315       const ObjFormatImpl* fmt = c ? obj_format_lookup(c->target.obj) : NULL;
    316       return fmt && fmt->tls_symbol_features;
    317     }
    318     default:
    319       return 1;
    320   }
    321 }
    322 
    323 int obj_format_static_ifunc_via_rela_iplt(const Compiler* c) {
    324   /* The single home for the (os == FREEBSD && obj == ELF) knowledge:
    325    * FreeBSD's crt walks [__rela_iplt_start, __rela_iplt_end) of
    326    * R_*_IRELATIVE relocs before main, so kit emits that table instead of
    327    * the ctor-based __kit_ifunc_init path on FreeBSD/ELF. */
    328   return c && c->target.os == KIT_OS_FREEBSD && c->target.obj == KIT_OBJ_ELF;
    329 }
    330 
    331 u32 obj_format_static_ifunc_irelative_type(const Compiler* c) {
    332   /* The R_*_IRELATIVE resolver wire type for the __rela_iplt table the
    333    * predicate above selects. Resolves through the *target* format rather
    334    * than a literal KIT_OBJ_ELF so the generic iplt pass names no format
    335    * constant; non-ELF formats have no elf_arch and yield 0. */
    336   const ObjFormatImpl* fmt;
    337   const ObjElfArchOps* ao;
    338   if (!c) return 0u;
    339   fmt = obj_format_lookup(c->target.obj);
    340   ao = (fmt && fmt->elf_arch) ? fmt->elf_arch(c->target.arch) : NULL;
    341   return ao ? ao->r_irelative : 0u;
    342 }
    343 
    344 int obj_format_boundary_sym_kind(const Compiler* c, KitSlice name,
    345                                  int* symkind) {
    346   /* PE/COFF owns two synthetic absolute globals the linker emits:
    347    * `__ImageBase` (image base for ASLR-relative math) and `_tls_used`
    348    * (the IMAGE_TLS_DIRECTORY anchor).  Both are SK_ABS.  Other formats
    349    * own no boundary symbols here. */
    350   if (!c || c->target.obj != KIT_OBJ_COFF) return 0;
    351   if (slice_eq_cstr(name, "__ImageBase") || slice_eq_cstr(name, "_tls_used")) {
    352     if (symkind) *symkind = SK_ABS;
    353     return 1;
    354   }
    355   return 0;
    356 }
    357 
    358 void obj_format_synth_inputs(const Compiler* c, Linker* l) {
    359   const ObjFormatImpl* fmt = c ? obj_format_lookup(c->target.obj) : NULL;
    360   if (fmt && fmt->synth_inputs) fmt->synth_inputs(l);
    361 }