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 }