diff --git a/tests/tests2/144_tls.c b/tests/tests2/144_tls.c new file mode 100644 index 00000000..ddae2ad7 --- /dev/null +++ b/tests/tests2/144_tls.c @@ -0,0 +1,50 @@ +#include +#include + +__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; +} diff --git a/tests/tests2/144_tls.expect b/tests/tests2/144_tls.expect new file mode 100644 index 00000000..2aad55c5 --- /dev/null +++ b/tests/tests2/144_tls.expect @@ -0,0 +1,8 @@ +42 +0 +42 +0 +100 +200 +42 +0 diff --git a/tests/tests2/Makefile b/tests/tests2/Makefile index 02138744..0fd98687 100644 --- a/tests/tests2/Makefile +++ b/tests/tests2/Makefile @@ -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' \ diff --git a/x86_64-gen.c b/x86_64-gen.c index 1cb532f8..5e6e96ca 100644 --- a/x86_64-gen.c +++ b/x86_64-gen.c @@ -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 diff --git a/x86_64-link.c b/x86_64-link.c index 62e20ff1..4a4f8581 100644 --- a/x86_64-link.c +++ b/x86_64-link.c @@ -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]; - x = val - sec->sh_addr - sec->data_offset; + + 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]; - x = val - sec->sh_addr - sec->data_offset; + + 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;