TLS bits
Some checks failed
build and test / test-x86_64-linux (push) Has been cancelled
build and test / test-x86_64-osx (push) Has been cancelled
build and test / test-aarch64-osx (push) Has been cancelled
build and test / test-x86_64-win32 (push) Has been cancelled
build and test / test-i386-win32 (push) Has been cancelled
build and test / test-arm64-win32 (push) Has been cancelled
build and test / test-armv7-linux (push) Has been cancelled
build and test / test-aarch64-linux (push) Has been cancelled
build and test / test-riscv64-linux (push) Has been cancelled
build and test / test-i386-linux (push) Has been cancelled

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())
This commit is contained in:
grischka 2026-08-15 23:59:06 +02:00
parent 2ba12e83b3
commit 1bcec040d0
5 changed files with 99 additions and 93 deletions

View File

@ -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
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 */
o(ARM64_LDR_X_POST | 0x000107E0U | t); /* ldr xt, [sp], #16 */
}
} 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;
}

View File

@ -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"

View File

@ -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() */

26
tccpe.c
View File

@ -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" : ""

View File

@ -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;
}