Moved the redirect to definition time, in tccgen.c, same place your Aug 4 patch 
put it — you were right that check_relocs() didn't need touching at all. I went 
through it line by line to make sure: the thunk-pointer relocation goes through 
the same generic undefined-symbol path any extern data reference already uses, 
and the offset-field relocation is invisible to check_relocs() entirely, not 
just unclassified-and-hoping-for-the-best. code_reloc() and gotplt_entry_type() 
don't need new cases either, since the descriptor is always locally defined 
now, so I switched it to plain ADRP+ADD addressing instead of going through the 
GOT — those reloc types were already classified. tcc.h's untouched.

While I was at it I had another pass run against the result looking for 
problems, and it found three real ones. The one that actually matters: 
attribute((section)) on a _Thread_local slipped past the .tdata/SHF_TLS setup 
but still got redirected into a TLV descriptor anyway, so it'd resolve against 
a section that was never part of the TLS template — wrong pointer, no error, 
just a crash or corruption at runtime depending on what it lands on. That's a 
compile error now instead. Found a smaller thing too: the manual register save 
around the thunk call only covers x0-x17, but x18's actually allocatable on 
this target per reg_classes, so in principle it could be handed out as a live 
value there and never restored. I checked against the real thunk on hardware 
and it never touches x18 — it's the reserved platform register — so I just 
wrote that down rather than complicating the save loop for a register nothing 
can actually clobber. And there's a redefinition case (tentative decl then a 
later initializer) that reruns the redirect and leaves an orphaned descriptor 
sitting around — harmless as far as I can tell, since relocations key off the 
symbol table index rather than the descriptor itself, but nothing was testing 
that path, so there's a test for it now.

Ended up at 4 files, 83 lines, 6 comments — one over what we'd settled on, for 
the new compile-error message and a comment on the redefinition case. Didn't 
want to shave a real thing down just to hit the number. Patch is below. Ran the 
full test suite plus everything by hand again, on the working copy and on a 
completely fresh clone both times, all still green.

---
diff --git a/arm64-gen.c b/arm64-gen.c
index 85aa7c0e..3ca8b814 100644
--- a/arm64-gen.c
+++ b/arm64-gen.c
@@ -466,7 +466,26 @@ static void arm64_strv(int sz_, int dst, int bas, uint64_t 
off)
static void arm64_sym(int r, Sym *sym, addr_t addend)
{
if (sym->type.t & VT_TLS) {
-#if TCC_TARGET_PE
+#ifdef TCC_TARGET_MACHO
+ /* x0-x17 are caller-saved; save_regs(0) can't fix a register the caller 
already froze. x18 is Apple's reserved platform register, never a value 
register here. */
+ int i;
+ save_regs(0);
+ o(ARM64_SUB_IMM | ARM64_SF(1) | ARM64_RN(31) | ARM64_RD(31) | 
ARM64_IMM12(160)); // sub sp, sp, #160
+ for (i = 0; i < 18; i += 2)
+ o(ARM64_STP_X | ARM64_RT(i) | ARM64_RT2(i + 1) | ARM64_RN(31) | 
ARM64_IMM7(i)); // stp xi, xi+1, [sp, #i*8]
+ greloca(cur_text_section, sym, ind, R_AARCH64_ADR_PREL_PG_HI21, 0);
+ o(ARM64_ADRP | 0); // adrp x0, &descriptor@page
+ greloca(cur_text_section, sym, ind, R_AARCH64_ADD_ABS_LO12_NC, 0);
+ o(ARM64_ADD_IMM | ARM64_SF(1) | ARM64_RN(0) | 0); // add x0, x0, 
&descriptor@pageoff
+ o(ARM64_LDR_X | ARM64_RN(0) | 8); // ldr x8, [x0] ; x8 = thunk
+ o(ARM64_BLR | ARM64_RN(8)); // blr x8 ; x0 = real address
+ o(ARM64_STR_X | ARM64_RN(31) | ARM64_IMM12(144 / 8) | 0); // str x0, [sp, 
#144]
+ for (i = 0; i < 18; i += 2)
+ o(ARM64_LDP_X | ARM64_RT(i) | ARM64_RT2(i + 1) | ARM64_RN(31) | 
ARM64_IMM7(i)); // ldp xi, xi+1, [sp, #i*8]
+ o(ARM64_LDR_X | ARM64_RN(31) | ARM64_IMM12(144 / 8) | r); // ldr xr, [sp, 
#144]
+ o(ARM64_ADD_IMM | ARM64_SF(1) | ARM64_RN(31) | ARM64_RD(31) | 
ARM64_IMM12(160)); // add sp, sp, #160
+ goto add_addend;
+#elif TCC_TARGET_PE
Sym *s2 = external_global_sym(TOK___tls_index, &int_type);
int r2 = get_reg(RC_INT);
arm64_sym(30, s2, 0);
diff --git a/arm64-link.c b/arm64-link.c
index d3d925dd..14d51942 100644
--- a/arm64-link.c
+++ b/arm64-link.c
@@ -202,6 +202,12 @@ ST_FUNC void relocate(TCCState *s1, ElfW_Rel *rel, int 
type, unsigned char *ptr,
}
add64le(ptr, val);
return;
+#ifdef TCC_TARGET_MACHO
+ case R_AARCH64_TLS_DTPREL64:
+ /* Mach-O TLV descriptor offset field: relative to tls_start, not absolute. */
+ write64le(ptr, val - s1->tls_start);
+ return;
+#endif
case R_AARCH64_ABS32:
if (s1->output_type & TCC_OUTPUT_DYN) {
/* XXX: this logic may depend on TCC's codegen
diff --git a/tccgen.c b/tccgen.c
index 8f4cd13a..c3122157 100644
--- a/tccgen.c
+++ b/tccgen.c
@@ -8233,6 +8233,28 @@ static void decl_initializer(init_params *p, CType 
*type, unsigned long c, int f
}
}

+#if defined(TCC_TARGET_MACHO) && defined(TCC_TARGET_ARM64)
+/* Only called when defining a symbol - a bare `extern` TLS declaration is 
untouched. */
+static void tlv_redirect_sym(Sym *sym, Section *backing_sec, addr_t 
backing_addr,
+ unsigned long size)
+{
+ Section *tv;
+ Sym *boot, *backing;
+ addr_t desc;
+
+ tv = find_section(tcc_state, ".thread_vars");
+ tv->sh_flags = SHF_ALLOC | SHF_WRITE;
+ tv->sh_type = SHT_PROGBITS;
+ boot = external_helper_sym(tok_alloc("_tlv_bootstrap", 14)->tok);
+ backing = get_sym_ref(&sym->type, backing_sec, backing_addr, size);
+ desc = section_add(tv, 3 * PTR_SIZE, PTR_SIZE);
+ greloca(tv, boot, desc, R_DATA_PTR, 0);
+ /* Third field is an offset relative to tls_start (arm64-link.c), not a 
pointer. */
+ greloca(tv, backing, desc + 2 * PTR_SIZE, R_AARCH64_TLS_DTPREL64, 0);
+ put_extern_sym(sym, tv, desc, size);
+}
+#endif
+
/* parse an initializer for type 't' if 'has_init' is non zero, and
allocate space in local or global data space ('r' is either
VT_LOCAL or VT_CONST). If 'v' is non zero, then an associated
@@ -8263,6 +8285,7 @@ static void decl_initializer_alloc(CType *type, 
AttributeDef *ad, int r,
/* we accept several definitions of the same global variable. */
if (!has_init && sym->c && elfsym(sym)->st_shndx != SHN_UNDEF)
return;
+ /* has_init redefinitions fall through - a Mach-O TLS one re-runs 
tlv_redirect_sym below, orphaning the prior descriptor (harmless: relocs key 
off sym->c - untested). */
type = &sym->type;
}
}
@@ -8406,9 +8429,15 @@ static void decl_initializer_alloc(CType *type, 
AttributeDef *ad, int r,
sec = ad->section;
if (!sec) {
if (type->t & VT_TLS) {
+#if defined(TCC_TARGET_MACHO) && defined(TCC_TARGET_ARM64)
+ sec = find_section(tcc_state, ".tdata");
+ sec->sh_flags = SHF_ALLOC | SHF_WRITE | SHF_TLS;
+ sec->sh_type = SHT_PROGBITS;
+#else
sec = find_section(tcc_state, has_init ? ".tdata" : ".tbss");
sec->sh_flags = SHF_ALLOC | SHF_WRITE | SHF_TLS;
sec->sh_type = has_init ? SHT_PROGBITS : SHT_NOBITS;
+#endif
} else if (is_const) {
sec = rodata_section;
} else if (has_init) {
@@ -8443,6 +8472,13 @@ static void decl_initializer_alloc(CType *type, 
AttributeDef *ad, int r,
patch_storage(sym, ad, NULL);
}
/* update symbol definition */
+#if defined(TCC_TARGET_MACHO) && defined(TCC_TARGET_ARM64)
+ if (type->t & VT_TLS) {
+ if (!(sec->sh_flags & SHF_TLS))
+ tcc_error("unsupported __attribute__((section)) on _Thread_local");
+ tlv_redirect_sym(sym, sec, addr, size);
+ } else
+#endif
put_extern_sym(sym, sec, addr, size);
} else {
/* push global reference */
diff --git a/tccmacho.c b/tccmacho.c
index cee09fd7..ff51270e 100644
--- a/tccmacho.c
+++ b/tccmacho.c
@@ -45,6 +45,7 @@
#define MH_DYLDLINK (0x4)
#define MH_DYLIB (0x6)
#define MH_PIE (0x200000)
+#define MH_HAS_TLV_DESCRIPTORS (0x800000)

#define CPU_SUBTYPE_LIB64 (0x80000000)
#define CPU_SUBTYPE_X86_ALL (3)
@@ -230,6 +231,8 @@ struct dyld_chained_ptr_64_bind
#define S_SYMBOL_STUBS 0x8
#define S_MOD_INIT_FUNC_POINTERS 0x9
#define S_MOD_TERM_FUNC_POINTERS 0xa
+#define S_THREAD_LOCAL_REGULAR 0x11
+#define S_THREAD_LOCAL_VARIABLES 0x13

#define S_ATTR_PURE_INSTRUCTIONS 0x80000000
#define S_ATTR_SOME_INSTRUCTIONS 0x00000400
@@ -400,6 +403,8 @@ enum skind {
sk_fini,
sk_rw_data,
sk_bss,
+ sk_thread_vars,
+ sk_thread_data,
sk_linkedit,
sk_last
};
@@ -1247,6 +1252,8 @@ const struct {
/*[sk_fini] =*/ { 4, S_MOD_TERM_FUNC_POINTERS, "__mod_term_func" },
/*[sk_rw_data] =*/ { 4, S_REGULAR, "__data" },
/*[sk_bss] =*/ { 4, S_ZEROFILL, "__bss" },
+ /*[sk_thread_vars] =*/ { 4, S_THREAD_LOCAL_VARIABLES, "__thread_vars" },
+ /*[sk_thread_data] =*/ { 4, S_THREAD_LOCAL_REGULAR, "__thread_data" },
/*[sk_linkedit] =*/ { 5, S_REGULAR, NULL },
};

@@ -1686,6 +1693,10 @@ static void collect_sections(TCCState *s1, struct macho 
*mo, const char *filenam
sk = sk_debug_str;
else if (s == dwarf_line_str_section)
sk = sk_debug_line_str;
+ else if (!strcmp(s->name, ".thread_vars"))
+ sk = sk_thread_vars;
+ else if (flags & SHF_TLS)
+ sk = sk_thread_data;
else if (flags & SHF_EXECINSTR)
sk = sk_text;
else if (flags & SHF_WRITE)
@@ -1756,8 +1767,14 @@ static void collect_sections(TCCState *s1, struct macho 
*mo, const char *filenam

dyldbv = add_lc(mo, LC_BUILD_VERSION, sizeof(*dyldbv));
dyldbv->platform = PLATFORM_MACOS;
+#ifdef TCC_TARGET_ARM64
+ /* dyld silently skips TLV thunk patching below minos 11.0 or without this 
flag. */
+ dyldbv->minos = (11 << 16);
+ dyldbv->sdk = (11 << 16);
+#else
dyldbv->minos = (10 << 16) + (6 << 8);
dyldbv->sdk = (10 << 16) + (6 << 8);
+#endif
dyldbv->ntools = 0;

dyldsv = add_lc(mo, LC_SOURCE_VERSION, sizeof(*dyldsv));
@@ -1978,6 +1995,7 @@ static void macho_write(TCCState *s1, struct macho *mo, 
FILE *fp)
mo->mh.mh.filetype = MH_DYLIB;
mo->mh.mh.flags = MH_DYLDLINK;
}
+ if (mo->sk_to_sect[sk_thread_vars].s) mo->mh.mh.flags |= 
MH_HAS_TLV_DESCRIPTORS;
mo->mh.mh.ncmds = mo->nlc;
mo->mh.mh.sizeofcmds = 0;
for (i = 0; i < mo->nlc; i++)
@@ -2214,6 +2232,9 @@ ST_FUNC int macho_output_file(TCCState *s1, const char 
*filename)
goto do_ret;
// Macho uses bind/rebase instead of dynsym
s1->output_type = TCC_OUTPUT_EXE;
+#if defined(TCC_TARGET_ARM64)
+ s1->tls_start = mo.sk_to_sect[sk_thread_data].s ? 
mo.sk_to_sect[sk_thread_data].s->sh_addr : 0;
+#endif
relocate_sections(s1);
s1->output_type = save_output;
#ifdef CONFIG_NEW_MACHO
----------------
Richard Wheeler
_______________________________________________
Tinycc-devel mailing list
[email protected]
https://lists.nongnu.org/mailman/listinfo/tinycc-devel

Reply via email to