tcc.h dropped entirely, per your note. arm64_tlv_base is gone: relocate()
now uses s1->tls_start, already set two switch cases above for the
TLSLE_ADD_TPREL relocations and unused by any Mach-O code path before this.
tlv_sym is gone too: the descriptor-memoization slot now reuses attr->plt_sym,
whose only other reader/writer (build_got_entries() in tccelf.c) never runs
for Mach-O output. 3 files, 93 lines. Re-ran the full hardware test round
(memoization via otool, mixed init/uninit, cross-file link, otool -hv/-l,
make test) - byte-for-byte same results as before this change.
--------------------
diff --git a/arm64-gen.c b/arm64-gen.c
index 85aa7c0e..9081fbce 100644
--- a/arm64-gen.c
+++ b/arm64-gen.c
@@ -466,7 +466,25 @@ 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
+ 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, sym, ind, R_AARCH64_ADR_GOT_PAGE, 0);
+ o(ARM64_ADRP | 0); // adrp x0, &descriptor@page
+ greloca(cur_text_section, sym, 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/arm64-link.c b/arm64-link.c
index d3d925dd..cbf2a7da 100644
--- a/arm64-link.c
+++ b/arm64-link.c
@@ -45,6 +45,7 @@ ST_FUNC int code_reloc (int reloc_type)
case R_AARCH64_LDST8_ABS_LO12_NC:
case R_AARCH64_TLSLE_ADD_TPREL_HI12:
case R_AARCH64_TLSLE_ADD_TPREL_LO12:
+ case R_AARCH64_TLS_DTPREL64:
case R_AARCH64_GLOB_DAT:
case R_AARCH64_COPY:
return 0;
@@ -84,6 +85,7 @@ ST_FUNC int gotplt_entry_type (int reloc_type)
case R_AARCH64_TSTBR14:
case R_AARCH64_TLSLE_ADD_TPREL_HI12:
case R_AARCH64_TLSLE_ADD_TPREL_LO12:
+ case R_AARCH64_TLS_DTPREL64:
return NO_GOTPLT_ENTRY;
case R_AARCH64_ABS32:
@@ -202,6 +204,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/tccmacho.c b/tccmacho.c
index cee09fd7..fa1a3e91 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,7 @@ enum skind {
sk_fini,
sk_rw_data,
sk_bss,
+ sk_thread_vars, sk_thread_data, sk_thread_bss,
sk_linkedit,
sk_last
};
@@ -648,6 +653,28 @@ static void tcc_macho_add_destructor(TCCState *s1)
add_array (s1, ".init_array", init_sym);
}
+#if defined(TCC_TARGET_ARM64)
+static int arm64_macho_tlv_desc(TCCState *s1, int sym_index)
+{
+ Section *tv;
+ int boot_index, desc_index = get_sym_attr(s1, sym_index, 1)->plt_sym;
+ addr_t addr;
+ if (desc_index)
+ return desc_index;
+ tv = find_section(s1, ".thread_vars");
+ tv->sh_flags = SHF_ALLOC | SHF_WRITE, tv->sh_type = SHT_PROGBITS;
+ boot_index = find_elf_sym(s1->symtab, "__tlv_bootstrap");
+ if (!boot_index)
+ boot_index = put_elf_sym(s1->symtab, 0, 0, ELFW(ST_INFO)(STB_GLOBAL,
STT_FUNC), 0, SHN_UNDEF, "__tlv_bootstrap");
+ addr = section_add(tv, 3 * PTR_SIZE, PTR_SIZE);
+ put_elf_reloca(s1->symtab, tv, addr, R_DATA_PTR, boot_index, 0);
+ put_elf_reloca(s1->symtab, tv, addr + 2 * PTR_SIZE, R_AARCH64_TLS_DTPREL64,
sym_index, 0);
+ desc_index = put_elf_sym(s1->symtab, addr, PTR_SIZE, ELFW(ST_INFO)(STB_LOCAL,
STT_OBJECT), 0, tv->sh_num, NULL);
+ get_sym_attr(s1, sym_index, 1)->plt_sym = desc_index;
+ return desc_index;
+}
+#endif
+
#ifdef CONFIG_NEW_MACHO
static void bind_rebase_add(struct macho *mo, int bind, int sh_info,
ElfW_Rel *rel, struct sym_attr *attr)
@@ -693,9 +720,23 @@ static void check_relocs(TCCState *s1, struct macho *mo)
|| gotplt_entry == ALWAYS_GOTPLT_ENTRY) {
attr = get_sym_attr(s1, sym_index, 1);
if (!attr->dyn_index) {
+#if defined(TCC_TARGET_ARM64)
+ int orig_index = sym_index;
+ if (ELFW(ST_TYPE)(sym->st_info) == STT_TLS) {
+ sym_index = arm64_macho_tlv_desc(s1, sym_index);
+ sym = &((ElfW(Sym) *)symtab_section->data)[sym_index]; attr =
get_sym_attr(s1, sym_index, 1);
+ }
+#endif
attr->got_offset = s1->got->data_offset;
attr->plt_offset = -1;
attr->dyn_index = 1; /* used as flag */
+#if defined(TCC_TARGET_ARM64)
+ /* relocate() looks up got_offset via the original symbol, not the
descriptor. */
+ if (sym_index != orig_index) {
+ struct sym_attr *oattr = get_sym_attr(s1, orig_index, 1);
+ oattr->got_offset = attr->got_offset, oattr->dyn_index = 1;
+ }
+#endif
section_ptr_add(s1->got, PTR_SIZE);
put_elf_reloc(s1->symtab, s1->got, attr->got_offset,
R_JMP_SLOT, sym_index);
@@ -1247,6 +1288,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 +1693,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 +1730,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 +1804,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 +2032,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++)
@@ -2199,6 +2254,13 @@ ST_FUNC int macho_output_file(TCCState *s1, const char
*filename)
tcc_add_runtime(s1);
tcc_macho_add_destructor(s1);
resolve_common_syms(s1);
+#if defined(TCC_TARGET_ARM64)
+ /* Must run before create_symtab(), which sorts/snapshots the symbol table. */
+ { int i, n = symtab_section->data_offset / sizeof(ElfW(Sym));
+ for (i = 1; i < n; i++)
+ if (ELFW(ST_TYPE)(((ElfW(Sym) *)symtab_section->data)[i].st_info) == STT_TLS)
+ arm64_macho_tlv_desc(s1, i); }
+#endif
create_symtab(s1, &mo);
check_relocs(s1, &mo);
ret = check_symbols(s1, &mo);
@@ -2214,6 +2276,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 : mo.sk_to_sect[sk_thread_bss].s ?
mo.sk_to_sect[sk_thread_bss].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