commit 9d57dd046e198673042c8cfca233666f7bfc7480
parent 4e8b7ce43241e6eab39044bd54f6eaaa72b02b8e
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Tue, 19 May 2026 17:36:33 -0700
Emit Mach-O TLV descriptors for TLS data
Diffstat:
2 files changed, 162 insertions(+), 0 deletions(-)
diff --git a/src/cg/data.c b/src/cg/data.c
@@ -1,4 +1,49 @@
#include "cg/internal.h"
+#include "core/vec.h"
+
+static void api_data_tls_write_zero(CfreeCg* g, uint64_t size);
+
+static void api_data_tls_ensure_materialized(CfreeCg* g) {
+ if (!g || !g->data_tls_collect || !g->data_tls_zero_fill) return;
+ if (g->data_size) api_data_tls_write_zero(g, g->data_size);
+ g->data_tls_zero_fill = 0;
+}
+
+static void api_data_tls_write(CfreeCg* g, const void* data, size_t len) {
+ if (!g || !len) return;
+ api_data_tls_ensure_materialized(g);
+ buf_write(&g->data_tls_bytes, data, len);
+ g->data_size += len;
+}
+
+static void api_data_tls_write_zero(CfreeCg* g, uint64_t size) {
+ u8 pad[64];
+ if (!g || !size) return;
+ memset(pad, 0, sizeof pad);
+ while (size >= sizeof pad) {
+ buf_write(&g->data_tls_bytes, pad, sizeof pad);
+ size -= sizeof pad;
+ }
+ if (size) buf_write(&g->data_tls_bytes, pad, (size_t)size);
+}
+
+static void api_data_tls_reloc(CfreeCg* g, CfreeCgSym target, int64_t addend,
+ RelocKind kind) {
+ Heap* h;
+ ObjTlsReloc* r;
+ if (!g || kind == R_NONE) return;
+ api_data_tls_ensure_materialized(g);
+ h = (Heap*)g->c->ctx->heap;
+ if (VEC_GROW(h, g->data_tls_relocs, g->data_tls_relocs_cap,
+ g->data_tls_nrelocs + 1u)) {
+ compiler_panic(g->c, api_no_loc(), "CfreeCg: oom on TLS data relocs");
+ }
+ r = &g->data_tls_relocs[g->data_tls_nrelocs++];
+ r->offset = (u32)g->data_size;
+ r->kind = kind;
+ r->target = (ObjSymId)target;
+ r->addend = addend;
+}
void cfree_cg_data_begin(CfreeCg* g, CfreeCgSym cg_sym,
CfreeCgDataDefAttrs attrs) {
@@ -21,6 +66,22 @@ void cfree_cg_data_begin(CfreeCg* g, CfreeCgSym cg_sym,
decl_attrs = api_sym_attrs(g, cg_sym);
align =
attrs.align ? attrs.align : (u32)abi_cg_alignof(c->abi, decl_attrs.type);
+
+ if ((decl_attrs.as.object.flags & CFREE_CG_OBJ_TLS) &&
+ obj_format_tls_via_descriptor(c)) {
+ g->data_sec = OBJ_SEC_NONE;
+ g->data_sym = sym;
+ g->data_base = 0;
+ g->data_size = 0;
+ g->data_tls_collect = 1;
+ g->data_tls_zero_fill =
+ (attrs.flags & CFREE_CG_DATADEF_ZERO_FILL) ? 1u : 0u;
+ g->data_tls_align = align ? align : 1u;
+ g->data_tls_nrelocs = 0;
+ buf_init(&g->data_tls_bytes, (Heap*)c->ctx->heap);
+ return;
+ }
+
if (!attrs.section && decl_attrs.as.object.section) {
attrs.section = decl_attrs.as.object.section;
}
@@ -99,6 +160,20 @@ void cfree_cg_data_common(CfreeCg* g, CfreeCgSym cg_sym, uint64_t size,
}
void cfree_cg_data_align(CfreeCg* g, uint32_t align) {
+ if (g && g->data_tls_collect) {
+ u32 a = align ? align : 1u;
+ u64 base = (g->data_size + (a - 1u)) & ~(u64)(a - 1u);
+ u64 pad = base - g->data_size;
+ if (pad) {
+ if (g->data_tls_zero_fill) {
+ g->data_size += pad;
+ } else {
+ api_data_tls_write_zero(g, pad);
+ g->data_size += pad;
+ }
+ }
+ return;
+ }
if (!g || g->data_sec == OBJ_SEC_NONE || !align) return;
g->data_size = obj_align_to(g->obj, g->data_sec, align) - g->data_base;
}
@@ -106,6 +181,24 @@ void cfree_cg_data_align(CfreeCg* g, uint32_t align) {
void cfree_cg_data_pad(CfreeCg* g, uint64_t size, uint8_t value) {
u8 pad[64];
if (!g || !size) return;
+ if (g->data_tls_collect) {
+ if (value == 0 && g->data_tls_zero_fill) {
+ g->data_size += size;
+ return;
+ }
+ api_data_tls_ensure_materialized(g);
+ memset(pad, value, sizeof pad);
+ while (size >= sizeof pad) {
+ buf_write(&g->data_tls_bytes, pad, sizeof pad);
+ size -= sizeof pad;
+ g->data_size += sizeof pad;
+ }
+ if (size) {
+ buf_write(&g->data_tls_bytes, pad, (size_t)size);
+ g->data_size += size;
+ }
+ return;
+ }
memset(pad, value, sizeof(pad));
while (size >= sizeof(pad)) {
obj_write(g->obj, g->data_sec, pad, sizeof(pad));
@@ -176,6 +269,10 @@ void cfree_cg_data_float(CfreeCg* g, double value, CfreeCgTypeId type) {
void cfree_cg_data_bytes(CfreeCg* g, const uint8_t* data, size_t len) {
if (!g || !len) return;
+ if (g->data_tls_collect) {
+ api_data_tls_write(g, data, len);
+ return;
+ }
obj_write(g->obj, g->data_sec, data, len);
g->data_size += len;
}
@@ -183,6 +280,15 @@ void cfree_cg_data_bytes(CfreeCg* g, const uint8_t* data, size_t len) {
void cfree_cg_data_zero(CfreeCg* g, uint64_t size) {
const Section* sec;
if (!g || !size) return;
+ if (g->data_tls_collect) {
+ if (g->data_tls_zero_fill) {
+ g->data_size += size;
+ } else {
+ api_data_tls_write_zero(g, size);
+ g->data_size += size;
+ }
+ return;
+ }
sec = obj_section_get(g->obj, g->data_sec);
if (sec && (sec->kind == SEC_BSS || sec->sem == SSEM_NOBITS)) {
obj_reserve_bss(g->obj, g->data_sec,
@@ -212,6 +318,12 @@ void api_cg_data_reloc(CfreeCg* g, CfreeCgSym target, int64_t addend,
ob = g->obj;
rk = api_data_reloc_kind(pcrel, width);
if (rk == R_NONE) return;
+ if (g->data_tls_collect) {
+ api_data_tls_reloc(g, target, addend, rk);
+ memset(pad, 0, sizeof pad);
+ api_data_tls_write(g, pad, width);
+ return;
+ }
memset(pad, 0, sizeof pad);
obj_write(ob, g->data_sec, pad, width);
obj_reloc(ob, g->data_sec, g->data_base + (u32)g->data_size, rk,
@@ -235,6 +347,10 @@ void cfree_cg_data_label_addr(CfreeCg* g, CfreeCgLabel target, int64_t addend,
u32 shift = g->c->target.big_endian ? (width - 1u - i) * 8u : i * 8u;
pad[i] = (u8)(((uint64_t)target + (uint64_t)addend) >> shift);
}
+ if (g->data_tls_collect) {
+ api_data_tls_write(g, pad, width);
+ return;
+ }
obj_write(g->obj, g->data_sec, pad, width);
g->data_size += width;
}
@@ -271,6 +387,12 @@ void cfree_cg_data_symdiff(CfreeCg* g, CfreeCgSym lhs, CfreeCgSym rhs,
return;
}
memset(pad, 0, sizeof(pad));
+ if (g->data_tls_collect) {
+ api_data_tls_reloc(g, lhs, addend, add_kind);
+ api_data_tls_reloc(g, rhs, 0, sub_kind);
+ api_data_tls_write(g, pad, width);
+ return;
+ }
obj_write(g->obj, g->data_sec, pad, width);
obj_reloc(g->obj, g->data_sec, g->data_base + (u32)g->data_size, add_kind,
(ObjSymId)lhs, addend);
@@ -280,7 +402,39 @@ void cfree_cg_data_symdiff(CfreeCg* g, CfreeCgSym lhs, CfreeCgSym rhs,
}
void cfree_cg_data_end(CfreeCg* g) {
+ Heap* h;
+ u8* flat;
if (!g) return;
+ if (g->data_tls_collect) {
+ h = (Heap*)g->c->ctx->heap;
+ flat = NULL;
+ if (!g->data_tls_zero_fill && g->data_size) {
+ flat = (u8*)h->alloc(h, (size_t)g->data_size, 1);
+ if (!flat)
+ compiler_panic(g->c, api_no_loc(), "CfreeCg: oom on TLS data bytes");
+ buf_flatten(&g->data_tls_bytes, flat);
+ }
+ obj_define_tls(g->c, g->obj, g->data_sym,
+ g->data_tls_zero_fill ? NULL : flat, (u32)g->data_size,
+ g->data_tls_zero_fill ? 0 : 1, g->data_tls_align,
+ g->data_tls_relocs, g->data_tls_nrelocs);
+ if (flat) h->free(h, flat, (size_t)g->data_size);
+ buf_fini(&g->data_tls_bytes);
+ if (g->data_tls_relocs)
+ h->free(h, g->data_tls_relocs,
+ sizeof(*g->data_tls_relocs) * g->data_tls_relocs_cap);
+ g->data_tls_relocs = NULL;
+ g->data_tls_relocs_cap = 0;
+ g->data_tls_nrelocs = 0;
+ g->data_tls_collect = 0;
+ g->data_tls_zero_fill = 0;
+ g->data_tls_align = 0;
+ g->data_sec = OBJ_SEC_NONE;
+ g->data_sym = OBJ_SYM_NONE;
+ g->data_base = 0;
+ g->data_size = 0;
+ return;
+ }
if (g->data_sym != OBJ_SYM_NONE) {
obj_symbol_define(g->obj, g->data_sym, g->data_sec, g->data_base,
g->data_size);
diff --git a/src/cg/internal.h b/src/cg/internal.h
@@ -160,6 +160,14 @@ struct CfreeCg {
ObjSymId data_sym;
u32 data_base;
u64 data_size;
+ u8 data_tls_collect;
+ u8 data_tls_zero_fill;
+ u8 data_tls_pad[2];
+ u32 data_tls_align;
+ Buf data_tls_bytes;
+ ObjTlsReloc* data_tls_relocs;
+ u32 data_tls_nrelocs;
+ u32 data_tls_relocs_cap;
};
void cg_api_fini(Compiler*);