feat(x86_64): add TLS Local Exec support with tests

This commit is contained in:
Meng Zhuo 2026-05-12 20:01:59 +08:00
parent 8443e25bf5
commit ea26b85ac0
5 changed files with 141 additions and 6 deletions

50
tests/tests2/144_tls.c Normal file
View File

@ -0,0 +1,50 @@
#include <stdio.h>
#include <pthread.h>
__thread int tls_init = 42;
__thread int tls_zero;
static void *thread_func(void *arg)
{
(void)arg;
printf("%d\n", tls_init);
if (tls_init != 42) return (void *)1;
printf("%d\n", tls_zero);
if (tls_zero != 0) return (void *)1;
tls_init = 100;
tls_zero = 200;
printf("%d\n", tls_init);
printf("%d\n", tls_zero);
return (void *)0;
}
int main()
{
pthread_t t;
void *ret;
int errors = 0;
printf("%d\n", tls_init);
if (tls_init != 42) errors = 1;
printf("%d\n", tls_zero);
if (tls_zero != 0) errors = 1;
pthread_create(&t, NULL, thread_func, NULL);
pthread_join(t, &ret);
if (ret) errors = 1;
printf("%d\n", tls_init);
if (tls_init != 42) errors = 1;
printf("%d\n", tls_zero);
if (tls_zero != 0) errors = 1;
return errors;
}

View File

@ -0,0 +1,8 @@
42
0
42
0
100
200
42
0

View File

@ -19,6 +19,9 @@ ifeq (,$(filter i386 x86_64,$(ARCH)))
SKIP += 85_asm-outside-function.test # x86 asm
SKIP += 127_asm_goto.test # hardcodes x86 asm
endif
ifeq (,$(filter x86_64,$(ARCH)))
SKIP += 144_tls.test # TLS only implemented on x86_64 so far
endif
ifeq ($(CONFIG_backtrace),no)
SKIP += 113_btdll.test
CONFIG_bcheck = no
@ -107,6 +110,10 @@ GEN-ALWAYS =
# constructor/destructor
108_constructor.test: NORUN = true
# TLS needs executable and pthread, not -run
144_tls.test: FLAGS += -pthread
144_tls.test: NORUN = true
112_backtrace.test: FLAGS += -dt -b
112_backtrace.test 113_btdll.test 126_bound_global.test: FILTER += \
-e 's;[0-9A-Fa-fx]\{5,\};........;g' \

View File

@ -368,7 +368,8 @@ void load(int r, SValue *sv)
#ifndef TCC_TARGET_PE
/* we use indirect access via got */
if ((fr & VT_VALMASK) == VT_CONST && (fr & VT_SYM) &&
(fr & VT_LVAL) && !(sv->sym->type.t & VT_STATIC)) {
(fr & VT_LVAL) && !(sv->sym->type.t & VT_STATIC)
&& !(sv->sym->type.t & VT_TLS)) {
/* use the result register as a temporal register */
int tr = r | TREG_MEM;
if (is_float(ft)) {
@ -385,6 +386,19 @@ void load(int r, SValue *sv)
v = fr & VT_VALMASK;
if (fr & VT_LVAL) {
int b, ll;
if ((fr & VT_SYM) && sv->sym->type.t & VT_TLS) {
int dst_reg = REG_VALUE(r);
int is64 = is64_type(ft);
o(0x64); /* fs segment prefix */
if (is64 || REX_BASE(r))
o(0x40 | (REX_BASE(r) << 0) | (is64 << 3)); /* rex.w/rex.r */
o(0x8b); /* mov r/m, r */
o(0x04 | (dst_reg << 3)); /* modrm: [sib] | destreg */
o(0x25); /* sib: disp32 */
greloca(cur_text_section, sv->sym, ind, R_X86_64_TPOFF32, fc);
gen_le32(0);
return;
}
if (v == VT_LLOCAL) {
v1.type.t = VT_PTR;
v1.r = VT_LOCAL | VT_LVAL;
@ -572,6 +586,20 @@ void store(int r, SValue *v)
ft &= ~(VT_VOLATILE | VT_CONSTANT);
bt = ft & VT_BTYPE;
if ((v->r & VT_SYM) && v->sym->type.t & VT_TLS) {
int src_reg = REG_VALUE(r);
int is64 = is64_type(bt);
o(0x64);
if (is64 || REX_BASE(r))
o(0x40 | (REX_BASE(r) << 0) | (is64 << 3));
o(0x89);
o(0x04 | (src_reg << 3));
o(0x25);
greloca(cur_text_section, v->sym, ind, R_X86_64_TPOFF32, fc);
gen_le32(0);
return;
}
#ifndef TCC_TARGET_PE
/* we need to access the variable via got */
if (fr == VT_CONST

View File

@ -13,7 +13,7 @@
#define R_NUM R_X86_64_NUM
#define ELF_START_ADDR 0x400000
#define ELF_PAGE_SIZE 0x200000
#define ELF_PAGE_SIZE 0x1000
#define PCRELATIVE_DLLPLT 1
#define RELOCATE_DLLPLT 1
@ -96,13 +96,15 @@ ST_FUNC int gotplt_entry_type (int reloc_type)
case R_X86_64_TLSGD:
case R_X86_64_TLSLD:
case R_X86_64_DTPOFF32:
case R_X86_64_TPOFF32:
case R_X86_64_DTPOFF64:
case R_X86_64_TPOFF64:
case R_X86_64_REX_GOTPCRELX:
case R_X86_64_PLT32:
case R_X86_64_PLTOFF64:
return ALWAYS_GOTPLT_ENTRY;
case R_X86_64_TPOFF32:
case R_X86_64_TPOFF64:
return NO_GOTPLT_ENTRY;
}
return -1;
@ -372,10 +374,30 @@ ST_FUNC void relocate(TCCState *s1, ElfW_Rel *rel, int type, unsigned char *ptr,
ElfW(Sym) *sym;
Section *sec;
int32_t x;
addr_t tls_start = 0, tls_end = 0, tls_align = 1;
int i;
sym = &((ElfW(Sym) *)symtab_section->data)[sym_index];
sec = s1->sections[sym->st_shndx];
for (i = 1; i < s1->nb_sections; i++) {
Section *s = s1->sections[i];
if (s->sh_flags & SHF_TLS && s->sh_size) {
if (!tls_start || s->sh_addr < tls_start)
tls_start = s->sh_addr;
if (s->sh_addr + s->sh_size > tls_end)
tls_end = s->sh_addr + s->sh_size;
if (s->sh_addralign > tls_align)
tls_align = s->sh_addralign;
}
}
if (tls_end > tls_start) {
addr_t tls_size = tls_end - tls_start;
addr_t aligned_size = (tls_size + tls_align - 1) & ~(tls_align - 1);
x = val - (tls_start + aligned_size);
} else {
x = val - sec->sh_addr - sec->data_offset;
}
add32le(ptr, x);
}
break;
@ -385,10 +407,30 @@ ST_FUNC void relocate(TCCState *s1, ElfW_Rel *rel, int type, unsigned char *ptr,
ElfW(Sym) *sym;
Section *sec;
int32_t x;
addr_t tls_start = 0, tls_end = 0, tls_align = 1;
int i;
sym = &((ElfW(Sym) *)symtab_section->data)[sym_index];
sec = s1->sections[sym->st_shndx];
for (i = 1; i < s1->nb_sections; i++) {
Section *s = s1->sections[i];
if (s->sh_flags & SHF_TLS && s->sh_size) {
if (!tls_start || s->sh_addr < tls_start)
tls_start = s->sh_addr;
if (s->sh_addr + s->sh_size > tls_end)
tls_end = s->sh_addr + s->sh_size;
if (s->sh_addralign > tls_align)
tls_align = s->sh_addralign;
}
}
if (tls_end > tls_start) {
addr_t tls_size = tls_end - tls_start;
addr_t aligned_size = (tls_size + tls_align - 1) & ~(tls_align - 1);
x = val - (tls_start + aligned_size);
} else {
x = val - sec->sh_addr - sec->data_offset;
}
add64le(ptr, x);
}
break;