From 8502540b4aad94a8c349b0a3b7309363617a2ff9 Mon Sep 17 00:00:00 2001 From: Meng Zhuo Date: Wed, 13 May 2026 10:30:10 +0800 Subject: [PATCH] feat(i386): add TLS Local Exec code generation and linker relocations --- i386-gen.c | 20 ++++++++++++++++++++ i386-link.c | 33 ++++++++++++++++++++++++++++++++- tests/tests2/Makefile | 2 +- 3 files changed, 53 insertions(+), 2 deletions(-) diff --git a/i386-gen.c b/i386-gen.c index e46a9718..83ecaa34 100644 --- a/i386-gen.c +++ b/i386-gen.c @@ -313,6 +313,16 @@ ST_FUNC void load(int r, SValue *sv) #endif if (fr & VT_LVAL) { + if ((fr & VT_SYM) && sv->sym->type.t & VT_TLS) { + int dst_reg = REG_VALUE(r); + o(0x65); /* gs segment prefix */ + 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_386_TLS_LE, fc); + gen_le32(0); + return; + } if (v == VT_LLOCAL) { v1.type.t = VT_INT; v1.r = VT_LOCAL | VT_LVAL; @@ -429,6 +439,16 @@ ST_FUNC void store(int r, SValue *v) } else #endif + if ((fr & VT_SYM) && v->sym->type.t & VT_TLS) { + o(0x65); /* gs segment prefix */ + o(opc); + o(0x04 | (REG_VALUE(r) << 3)); /* modrm: [sib] | srcreg */ + o(0x25); /* sib: disp32 */ + greloca(cur_text_section, v->sym, ind, R_386_TLS_LE, fc); + gen_le32(0); + return; + } + if (fr == VT_CONST || fr == VT_LOCAL || (v->r & VT_LVAL)) { gen_modrm(opc, r, v->r, v->sym, fc); } else if (fr != r) { diff --git a/i386-link.c b/i386-link.c index 31d87c9e..bfccce02 100644 --- a/i386-link.c +++ b/i386-link.c @@ -305,7 +305,6 @@ ST_FUNC void relocate(TCCState *s1, ElfW_Rel *rel, int type, unsigned char *ptr, } return; case R_386_TLS_LDO_32: - case R_386_TLS_LE: { ElfW(Sym) *sym; Section *sec; @@ -317,6 +316,38 @@ ST_FUNC void relocate(TCCState *s1, ElfW_Rel *rel, int type, unsigned char *ptr, add32le(ptr, x); } return; + case R_386_TLS_LE: + { + 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); + } + return; case R_386_NONE: return; default: diff --git a/tests/tests2/Makefile b/tests/tests2/Makefile index 69e91e33..4bda9a4b 100644 --- a/tests/tests2/Makefile +++ b/tests/tests2/Makefile @@ -19,7 +19,7 @@ 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 riscv64 arm64 arm,$(ARCH))) +ifeq (,$(filter x86_64 riscv64 arm64 arm i386,$(ARCH))) SKIP += 144_tls.test # TLS only implemented on these architectures so far endif ifeq ($(CONFIG_backtrace),no)