Reworked per feedback: no changes to tccgen.c. TLV descriptor creation now
happens per-access in arm64-gen.c (arm64_sym) instead of at declaration
time, so tccgen.c's ordinary .tdata/.tbss handling is untouched. tccmacho.c's
offset patch now treats whichever of .tdata/.tbss is present as the
per-thread template base, rather than forcing both into one section. Each
access gets its own descriptor (no memoization) - still correct, since every
descriptor's offset field resolves against the same backing symbol at link
time. Register handling (save_regs() plus manual x0-x17 save/restore) is
unchanged from the last round.
---
arm64-gen.c | 37 ++++++++++++++++++++++++++++++++++++-
tccmacho.c | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++++++---
2 files changed, 91 insertions(+), 4 deletions(-)

diff --git a/arm64-gen.c b/arm64-gen.c
index 85aa7c0..ad0463d 100644
--- a/arm64-gen.c
+++ b/arm64-gen.c
@@ -463,10 +463,47 @@ static void arm64_strv(int sz_, int dst, int bas, 
uint64_t off)
}
}

+#ifdef TCC_TARGET_MACHO
+static Sym *arm64_macho_tlv_desc(Sym *sym)
+{
+ Section *tv = find_section(tcc_state, ".thread_vars");
+ Sym *boot = external_helper_sym(tok_alloc("_tlv_bootstrap", 14)->tok);
+ addr_t addr;
+
+ tv->sh_flags = SHF_ALLOC | SHF_WRITE;
+ tv->sh_type = SHT_PROGBITS;
+ addr = section_add(tv, 3 * PTR_SIZE, PTR_SIZE);
+ greloca(tv, boot, addr, R_DATA_PTR, 0);
+ greloca(tv, sym, addr + 2 * PTR_SIZE, R_DATA_PTR, 0);
+ /* No memoization: a duplicate descriptor still resolves correctly at link 
time. */
+ return get_sym_ref(&sym->type, tv, addr, PTR_SIZE);
+}
+#endif
+
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
+ Sym *dsym = arm64_macho_tlv_desc(sym);
+ int i;
+ /* blr below may clobber x0-x17: save_regs() can't protect store()'s frozen 
raw r. */
+ 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, dsym, ind, R_AARCH64_ADR_GOT_PAGE, 0);
+ o(ARM64_ADRP | 0); // adrp x0, &descriptor@page
+ greloca(cur_text_section, dsym, ind, R_AARCH64_LD64_GOT_LO12_NC, 0);
+ o(ARM64_LDR_X | ARM64_RN(0) | 0); // ldr 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/tccmacho.c b/tccmacho.c
index cee09fd..390e226 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,9 @@ 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_ZEROFILL 0x12
+#define S_THREAD_LOCAL_VARIABLES 0x13

#define S_ATTR_PURE_INSTRUCTIONS 0x80000000
#define S_ATTR_SOME_INSTRUCTIONS 0x00000400
@@ -400,6 +404,9 @@ enum skind {
sk_fini,
sk_rw_data,
sk_bss,
+ sk_thread_vars,
+ sk_thread_data,
+ sk_thread_bss,
sk_linkedit,
sk_last
};
@@ -767,9 +774,15 @@ static void check_relocs(TCCState *s1, struct macho *mo)
rel->r_addend += attr->plt_offset;
}
}
- if (type == R_DATA_PTR || type == R_JMP_SLOT)
- bind_rebase_add(mo, sym->st_shndx == SHN_UNDEF ? 1 : 0,
+ if (type == R_DATA_PTR || type == R_JMP_SLOT) {
+ /* Descriptor field 3 holds a template-relative offset, not a pointer - it 
must
+ skip rebase (dyld would double-apply the ASLR slide) and get patched below 
instead. */
+ int tlv_offset_field = sym->st_shndx != SHN_UNDEF
+ && !strcmp(s1->sections[s->sh_info]->name, ".thread_vars");
+ if (!tlv_offset_field)
+ bind_rebase_add(mo, sym->st_shndx == SHN_UNDEF ? 1 : 0,
s->sh_info, &save_rel, NULL);
+ }
}
}
/* remove deleted binds */
@@ -1247,6 +1260,9 @@ 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_thread_bss] =*/ { 4, S_THREAD_LOCAL_ZEROFILL, "__thread_bss" },
/*[sk_linkedit] =*/ { 5, S_REGULAR, NULL },
};

@@ -1649,7 +1665,7 @@ static void collect_sections(TCCState *s1, struct macho 
*mo, const char *filenam
default: sk = sk_unknown; break;
case SHT_INIT_ARRAY: sk = sk_init; break;
case SHT_FINI_ARRAY: sk = sk_fini; break;
- case SHT_NOBITS: sk = sk_bss; break;
+ case SHT_NOBITS: sk = (flags & SHF_TLS) ? sk_thread_bss : sk_bss; break;
case SHT_SYMTAB: sk = sk_discard; break;
case SHT_STRTAB:
if (s == stabstr_section)
@@ -1686,6 +1702,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 +1776,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 +2004,8 @@ 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++)
@@ -2176,6 +2204,27 @@ ST_FUNC void bind_rebase_import(TCCState *s1, struct 
macho *mo)
}
#endif

+#if defined(TCC_TARGET_ARM64)
+static void tlv_patch_descriptor_offsets(struct macho *mo)
+{
+ Section *tv, *base;
+ addr_t off;
+
+ tv = mo->sk_to_sect[sk_thread_vars].s;
+ if (!tv)
+ return;
+ base = mo->sk_to_sect[sk_thread_data].s;
+ if (!base)
+ base = mo->sk_to_sect[sk_thread_bss].s;
+ if (!base)
+ return;
+ for (off = 0; off + 3 * PTR_SIZE <= tv->data_offset; off += 3 * PTR_SIZE) {
+ unsigned char *field = tv->data + off + 2 * PTR_SIZE;
+ write64le(field, read64le(field) - base->sh_addr);
+ }
+}
+#endif
+
ST_FUNC int macho_output_file(TCCState *s1, const char *filename)
{
int fd, mode, file_type;
@@ -2216,6 +2265,9 @@ ST_FUNC int macho_output_file(TCCState *s1, const char 
*filename)
s1->output_type = TCC_OUTPUT_EXE;
relocate_sections(s1);
s1->output_type = save_output;
+#if defined(TCC_TARGET_ARM64)
+ tlv_patch_descriptor_offsets(&mo);
+#endif
#ifdef CONFIG_NEW_MACHO
bind_rebase_import(s1, &mo); #endif
_________________________________
Richard Wheeler
_______________________________________________
Tinycc-devel mailing list
[email protected]
https://lists.nongnu.org/mailman/listinfo/tinycc-devel

Reply via email to