From 1bcec040d0fec133a7dcc4d256a2f6333d0d15bd Mon Sep 17 00:00:00 2001 From: grischka Date: Sat, 15 Aug 2026 23:59:06 +0200 Subject: [PATCH] TLS bits tccelf.c: - set_elf_sym() check NOBITS instead of just .bss - sort_sections() must have common alignment for tls sections fix BSD relro tccpe.c: - sort .tbss after .tdata tccrun.c: - TLS not supported with -run - arm64: FlushInstructionCache on win32 too lib/bt-exe.c: - use less unused bss memory (from global vars in tcc.h) arm64-gen.c: - arm64_sym(): direct access (no GOT) for 'static' vars relocate with 'addend' unless GOT - reserve X16/x17 for internal usage (specifically to avoid their usage as value register passed to store()) --- arm64-gen.c | 113 +++++++++++++++++++++++---------------------------- lib/bt-exe.c | 2 +- tccelf.c | 39 +++++++++++------- tccpe.c | 26 ++++++------ tccrun.c | 12 +++++- 5 files changed, 99 insertions(+), 93 deletions(-) diff --git a/arm64-gen.c b/arm64-gen.c index 85aa7c0e..27e88414 100644 --- a/arm64-gen.c +++ b/arm64-gen.c @@ -77,14 +77,10 @@ ST_DATA const int reg_classes[NB_REGS] = { RC_INT | RC_R(13), RC_INT | RC_R(14), RC_INT | RC_R(15), - RC_INT | RC_R(16), - RC_INT | RC_R(17), -#ifdef TCC_TARGET_PE - RC_R(18), /* (x18 reserved on Windows) */ -#else - RC_INT | RC_R(18), -#endif - RC_R30, // not in RC_INT as we make special use of x30 + RC_R(16), /* reserved for generator (arm64_sym()) */ + RC_R(17), /* reserved for generator (arm64_sym()) */ + RC_R(18), /* reserved on Windows/OSX */ + RC_R30, /* not in RC_INT as we make special use of x30 */ RC_FLOAT | RC_F(0), RC_FLOAT | RC_F(1), RC_FLOAT | RC_F(2), @@ -298,6 +294,7 @@ static void arm64_spoff(int reg, uint64_t off) static uint64_t arm64_check_offset(int sz_, uint64_t off) { +#ifndef TCC_TARGET_PE uint32_t sz = sz_; uint64_t scaled_mask = 0xffful << sz; if (!(off & ~scaled_mask) || (off < 256 || -off <= 256)) @@ -306,6 +303,7 @@ static uint64_t arm64_check_offset(int sz_, uint64_t off) return scaled_mask; if (off & 0x1fful) return 0x1fful; +#endif return 0; } @@ -463,12 +461,17 @@ static void arm64_strv(int sz_, int dst, int bas, uint64_t off) } } +static void arm64_reloca(Sym *sym, int reloc_type, uint64_t addend) +{ + greloca(cur_text_section, sym, ind, reloc_type, addend); +} + static void arm64_sym(int r, Sym *sym, addr_t addend) { if (sym->type.t & VT_TLS) { -#if TCC_TARGET_PE +#ifdef TCC_TARGET_PE Sym *s2 = external_global_sym(TOK___tls_index, &int_type); - int r2 = get_reg(RC_INT); + int r2 = get_reg(RC_R(16)); arm64_sym(30, s2, 0); o(0xb94003de); // ldr w30, [x30] o(0xf9402e40 | r2); // ldr r2, [x18, #88] @@ -477,46 +480,35 @@ static void arm64_sym(int r, Sym *sym, addr_t addend) #else o(0xd53bd040 | r); /* mrs xr, tpidr_el0 */ #endif - greloca(cur_text_section, sym, ind, R_AARCH64_TLSLE_ADD_TPREL_HI12, 0); - /* add xr, xr, #0, lsl #12 */ - o(ARM64_ADD_IMM | ARM64_SF(1) | ARM64_SH(1) | ARM64_RN(r) | ARM64_RD(r)); - greloca(cur_text_section, sym, ind, R_AARCH64_TLSLE_ADD_TPREL_LO12, 0); - /* add xr, xr, #0 */ - o(ARM64_ADD_IMM | ARM64_SF(1) | ARM64_RN(r) | ARM64_RD(r)); - goto add_addend; - } -#ifdef TCC_TARGET_PE - /* PE links symbol addresses directly; there is no ELF-style GOT here. */ - greloca(cur_text_section, sym, ind, R_AARCH64_ADR_PREL_PG_HI21, 0); - o(ARM64_ADRP | r); // adrp xr, #sym - greloca(cur_text_section, sym, ind, R_AARCH64_ADD_ABS_LO12_NC, 0); - o(ARM64_ADD_IMM | ARM64_SF(1) | ARM64_RN(r) | r); // add xr, xr, #sym -#else - greloca(cur_text_section, sym, ind, R_AARCH64_ADR_GOT_PAGE, 0); - o(ARM64_ADRP | r); // adrp xr, #sym - greloca(cur_text_section, sym, ind, R_AARCH64_LD64_GOT_LO12_NC, 0); - o(ARM64_LDR_X | ARM64_RN(r) | r); // ld xr,[xr, #sym] -#endif -add_addend: - if (addend) { - // add xr, xr, #addend - if (addend & 0xffful) - o(ARM64_ADD_IMM | ARM64_SF(1) | ARM64_RN(r) | r | - (addend & 0xfff) << 10); - if (addend > 0xffful) { - // add xr, xr, #addend, lsl #12 - if (addend & 0xfff000ul) - o(ARM64_ADD_IMM | ARM64_SF(1) | ARM64_SH(1) | - ARM64_RN(r) | r | ((addend >> 12) & 0xfff) << 10); - if (addend > 0xfffffful) { - /* very unlikely */ - int t = r ? 0 : 1; - o(ARM64_STR_X_PRE | 0x001F0FE0U | t); /* str xt, [sp, #-16]! */ - arm64_movimm(t, addend & ~0xffffffull); // use xt for addent - o(ARM64_ADD_REG | ARM64_SF(1) | ARM64_RM(t) | ARM64_RN(r) | r); /* add xr, xr, xt */ - o(ARM64_LDR_X_POST | 0x000107E0U | t); /* ldr xt, [sp], #16 */ - } + arm64_reloca(sym, R_AARCH64_TLSLE_ADD_TPREL_HI12, addend); + o(ARM64_ADD_IMM | ARM64_SF(1) | ARM64_SH(1) | ARM64_RN(r) | ARM64_RD(r)); /* add xr, xr, #0, lsl #12 */ + arm64_reloca(sym, R_AARCH64_TLSLE_ADD_TPREL_LO12, addend); + o(ARM64_ADD_IMM | ARM64_SF(1) | ARM64_RN(r) | ARM64_RD(r)); /* add xr, xr, #0 */ +#ifndef TCC_TARGET_PE /* no elf-style GOT on Windows */ + } else if (!(sym->type.t & VT_STATIC)) { + arm64_reloca(sym, R_AARCH64_ADR_GOT_PAGE, 0); + o(ARM64_ADRP | r); /* adrp xr, #sym */ + arm64_reloca(sym, R_AARCH64_LD64_GOT_LO12_NC, 0); + o(ARM64_LDR_X | ARM64_RN(r) | r); /* ld xr,[xr, #sym] */ + if (addend > 0xffffff) { + int t = get_reg(RC_R(16)); + arm64_movimm(t, addend); // use xt for addent + o(ARM64_ADD_REG | ARM64_SF(1) | ARM64_RM(t) | ARM64_RN(r) | r); /* add xr, xr, xt */ + } else { + if (addend & 0xfff) /* add xr, xr, #addend */ + o(ARM64_ADD_IMM | ARM64_SF(1) + | ARM64_RN(r) | r | (addend & 0xfff) << 10); + addend >>= 12; + if (addend & 0xfff) /* add xr, xr, #addend, lsl #12 */ + o(ARM64_ADD_IMM | ARM64_SF(1) | ARM64_SH(1) + | ARM64_RN(r) | r | (addend & 0xfff) << 10); } +#endif + } else { + arm64_reloca(sym, R_AARCH64_ADR_PREL_PG_HI21, addend); + o(ARM64_ADRP | r); /* adrp xr, #sym */ + arm64_reloca(sym, R_AARCH64_ADD_ABS_LO12_NC, addend); + o(ARM64_ADD_IMM | ARM64_SF(1) | ARM64_RN(r) | r); /* add xr, xr, #sym */ } } @@ -678,8 +670,8 @@ ST_FUNC void store(int r, SValue *sv) static void arm64_gen_bl_or_b(int b) { - if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST && (vtop->r & VT_SYM)) { - greloca(cur_text_section, vtop->sym, ind, + if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == (VT_CONST | VT_SYM)) { + arm64_reloca(vtop->sym, b ? R_AARCH64_JUMP26 : R_AARCH64_CALL26, 0); o(b ? ARM64_B : ARM64_BL); // b/bl . } @@ -691,16 +683,16 @@ static void arm64_gen_bl_or_b(int b) } } -#if defined(CONFIG_TCC_BCHECK) - -static void gen_bounds_call(int v) +#if defined CONFIG_TCC_BCHECK || defined TCC_TARGET_PE +static void gen_static_call(int v) { Sym *sym = external_helper_sym(v); - - greloca(cur_text_section, sym, ind, R_AARCH64_CALL26, 0); + arm64_reloca(sym, R_AARCH64_CALL26, 0); o(ARM64_BL); // bl } +#endif +#if defined(CONFIG_TCC_BCHECK) static void gen_bounds_prolog(void) { /* leave some room for bound checking code */ @@ -735,7 +727,7 @@ static void gen_bounds_epilog(void) saved_ind = ind; ind = func_bound_ind; arm64_sym(0, sym_data, 0); - gen_bounds_call(TOK___bound_local_new); + gen_static_call(TOK___bound_local_new); ind = saved_ind; } @@ -743,7 +735,7 @@ static void gen_bounds_epilog(void) o(0xa9bf07e0); /* stp x0, x1, [sp, #-16]! */ o(0x3c9f0fe0); /* str q0, [sp, #-16]! */ arm64_sym(0, sym_data, 0); - gen_bounds_call(TOK___bound_local_delete); + gen_static_call(TOK___bound_local_delete); o(0x3cc107e0); /* ldr q0, [sp], #16 */ o(0xa8c107e0); /* ldp x0, x1, [sp], #16 */ } @@ -1004,11 +996,8 @@ static void arm64_sub_sp(uint64_t diff) return; #ifdef TCC_TARGET_PE if (diff >= 4096) { - Sym *sym = external_helper_sym(TOK___chkstk); - arm64_movimm(15, diff >> 4); - greloca(cur_text_section, sym, ind, R_AARCH64_CALL26, 0); - o(ARM64_BL); // bl __chkstk + gen_static_call(TOK___chkstk); o(0xcb2f73ff); // sub sp,sp,x15,lsl #4 return; } diff --git a/lib/bt-exe.c b/lib/bt-exe.c index 17fdc501..184006fa 100644 --- a/lib/bt-exe.c +++ b/lib/bt-exe.c @@ -3,7 +3,7 @@ from tccrun.c into executables. */ #define CONFIG_TCC_BACKTRACE_ONLY -#define ONE_SOURCE 1 +#define ONE_SOURCE 0 #define pstrcpy tcc_pstrcpy #include "../tccrun.c" diff --git a/tccelf.c b/tccelf.c index f2c67142..26bbeb9b 100644 --- a/tccelf.c +++ b/tccelf.c @@ -693,6 +693,14 @@ version_add (TCCState *s1) } #endif /* ndef ELF_OBJ_ONLY */ +/* catch .tbss also */ +static int IS_BSS(TCCState *s1, int ndx) +{ + return ndx == SHN_COMMON + || (ndx < s1->nb_sections + && s1->sections[ndx]->sh_type == SHT_NOBITS); +} + /* add an elf symbol : check if it is already defined and patch it. Return symbol index. NOTE that sh_num can be SHN_UNDEF. */ ST_FUNC int set_elf_sym(Section *s, addr_t value, unsigned long size, @@ -744,13 +752,10 @@ ST_FUNC int set_elf_sym(Section *s, addr_t value, unsigned long size, /* ignore hidden symbols after */ } else if (s->sh_flags & SHF_DYNSYM) { /* we accept that two DLL define the same symbol */ - } else if ((esym->st_shndx == SHN_COMMON - || esym->st_shndx == bss_section->sh_num) - && (shndx < SHN_LORESERVE - && shndx != bss_section->sh_num)) { + } else if (!IS_BSS(s1, shndx) && IS_BSS(s1, esym->st_shndx)) { /* data symbol gets precedence over common/bss */ goto do_patch; - } else if (shndx == SHN_COMMON || shndx == bss_section->sh_num) { + } else if (IS_BSS(s1, shndx)) { /* data symbol keeps precedence over common/bss */ } else if (esym->st_other & ST_ASM_SET) { /* If the existing symbol came from an asm .set @@ -2259,6 +2264,7 @@ static int sort_sections(TCCState *s1, int *sec_order, struct dyn_inf *d) int i, j, k, f, f0, n; int nb_sections = s1->nb_sections; int *sec_cls = sec_order + nb_sections; + int tls_align = 0; /* need common alignment for all tls sections */ for (i = 1; i < nb_sections; i++) { s = s1->sections[i]; @@ -2294,6 +2300,8 @@ static int sort_sections(TCCState *s1, int *sec_order, struct dyn_inf *d) k = 0x60; /* RELRO sections --> */ } else if (s->sh_flags & SHF_TLS) { + if (s->sh_addralign > tls_align) + tls_align = s->sh_addralign; k = 0x40 + (s->sh_type == SHT_NOBITS); } else if (s->sh_type == SHT_PREINIT_ARRAY) { k = 0x42; @@ -2302,11 +2310,11 @@ static int sort_sections(TCCState *s1, int *sec_order, struct dyn_inf *d) } else if (s->sh_type == SHT_FINI_ARRAY) { k = 0x44; } else if (s->sh_type == SHT_DYNAMIC) { - k = 0x46; + k = 0x48; } else if (s == s1->got) { - k = 0x47; /* .got as RELRO needs BIND_NOW in DT_FLAGS */ + k = 0x49; /* .got as RELRO needs BIND_NOW in DT_FLAGS */ } else if (s->reloc && (s->reloc->sh_flags & SHF_ALLOC) && j == 0x100) { - k = 0x45; + k = 0x45; /* +1 for data.ro */ /* <-- */ } else if (s->sh_type == SHT_NOTE) { k = 0x08, d->notes = 1; @@ -2349,15 +2357,18 @@ static int sort_sections(TCCState *s1, int *sec_order, struct dyn_inf *d) /* NetBSD only supports 2 PT_LOAD sections. See: https://blog.netbsd.org/tnf/entry/the_first_report_on_lld */ if ((f & SHF_WRITE) == 0) - f |= SHF_EXECINSTR, k = 0; /* no relro */ + f |= SHF_EXECINSTR; + k = 0; /* no relro */ #endif + if ((k & 0xfff0) == 0x240) /* RELRO sections */ + d->relro = 1, f |= SHFX_RELRO; /* start new header when flags changed, but avoid zero memsz */ if (f != f0 && s->sh_size) f0 = f, ++n, f |= SHFX_NEWPH; - if ((s->sh_flags & SHF_TLS) && s->sh_size) + if ((s->sh_flags & SHF_TLS) && s->sh_size) { + s->sh_addralign = tls_align; d->tls = 1, f |= SHF_TLS; - if ((k & 0xfff0) == 0x240) /* RELRO sections */ - d->relro = 1, f |= SHFX_RELRO; + } } sec_cls[i] = f; //printf("ph %d sec %02d : %3X %3X %8.2X %04X %s\n", (f>0) * n, i, f, k, s->sh_type, (int)s->sh_size, s->name); @@ -2484,7 +2495,7 @@ static int layout_sections(TCCState *s1, int *sec_order, struct dyn_inf *d) s->sh_offset = file_offset; s->sh_addr = addr; - //printf("%d : %08x %08x %04x %03x %s\n", (int)(ph - d->phdr), (int)file_offset, (int)addr, (int)s->sh_size, s->sh_type, s->name); + //printf("%d : %08x %08x %04x %03x %s %d\n", n, (int)file_offset, (int)addr, (int)s->sh_size, s->sh_type, s->name, align + 1); addr += s->sh_size; if (s->sh_type != SHT_NOBITS) file_offset += s->sh_size; @@ -2516,8 +2527,6 @@ static int layout_sections(TCCState *s1, int *sec_order, struct dyn_inf *d) } if (f & SHF_TLS) { ph2 = update_phdr(&d->phdr[d->tls], PT_TLS, s, addr, file_offset); - if (s->sh_addralign > ph2->p_align) - ph2->p_align = s->sh_addralign; if (s->sh_type == SHT_NOBITS) addr -= s->sh_size; /* for xxx-link.c:relocate() */ diff --git a/tccpe.c b/tccpe.c index 90eae16b..701a76af 100644 --- a/tccpe.c +++ b/tccpe.c @@ -351,7 +351,8 @@ enum { sec_bss , sec_idata , sec_pdata , - sec_tls , + sec_tdata , + sec_tbss , sec_other , sec_rsrc , sec_debug , @@ -1197,6 +1198,7 @@ static void pe_build_tls(struct pe_info *pe, Section *s) int c, n; if (0 == s) { + pe->tls_size = sizeof (IMAGE_TLS_DIRECTORY); pe->tls_dir = section_add(pe->thunk, pe->tls_size, 16); pe->tls_data = section_add(data_section, PTR_SIZE * (1+3), 16); /* put relocations on entries */ @@ -1209,6 +1211,7 @@ static void pe_build_tls(struct pe_info *pe, Section *s) 0, data_section->sh_num, "__tls_index"); return; } + if (0 == s1->tls_start) s1->tls_start = s->sh_addr; d = (void*)(pe->thunk->data + pe->tls_dir); @@ -1235,7 +1238,7 @@ static int pe_section_class(Section *s) return sec_debug; } else if (flags & SHF_ALLOC) { if (flags & SHF_TLS) - return sec_tls; + return sec_tdata + (type == SHT_NOBITS); if (type == SHT_PROGBITS || type == SHT_INIT_ARRAY || type == SHT_FINI_ARRAY) { @@ -1273,6 +1276,7 @@ static int pe_assign_addresses (struct pe_info *pe) if (PE_DLL == pe->type || (s1->pe_dll_characteristics & IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE)) pe->reloc = new_section(s1, ".reloc", SHT_PROGBITS, 0); + pe->thunk = rodata_section; //pe->thunk = new_section(s1, ".iedat", SHT_PROGBITS, SHF_ALLOC); nbs = s1->nb_sections; @@ -1284,35 +1288,30 @@ static int pe_assign_addresses (struct pe_info *pe) for (n = i; n > 1 && k < (c = sec_cls[n - 1]); --n) sec_cls[n] = c, sec_order[n] = sec_order[n - 1]; sec_cls[n] = k, sec_order[n] = i; - if (k == sec_tls) - pe->tls_size = sizeof (IMAGE_TLS_DIRECTORY); + if ((s->sh_flags & SHF_TLS) && 0 == pe->tls_size) + pe_build_tls(pe, NULL); } si = NULL; addr = pe->imagebase + 1; - for (i = 1; (c = sec_cls[i]) < sec_last; ++i) { s = s1->sections[sec_order[i]]; if (PE_MERGE_DATA && c == sec_bss) c = sec_data; + if (c == sec_tbss) + c = sec_tdata; if (si && c == si->cls && c != sec_debug) { /* merge with previous section */ - s->sh_addr = addr = ((addr - 1) | (16 - 1)) + 1; + s->sh_addr = addr = ((addr - 1) | (s->sh_addralign - 1)) + 1; } else { si = NULL; s->sh_addr = addr = pe_virtual_align(pe, addr); } - if (NULL == pe->thunk - && c == (data_section == rodata_section ? sec_data : sec_rdata)) - pe->thunk = s; - if (s == pe->thunk) { pe_build_imports(pe); pe_build_exports(pe); - if (pe->tls_size) - pe_build_tls(pe, NULL); } if (s == pe->reloc) @@ -1367,7 +1366,7 @@ add_section: Section *s = s1->sections[sec_order[i]]; int type = s->sh_type; int flags = s->sh_flags; - printf("section %-16s %-10s %p %04x %s,%s,%s\n", + printf("section %-16s %-10s %p %04x %2d %s,%s,%s\n", s->name, type == SHT_PROGBITS ? "progbits" : type == SHT_INIT_ARRAY ? "initarr" : @@ -1378,6 +1377,7 @@ add_section: type == SHT_RELX ? "rel" : "???", s->sh_addr, (unsigned)s->data_offset, + s->sh_addralign, flags & SHF_ALLOC ? "alloc" : "", flags & SHF_WRITE ? "write" : "", flags & SHF_EXECINSTR ? "exec" : "" diff --git a/tccrun.c b/tccrun.c index 4ff511df..1003ea5d 100644 --- a/tccrun.c +++ b/tccrun.c @@ -385,6 +385,9 @@ redo: continue; } + if ((s->sh_flags & SHF_TLS) && length) + return tcc_error_noabort("thread-local storage not supported with -run"); + align = s->sh_addralign; if (++n == 1) { #if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64 @@ -486,13 +489,18 @@ static int protect_pages(void *ptr, unsigned long length, int mode) }; if (mprotect(ptr, length, protect[mode])) return -1; +#endif /* XXX: BSD sometimes dump core with bad system call */ -# if (defined TCC_TARGET_ARM && !TARGETOS_BSD) || defined TCC_TARGET_ARM64 || defined TCC_TARGET_RISCV64 +#if (defined TCC_TARGET_ARM && !TARGETOS_BSD) \ + || defined TCC_TARGET_ARM64 || defined TCC_TARGET_RISCV64 if (mode == 0 || mode == 3) { +#ifdef _WIN32 + FlushInstructionCache(GetCurrentProcess(), ptr, length); +#else void __clear_cache(void *beginning, void *end); __clear_cache(ptr, (char *)ptr + length); +#endif } -# endif #endif return 0; }