feat: implement Thread Local Storage frontend support

This commit is contained in:
Meng Zhuo 2026-05-12 19:57:26 +08:00
parent fad812360b
commit 8443e25bf5
4 changed files with 64 additions and 9 deletions

8
tcc.h
View File

@ -889,6 +889,7 @@ struct TCCState {
/* predefined sections */
Section *text_section, *data_section, *rodata_section, *bss_section;
Section *tdata_section, *tbss_section;
Section *common_section;
Section *cur_text_section; /* current section where function code is generated */
#ifdef CONFIG_TCC_BCHECK
@ -1063,7 +1064,8 @@ struct filespec {
#define VT_STATIC 0x00002000 /* static variable */
#define VT_TYPEDEF 0x00004000 /* typedef definition */
#define VT_INLINE 0x00008000 /* inline definition */
/* currently unused: 0x000[1248]0000 */
#define VT_TLS 0x00010000 /* thread-local storage */
/* currently unused: 0x000[248]0000 */
#define VT_STRUCT_SHIFT 20 /* shift for bitfield shift values (32 - 2*6) */
#define VT_STRUCT_MASK (((1U << (6+6)) - 1) << VT_STRUCT_SHIFT | VT_BITFIELD)
@ -1081,7 +1083,7 @@ struct filespec {
#define VT_ATOMIC VT_VOLATILE
/* type mask (except storage) */
#define VT_STORAGE (VT_EXTERN | VT_STATIC | VT_TYPEDEF | VT_INLINE)
#define VT_STORAGE (VT_EXTERN | VT_STATIC | VT_TYPEDEF | VT_INLINE | VT_TLS)
#define VT_TYPE (~(VT_STORAGE|VT_STRUCT_MASK))
/* symbol was created by tccasm.c first */
@ -1970,6 +1972,8 @@ static inline void post_sem(TCCSem *p) {
#define data_section TCC_STATE_VAR(data_section)
#define rodata_section TCC_STATE_VAR(rodata_section)
#define bss_section TCC_STATE_VAR(bss_section)
#define tdata_section TCC_STATE_VAR(tdata_section)
#define tbss_section TCC_STATE_VAR(tbss_section)
#define common_section TCC_STATE_VAR(common_section)
#define cur_text_section TCC_STATE_VAR(cur_text_section)
#define bounds_section TCC_STATE_VAR(bounds_section)

View File

@ -70,6 +70,8 @@ ST_FUNC void tccelf_new(TCCState *s)
/* create ro data section (make ro after relocation done with GNU_RELRO) */
rodata_section = new_section(s, rdata, SHT_PROGBITS, shf_RELRO);
bss_section = new_section(s, ".bss", SHT_NOBITS, SHF_ALLOC | SHF_WRITE);
tdata_section = new_section(s, ".tdata", SHT_PROGBITS, SHF_ALLOC | SHF_WRITE | SHF_TLS);
tbss_section = new_section(s, ".tbss", SHT_NOBITS, SHF_ALLOC | SHF_WRITE | SHF_TLS);
common_section = new_section(s, ".common", SHT_NOBITS, SHF_PRIVATE);
common_section->sh_num = SHN_COMMON;
@ -2294,7 +2296,7 @@ static int sort_sections(TCCState *s1, int *sec_order, struct dyn_inf *d)
if (k < 0x900)
++d->shnum;
if (k < 0x700) {
f = s->sh_flags & (SHF_ALLOC|SHF_WRITE|SHF_EXECINSTR|SHF_TLS);
f = s->sh_flags & (SHF_ALLOC|SHF_WRITE|SHF_EXECINSTR);
#if TARGETOS_NetBSD
/* NetBSD only supports 2 PT_LOAD sections.
See: https://blog.netbsd.org/tnf/entry/the_first_report_on_lld */
@ -2353,6 +2355,18 @@ static int layout_sections(TCCState *s1, int *sec_order, struct dyn_inf *d)
++phnum;
if (d->roinf)
++phnum;
{
int has_tls = 0;
for (i = 1; i < s1->nb_sections; i++) {
s = s1->sections[i];
if (s->sh_flags & SHF_TLS) {
has_tls = 1;
break;
}
}
if (has_tls)
++phnum;
}
d->phnum = phnum;
d->phdr = tcc_mallocz(phnum * sizeof(ElfW(Phdr)));
@ -2429,10 +2443,6 @@ static int layout_sections(TCCState *s1, int *sec_order, struct dyn_inf *d)
ph->p_flags |= PF_W;
if (f & SHF_EXECINSTR)
ph->p_flags |= PF_X;
if (f & SHF_TLS) {
ph->p_type = PT_TLS;
ph->p_align = align + 1;
}
ph->p_offset = file_offset;
ph->p_vaddr = addr;
@ -2477,6 +2487,34 @@ static int layout_sections(TCCState *s1, int *sec_order, struct dyn_inf *d)
fill_phdr(++ph, PT_GNU_EH_FRAME, eh_frame_hdr_section);
if (d->roinf)
fill_phdr(++ph, PT_GNU_RELRO, d->roinf)->p_flags |= PF_W;
{
/* Create PT_TLS segment covering all TLS sections */
Section *tls_start_sec = NULL;
addr_t tls_start = 0, tls_end = 0;
for (i = 1; i < s1->nb_sections; i++) {
s = s1->sections[i];
if (s->sh_flags & SHF_TLS && s->sh_size) {
if (!tls_start_sec) {
tls_start_sec = s;
tls_start = s->sh_addr;
tls_end = s->sh_addr + s->sh_size;
} else {
if (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 (tls_start_sec) {
ph = fill_phdr(++ph, PT_TLS, tls_start_sec);
ph->p_vaddr = tls_start;
ph->p_paddr = tls_start;
ph->p_filesz = tls_end - tls_start;
ph->p_memsz = ph->p_filesz;
ph->p_align = tls_start_sec->sh_addralign;
}
}
if (d->interp)
fill_phdr(&d->phdr[1], PT_INTERP, d->interp);
if (phfill) {

View File

@ -526,6 +526,8 @@ ST_FUNC void put_extern_sym2(Sym *sym, int sh_num,
sym_type = STT_NOTYPE;
if (IS_ASM_FUNC(t))
sym_type = STT_FUNC;
} else if (t & VT_TLS) {
sym_type = STT_TLS;
} else {
sym_type = STT_OBJECT;
}
@ -4931,7 +4933,12 @@ static int parse_btype(CType *type, AttributeDef *ad, int ignore_label)
}
goto basic_type2;
case TOK_THREAD_LOCAL:
tcc_error("_Thread_local is not implemented");
case TOK___thread:
if (t & VT_TLS)
tcc_error("multiple thread-local storage specifiers");
t |= VT_TLS;
next();
break;
default:
if (typespec_found)
goto the_end;
@ -8376,7 +8383,12 @@ static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r,
CType *tp = type;
while ((tp->t & (VT_BTYPE|VT_ARRAY)) == (VT_PTR|VT_ARRAY))
tp = &tp->ref->type;
if (tp->t & VT_CONSTANT) {
if (type->t & VT_TLS) {
if (has_init)
sec = tdata_section;
else
sec = tbss_section;
} else if (tp->t & VT_CONSTANT) {
sec = rodata_section;
} else if (has_init) {
sec = data_section;

View File

@ -39,6 +39,7 @@
DEF(TOK_RESTRICT3, "__restrict__")
DEF(TOK_EXTENSION, "__extension__") /* gcc keyword */
DEF(TOK_THREAD_LOCAL, "_Thread_local") /* C11 thread-local storage */
DEF(TOK___thread, "__thread") /* GCC thread-local storage extension */
DEF(TOK_GENERIC, "_Generic")
DEF(TOK_STATIC_ASSERT, "_Static_assert")