diff --git a/arm64-gen.c b/arm64-gen.c index 6e6e4784..268319af 100644 --- a/arm64-gen.c +++ b/arm64-gen.c @@ -296,22 +296,17 @@ static void arm64_spoff(int reg, uint64_t off) } } -/* invert 0: return value to use for store/load */ -/* invert 1: return value to use for arm64_sym */ -static uint64_t arm64_check_offset(int invert, int sz_, uint64_t off) +static uint64_t arm64_check_offset(int sz_, uint64_t off) { uint32_t sz = sz_; uint64_t scaled_mask = 0xffful << sz; - - if (!(off & ~scaled_mask) || - (off < 256 || -off <= 256)) - return invert ? off : 0ul; - else if (off & scaled_mask) - return invert ? off & scaled_mask : off & ~scaled_mask; - else if (off & 0x1fful) - return invert ? off & 0x1fful : off & ~0x1fful; - else - return invert ? 0ul : off; + if (!(off & ~scaled_mask) || (off < 256 || -off <= 256)) + return ~0ull; + if (off & scaled_mask) + return scaled_mask; + if (off & 0x1fful) + return 0x1fful; + return 0; } static void arm64_ldrx(int sg, int sz_, int dst, int bas, uint64_t off) @@ -468,8 +463,41 @@ static void arm64_strv(int sz_, int dst, int bas, uint64_t off) } } -static void arm64_add_offset(int r, unsigned long addend) +static void arm64_sym(int r, Sym *sym, unsigned long addend) { + if (sym->type.t & VT_TLS) { +#if TCC_TARGET_PE + Sym *s2 = external_global_sym(TOK___tls_index, &int_type); + int r2 = get_reg(RC_INT); + arm64_sym(30, s2, 0); + o(0xb94003de); // ldr w30, [x30] + o(0xf9402e40 | r2); // ldr r2, [x18, #88] + o(0x8b1e0c1e | r2 << 5); // add x30, r2, x30, lsl #3 + o(0xf94003c0 | r); // ldr xr, [x30] +#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) @@ -484,7 +512,7 @@ static void arm64_add_offset(int r, unsigned long addend) /* very unlikely */ int t = r ? 0 : 1; o(ARM64_STR_X_PRE | 0x001F0FE0U | t); /* str xt, [sp, #-16]! */ - arm64_movimm(t, addend & ~0xfffffful); // use xt for addent + arm64_movimm(t, addend & ~0xffffffull); // 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 */ } @@ -492,111 +520,60 @@ static void arm64_add_offset(int r, unsigned long addend) } } -static void arm64_sym(int r, Sym *sym, unsigned long 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 - arm64_add_offset(r, addend); -} - static void arm64_load_cmp(int r, SValue *sv); -static void arm64_tls_sym(int r, Sym *sym, unsigned long addend) -{ - o(0xd53bd040 | r); /* mrs xr, tpidr_el0 */ - 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)); - arm64_add_offset(r, addend); -} - ST_FUNC void load(int r, SValue *sv) { int svtt = sv->type.t; - int svr = sv->r & ~(VT_BOUNDED | VT_NONCONST); + int svr = sv->r & (VT_VALMASK | VT_LVAL | VT_SYM); int svrv = svr & VT_VALMASK; uint64_t svcul = sv->c.i; - uint64_t svcoff = (uint64_t)(int64_t)(int32_t)sv->c.i; + uint64_t svcoff = (int32_t)sv->c.i; + int sb = !(svtt & VT_UNSIGNED); + int sz = arm64_type_size(svtt); if (svr == (VT_LOCAL | VT_LVAL)) { if (IS_FREG(r)) - arm64_ldrv(arm64_type_size(svtt), fltr(r), 29, svcoff); + arm64_ldrv(sz, fltr(r), 29, svcoff); else - arm64_ldrx(!(svtt & VT_UNSIGNED), arm64_type_size(svtt), - intr(r), 29, svcoff); + arm64_ldrx(sb, sz, intr(r), 29, svcoff); return; } if (svr == (VT_CONST | VT_LVAL)) { - uint64_t i = sv->c.i; - - if (sv->sym && (sv->sym->type.t & VT_TLS)) - arm64_tls_sym(30, sv->sym, 0); - else if (sv->sym) - arm64_sym(30, sv->sym, // use x30 for address - arm64_check_offset(0, arm64_type_size(svtt), i)); - else - arm64_movimm (30, i), i = 0; + arm64_movimm (30, svcul); if (IS_FREG(r)) - arm64_ldrv(arm64_type_size(svtt), fltr(r), 30, - arm64_check_offset(1, arm64_type_size(svtt), i)); + arm64_ldrv(sz, fltr(r), 30, 0); else - arm64_ldrx(!(svtt&VT_UNSIGNED), arm64_type_size(svtt), intr(r), 30, - arm64_check_offset(1, arm64_type_size(svtt), i)); + arm64_ldrx(sb, sz, intr(r), 30, 0); return; } - if ((svr & ~VT_VALMASK) == VT_LVAL && svrv < VT_CONST) { - if ((svtt & VT_BTYPE) != VT_VOID) { - if (IS_FREG(r)) - arm64_ldrv(arm64_type_size(svtt), fltr(r), intr(svrv), 0); - else - arm64_ldrx(!(svtt & VT_UNSIGNED), arm64_type_size(svtt), - intr(r), intr(svrv), 0); - } + if (svrv < VT_CONST && (svr & VT_LVAL)) { + if (IS_FREG(r)) + arm64_ldrv(sz, fltr(r), intr(svrv), 0); + else + arm64_ldrx(sb, sz, intr(r), intr(svrv), 0); return; } if (svr == (VT_CONST | VT_LVAL | VT_SYM)) { - if (sv->sym->type.t & VT_TLS) - arm64_tls_sym(30, sv->sym, 0); - else - arm64_sym(30, sv->sym, // use x30 for address - arm64_check_offset(0, arm64_type_size(svtt), svcoff)); + uint64_t mask = arm64_check_offset(sz, svcoff); + arm64_sym(30, sv->sym, svcoff & ~mask); if (IS_FREG(r)) - arm64_ldrv(arm64_type_size(svtt), fltr(r), 30, - arm64_check_offset(1, arm64_type_size(svtt), svcoff)); + arm64_ldrv(sz, fltr(r), 30, svcoff & mask); else - arm64_ldrx(!(svtt&VT_UNSIGNED), arm64_type_size(svtt), intr(r), 30, - arm64_check_offset(1, arm64_type_size(svtt), svcoff)); + arm64_ldrx(sb, sz, intr(r), 30, svcoff & mask); return; } if (svr == (VT_CONST | VT_SYM)) { - if (sv->sym->type.t & VT_TLS) - arm64_tls_sym(intr(r), sv->sym, svcul); - else - arm64_sym(intr(r), sv->sym, svcul); + arm64_sym(intr(r), sv->sym, svcul); return; } if (svr == VT_CONST) { - if ((svtt & VT_BTYPE) != VT_VOID) - arm64_movimm(intr(r), arm64_type_size(svtt) == 3 ? - sv->c.i : (uint32_t)svcul); + arm64_movimm(intr(r), sz == 3 ? svcul : (uint32_t)svcul); return; } @@ -636,9 +613,9 @@ ST_FUNC void load(int r, SValue *sv) if (svr == (VT_LLOCAL | VT_LVAL)) { arm64_ldrx(0, 3, 30, 29, svcoff); // use x30 for offset if (IS_FREG(r)) - arm64_ldrv(arm64_type_size(svtt), fltr(r), 30, 0); + arm64_ldrv(sz, fltr(r), 30, 0); else - arm64_ldrx(!(svtt & VT_UNSIGNED), arm64_type_size(svtt), + arm64_ldrx(sb, sz, intr(r), 30, 0); return; } @@ -655,63 +632,43 @@ ST_FUNC void load(int r, SValue *sv) ST_FUNC void store(int r, SValue *sv) { int svtt = sv->type.t; - int svr = sv->r & ~VT_BOUNDED; + int svr = sv->r & (VT_VALMASK | VT_LVAL | VT_SYM); int svrv = svr & VT_VALMASK; - uint64_t svcoff = (uint64_t)(int64_t)(int32_t)sv->c.i; + uint64_t svcoff = (int32_t)sv->c.i; + int sz = arm64_type_size(svtt); if (svr == (VT_LOCAL | VT_LVAL)) { if (IS_FREG(r)) - arm64_strv(arm64_type_size(svtt), fltr(r), 29, svcoff); + arm64_strv(sz, fltr(r), 29, svcoff); else - arm64_strx(arm64_type_size(svtt), intr(r), 29, svcoff); + arm64_strx(sz, intr(r), 29, svcoff); return; } if (svr == (VT_CONST | VT_LVAL)) { - uint64_t i = sv->c.i; - if (sv->sym && (sv->sym->type.t & VT_TLS)) - arm64_tls_sym(30, sv->sym, 0); - else - if (sv->sym) - arm64_sym(30, sv->sym, // use x30 for address - arm64_check_offset(0, arm64_type_size(svtt), i)); - else - arm64_movimm (30, i), i = 0; - + arm64_movimm (30, sv->c.i); if (IS_FREG(r)) - arm64_strv(arm64_type_size(svtt), fltr(r), 30, - arm64_check_offset(1, arm64_type_size(svtt), i)); + arm64_strv(sz, fltr(r), 30, 0); else - arm64_strx(arm64_type_size(svtt), intr(r), 30, - arm64_check_offset(1, arm64_type_size(svtt), i)); + arm64_strx(sz, intr(r), 30, 0); return; } - if ((svr & ~VT_VALMASK) == VT_LVAL && svrv < VT_CONST) { + if (svrv < VT_CONST && (svr & VT_LVAL)) { if (IS_FREG(r)) - arm64_strv(arm64_type_size(svtt), fltr(r), intr(svrv), 0); + arm64_strv(sz, fltr(r), intr(svrv), 0); else - arm64_strx(arm64_type_size(svtt), intr(r), intr(svrv), 0); + arm64_strx(sz, intr(r), intr(svrv), 0); return; } if (svr == (VT_CONST | VT_LVAL | VT_SYM)) { - if (sv->sym->type.t & VT_TLS) { - arm64_tls_sym(30, sv->sym, 0); - if (IS_FREG(r)) - arm64_strv(arm64_type_size(svtt), fltr(r), 30, svcoff); - else - arm64_strx(arm64_type_size(svtt), intr(r), 30, svcoff); - return; - } - arm64_sym(30, sv->sym, // use x30 for address - arm64_check_offset(0, arm64_type_size(svtt), svcoff)); + uint64_t mask = arm64_check_offset(sz, svcoff); + arm64_sym(30, sv->sym, svcoff & ~mask); if (IS_FREG(r)) - arm64_strv(arm64_type_size(svtt), fltr(r), 30, - arm64_check_offset(1, arm64_type_size(svtt), svcoff)); + arm64_strv(sz, fltr(r), 30, svcoff & mask); else - arm64_strx(arm64_type_size(svtt), intr(r), 30, - arm64_check_offset(1, arm64_type_size(svtt), svcoff)); + arm64_strx(sz, intr(r), 30, svcoff & mask); return; } @@ -1789,9 +1746,9 @@ static int arm64_gen_opic(int op, uint32_t l, int rev, uint64_t val, uint32_t s = l ? val >> 63 : val >> 31; val = s ? -val : val; val = l ? val : (uint32_t)val; - if (!(val & ~0xffful)) + if (!(val & ~0xfffull)) o(0x11000000 | l << 31 | s << 30 | x | a << 5 | val << 10); - else if (!(val & ~0xfff000ul)) + else if (!(val & ~0xfff000ull)) o(0x11400000 | l << 31 | s << 30 | x | a << 5 | val >> 12 << 10); else { arm64_movimm(30, val); // use x30 diff --git a/arm64-link.c b/arm64-link.c index dd02479a..d3d925dd 100644 --- a/arm64-link.c +++ b/arm64-link.c @@ -371,8 +371,12 @@ ST_FUNC void relocate(TCCState *s1, ElfW_Rel *rel, int type, unsigned char *ptr, return; case R_AARCH64_TLSLE_ADD_TPREL_HI12: case R_AARCH64_TLSLE_ADD_TPREL_LO12: { +#if TCC_TARGET_PE + int64_t tp_offset = val - s1->tls_start; +#else /* glibc arm64: tp points to tcbhead_t (DTV), TLS data starts after it */ int64_t tp_offset = val - s1->tls_start + 16; +#endif int64_t imm; if (type == R_AARCH64_TLSLE_ADD_TPREL_HI12) imm = (tp_offset >> 12) & 0xfff; diff --git a/configure b/configure index 97999e1c..c63c7403 100755 --- a/configure +++ b/configure @@ -363,8 +363,10 @@ case $targetos in default_conf "codesign" DLLSUF=".dylib" if test -z "$build_cross"; then - cc=`command -v cc` - cc=`readlink $cc || echo clang` + if test "$cc" = "gcc"; then + cc=`command -v cc` + cc=`readlink $cc || echo clang` + fi tcc_usrinclude="`xcrun --show-sdk-path`/usr/include" default tcc_sysincludepaths "{B}/include:$tcc_usrinclude" if test "${confvars%new_macho*}" = "${confvars}"; then diff --git a/i386-gen.c b/i386-gen.c index 54d24806..36a246ed 100644 --- a/i386-gen.c +++ b/i386-gen.c @@ -242,12 +242,20 @@ static void gen_modrm(int opc, int op_r2, int r, Sym *sym, int c) int op_reg = REG_VALUE(op_r2) << 3; if ((r & VT_SYM) && (sym->type.t & VT_TLS)) { - if (opc == 0xdbc0d9) // VT_LDOUBLE - o(0xc0d9), opc = 0xdb; - o(0x65); /* gs segment prefix */ - o(opc); - oad(0x05 | op_reg, c); +#ifdef TCC_TARGET_PE + Sym *s2 = external_global_sym(TOK___tls_index, &int_type); + r = get_reg(RC_INT); + gen_modrm(0x8b, r, VT_SYM|VT_CONST, s2, 0); /* mov __tls_index, r */ + o(0x02e0c1 | r << 8); /* shl 2,r */ + oad(0x050364 | r << 19, 11*PTR_SIZE); /* add fs:0x2c,r */ + gen_modrm(0x8b, r, r | VT_LVAL, 0, 0); /* mov (r),r */ + o(opc), oad(0x80 | op_reg | r, c); /* mov #c(r),op_reg */ greloc(cur_text_section, sym, ind - 4, R_386_TLS_LE); +#else + o(0x65); /* gs segment prefix */ + o(opc), oad(0x05 | op_reg, c); + greloc(cur_text_section, sym, ind - 4, R_386_TLS_LE); +#endif #if defined CONFIG_TCC_PIC } else if ((r & (VT_VALMASK|VT_SYM)) == (VT_CONST|VT_SYM)) { int is_got = (op_r2 & TREG_MEM) && !(sym->type.t & VT_STATIC); @@ -353,9 +361,13 @@ ST_FUNC void load(int r, SValue *sv) gen_modrm(opc, r, fr, sv->sym, fc); } else { if ((fr & VT_SYM) && (sv->sym->type.t & VT_TLS)) { +#ifdef TCC_TARGET_PE + gen_modrm(0x8d, r, fr, sv->sym, fc); +#else oad(0x058b65 | REG_VALUE(r) << 19, 0); /* mov gs:0,r */ oad(0xC081 | REG_VALUE(r) << 8, fc); /* add tpoffs,r */ greloc(cur_text_section, sv->sym, ind - 4, R_386_TLS_LE); +#endif #if defined CONFIG_TCC_PIC } else if ((fr & (VT_VALMASK|VT_SYM)) == (VT_CONST|VT_SYM)) { if (sv->sym->type.t & VT_STATIC) { @@ -412,7 +424,8 @@ ST_FUNC void store(int r, SValue *v) opc = 0xdd; /* fstpl */ r = 2; } else if (bt == VT_LDOUBLE) { - opc = 0xdbc0d9; /* fld %st(0), fstpt */ + o(0xc0d9); /* fld %st(0), fstpt */ + opc = 0xdb; r = 7; } else if (bt == VT_SHORT) { opc = 0x8966; diff --git a/libtcc.c b/libtcc.c index fcc035a9..44ea25fa 100644 --- a/libtcc.c +++ b/libtcc.c @@ -1689,6 +1689,7 @@ static const TCCOption tcc_options[] = { { "-param", 0, TCC_OPTION_HAS_ARG }, { "pedantic", 0, 0 }, { "pie", 0, 0 }, + { "no-pie", 0, 0 }, { "pipe", 0, 0 }, { "s", 0, 0 }, { "traditional", 0, 0 }, diff --git a/tcc.h b/tcc.h index 15333b3d..abffcbfe 100644 --- a/tcc.h +++ b/tcc.h @@ -71,6 +71,9 @@ extern long double strtold (const char *__nptr, char **__endptr); # define strtoll _strtoi64 # define strtoull _strtoui64 # endif + /* some compilers don't know ldexpl and windows doesn't have long doubles anyway */ +# undef ldexpl +# define ldexpl ldexp # ifdef LIBTCC_AS_DLL # define LIBTCCAPI __declspec(dllexport) # define PUB_FUNC LIBTCCAPI diff --git a/tccgen.c b/tccgen.c index 218fb063..227ff618 100644 --- a/tccgen.c +++ b/tccgen.c @@ -842,7 +842,7 @@ ST_FUNC void label_pop(Sym **ptop, Sym *slast, int keep) if (s->c) { /* define corresponding symbol. A size of 1 is put. */ - put_extern_sym(s, cur_text_section, s->jnext, 1); + put_extern_sym(s, cur_text_section, s->jind, 1); } } /* remove label */ diff --git a/tccpe.c b/tccpe.c index ab30268e..90eae16b 100644 --- a/tccpe.c +++ b/tccpe.c @@ -241,6 +241,15 @@ typedef struct _IMAGE_EXPORT_DIRECTORY { DWORD AddressOfNameOrdinals; } IMAGE_EXPORT_DIRECTORY,*PIMAGE_EXPORT_DIRECTORY; +typedef struct _IMAGE_TLS_DIRECTORY { + ADDR3264 StartAddressOfRawData; + ADDR3264 EndAddressOfRawData; + ADDR3264 AddressOfIndex; + ADDR3264 AddressOfCallBacks; + DWORD SizeOfZeroFill; + DWORD Characteristics; +} IMAGE_TLS_DIRECTORY; + typedef struct _IMAGE_IMPORT_DESCRIPTOR { union { DWORD Characteristics; @@ -342,6 +351,7 @@ enum { sec_bss , sec_idata , sec_pdata , + sec_tls , sec_other , sec_rsrc , sec_debug , @@ -404,6 +414,9 @@ struct pe_info { DWORD iat_size; DWORD exp_offs; DWORD exp_size; + DWORD tls_dir; + DWORD tls_data; + DWORD tls_size; int subsystem; DWORD section_align; DWORD file_align; @@ -758,6 +771,10 @@ static int pe_write(struct pe_info *pe) pe_set_datadir(&pe_header, IMAGE_DIRECTORY_ENTRY_EXPORT, pe->exp_offs, pe->exp_size); } + if (pe->tls_size) { + pe_set_datadir(&pe_header, IMAGE_DIRECTORY_ENTRY_TLS, + pe->tls_dir + (pe->thunk->sh_addr - pe->imagebase), pe->tls_size); + } memcpy(psh->Name, sh_name, umin(strlen(sh_name), sizeof psh->Name)); if (pe->coffstr && strlen(sh_name) > 8) { @@ -1172,6 +1189,39 @@ static void pe_build_reloc (struct pe_info *pe) } } +/* ------------------------------------------------------------- */ +static void pe_build_tls(struct pe_info *pe, Section *s) +{ + TCCState *s1 = pe->s1; + IMAGE_TLS_DIRECTORY *d; + int c, n; + + if (0 == s) { + 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 */ + c = put_elf_sym(symtab_section, 0, 0, 0, 0, data_section->sh_num, 0); + for (n = 0; n < 4; ++n) + put_elf_reloc(symtab_section, pe->thunk, pe->tls_dir + PTR_SIZE*n, REL_TYPE_DIRECT, c); + /* for generators */ + set_elf_sym(symtab_section, pe->tls_data, PTR_SIZE * 4, + ELFW(ST_INFO)(STB_GLOBAL, STT_OBJECT), + 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); + d->StartAddressOfRawData = s1->tls_start - data_section->sh_addr; + d->EndAddressOfRawData = s->sh_addr + s->data_offset - data_section->sh_addr; + d->AddressOfIndex = pe->tls_data; + d->AddressOfCallBacks = pe->tls_data + PTR_SIZE; + d->SizeOfZeroFill = 0; + d->Characteristics = 0; + /* to reuse logic from linux in xxx-link.c */ + s1->tls_end = s1->tls_start; +} + /* ------------------------------------------------------------- */ static int pe_section_class(Section *s) { @@ -1184,6 +1234,8 @@ static int pe_section_class(Section *s) if (0 == memcmp(name, ".stab", 5) || 0 == memcmp(name, ".debug_", 7)) { return sec_debug; } else if (flags & SHF_ALLOC) { + if (flags & SHF_TLS) + return sec_tls; if (type == SHT_PROGBITS || type == SHT_INIT_ARRAY || type == SHT_FINI_ARRAY) { @@ -1232,6 +1284,8 @@ 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); } si = NULL; addr = pe->imagebase + 1; @@ -1257,7 +1311,10 @@ static int pe_assign_addresses (struct pe_info *pe) 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) pe_build_reloc (pe); @@ -1277,7 +1334,7 @@ static int pe_assign_addresses (struct pe_info *pe) si->pe_flags = IMAGE_SCN_MEM_READ; if (s->sh_flags & SHF_EXECINSTR) si->pe_flags |= IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_CNT_CODE; - else if (s->sh_type == SHT_NOBITS) + else if (s->sh_type == SHT_NOBITS && !(s->sh_flags & SHF_TLS)) si->pe_flags |= IMAGE_SCN_CNT_UNINITIALIZED_DATA; else si->pe_flags |= IMAGE_SCN_CNT_INITIALIZED_DATA; @@ -1297,6 +1354,12 @@ add_section: *ps = s, s->prev = NULL; si->data_size = si->sh_size; } + + if (s->sh_flags & SHF_TLS) { + strcpy(si->name, ".tls"); + pe_build_tls(pe, s); + } + //printf("%08x %05x %08x %s\n", si->sh_addr, si->sh_size, si->pe_flags, s->name); } #if 0 @@ -1766,35 +1829,28 @@ static char *trimback(char *a, char *e) static char *get_token(char **s, char *f) { - char *p = *s, *e; - p = e = trimfront(p); - while ((unsigned char)*e > ' ') - ++e; - *s = trimfront(e); - *f = **s; *e = 0; - return p; -} + char *p, *e; + int q; -/* Read a LIBRARY argument whose quotes may protect whitespace. */ -static char *get_libname(char **s, char *f) -{ - char *p = trimfront(*s), *e; - if (*p != '"') - return get_token(s, f); - for (e = ++p; *e && *e != '"' && *e != '\r' && *e != '\n'; ++e) + p = trimfront(*s); + q = *p; + if (q == '"') /* support quoted LIBRARY "xyz.dll" */ + ++p; + else + q = ' '; + for (e = p; (unsigned char)*e >= ' ' && *e != q; ++e) ; - if (*e != '"' || (unsigned char)e[1] > ' ') - return NULL; - *s = trimfront(e + 1); - *f = **s; - *e = 0; + if (*e == '"') + *e++ = 0; + *s = trimfront(e); + *f = **s, *e = 0; return p; } static int pe_load_def(TCCState *s1, int fd) { int state = 0, ret = -1, dllindex = 0, ord; - char *dllname = NULL, *buf, *line, *p, *x, next; + char dllname[80], *buf, *line, *p, *x, next; buf = tcc_load_text(fd); if (!buf) @@ -1808,13 +1864,9 @@ static int pe_load_def(TCCState *s1, int fd) case 0: if (0 != stricmp(p, "LIBRARY") || next == '\n') goto quit; - p = get_libname(&line, &next); - if (!p || !*p) - goto quit; - dllname = tcc_malloc(strlen(p) + 5); - strcpy(dllname, p); + pstrcpy(dllname, sizeof dllname, get_token(&line, &next)); if (!*tcc_fileextension(dllname)) - strcat(dllname, ".dll"); + pstrcat(dllname, sizeof dllname, ".dll"); ++state; break; case 1: @@ -1845,7 +1897,6 @@ skip: } ret = 0; quit: - tcc_free(dllname); tcc_free(buf); return ret; } diff --git a/tccpp.c b/tccpp.c index c6bf1860..214e9cd4 100644 --- a/tccpp.c +++ b/tccpp.c @@ -21,21 +21,6 @@ #define USING_GLOBALS #include "tcc.h" -#if defined(_WIN32) && defined(__TINYC__) - /* allow self-host build with tcc 0.9.27 - doesn't have ldexpl in tcc_libm.h . - * - * in tcc 0.9.27 both ldexp and ldexpl are declared in win32/include/math.h, - * but only ldexp can be linked - via win32/lib/msvcrt.def. we can't test - * whether we have ldexpl or not, so map ldexpl to ldexp unconditionally. - * - * note that ldexpl takes long double while ldexp takes double, however, on - * windows these types are identical, and current ldexpl in tcc_libm.h also - * uses the "normal" double scalbn - just like ldexp, so do the same here. - */ - #undef ldexpl - #define ldexpl ldexp -#endif - /* #define to 1 to enable (see parse_pp_string()) */ #define ACCEPT_LF_IN_STRINGS 0 diff --git a/tcctok.h b/tcctok.h index 458e106d..d8e7b445 100644 --- a/tcctok.h +++ b/tcctok.h @@ -306,6 +306,7 @@ #if defined TCC_TARGET_PE DEF(TOK___chkstk, "__chkstk") + DEF(TOK___tls_index, "__tls_index") #endif #ifdef TCC_TARGET_ARM64 DEF(TOK___arm64_clear_cache, "__arm64_clear_cache") diff --git a/tests/test-win32.bat b/tests/test-win32.bat index bf593cbf..ef3b4afe 100644 --- a/tests/test-win32.bat +++ b/tests/test-win32.bat @@ -70,5 +70,5 @@ echo>>%CFG_MAK% TOPSRC = $(TOP) echo>>%CFG_MAK% SHELL = sh echo>>%CFG_MAK% test.ref: CFLAGS += %REF_LINK% -set GMAKE=make +if "%GMAKE%"=="" set GMAKE=make %GMAKE% TCC_LOCAL=tcc.exe LIBTCC=win32/%LIBTCC% %TESTS% diff --git a/tests/tests2/124_atomic_counter.c b/tests/tests2/124_atomic_counter.c index 42de43d4..5017ad50 100644 --- a/tests/tests2/124_atomic_counter.c +++ b/tests/tests2/124_atomic_counter.c @@ -2,7 +2,7 @@ #include #include #include -#include +#include "threads.h" #include #define NR_THREADS 16 diff --git a/tests/tests2/144_tls.c b/tests/tests2/144_tls.c index 82c844ce..c7134ede 100644 --- a/tests/tests2/144_tls.c +++ b/tests/tests2/144_tls.c @@ -1,5 +1,11 @@ #include -#include +#include "threads.h" + +#if __SIZEOF_POINTER__ == 8 +# define pFMT "0x%llx" +#else +# define pFMT "0x%x" +#endif #define CHECK(var,fmt,val) printf(fmt "\n",var); if (var != val) errors = 1 @@ -70,7 +76,7 @@ static int check(void) CHECK(tls_float, "%g", 46.0); CHECK(tls_double, "%g", 47.0); CHECK(tls_long_double, "%Lg", 48.0); - CHECK(tls_ptr, "%p", (int *)49); + CHECK(tls_ptr, pFMT, (int *)49); CHECK(tls_enum, "%d", tls_b); CHECK(tls_struct.tls_d, "%d", 50); CHECK(tls_struct.tls_e, "%d", 51); @@ -121,7 +127,7 @@ static void *thread_func(void *arg) CHECK(tls_double, "%g", 70.0); CHECK(tls_long_double, "%Lg", 80.0); CHECK(tls_enum, "%d", tls_c); - CHECK(tls_ptr, "%p", (int *)90); + CHECK(tls_ptr, pFMT, (int *)90); CHECK(tls_struct.tls_d, "%d", 100); CHECK(tls_struct.tls_e, "%d", 110); CHECK(tls_struct.tls_f, "%d", 120); @@ -130,7 +136,7 @@ static void *thread_func(void *arg) || tls_array[index] != -3333 || check_extra(1)) errors = 1; - return (void *)errors; + return (void *)(size_t)errors; } int main() diff --git a/tests/tests2/147_def_library.c b/tests/tests2/147_def_library.c deleted file mode 100644 index 8e1d9b3f..00000000 --- a/tests/tests2/147_def_library.c +++ /dev/null @@ -1,21 +0,0 @@ -#ifdef DEF_LIBRARY_DLL - -__declspec(dllexport) int def_library_value(void) -{ - return 42; -} - -#else - -#include - -int def_library_value(void); - -int main(void) -{ - int value = def_library_value(); - printf("value: %d\n", value); - return value != 42; -} - -#endif diff --git a/tests/tests2/147_def_library.def.in b/tests/tests2/147_def_library.def.in deleted file mode 100644 index aedf95d0..00000000 --- a/tests/tests2/147_def_library.def.in +++ /dev/null @@ -1,3 +0,0 @@ -LIBRARY "def library test" -EXPORTS - def_library_value diff --git a/tests/tests2/147_def_library.expect b/tests/tests2/147_def_library.expect deleted file mode 100644 index 84a64c37..00000000 --- a/tests/tests2/147_def_library.expect +++ /dev/null @@ -1 +0,0 @@ -value: 42 diff --git a/tests/tests2/Makefile b/tests/tests2/Makefile index d8c4ebb8..91a043cf 100644 --- a/tests/tests2/Makefile +++ b/tests/tests2/Makefile @@ -15,9 +15,6 @@ endif ifeq (,$(filter i386,$(ARCH))) SKIP += 98_al_ax_extend.test 99_fastcall.test endif -ifeq (,$(filter x86_64 riscv64 arm64 i386,$(ARCH))) - SKIP += 146_tls_extern.test # TLS only implemented on these architectures -endif ifeq (,$(filter i386 x86_64,$(ARCH))) SKIP += 85_asm-outside-function.test # x86 asm SKIP += 127_asm_goto.test # hardcodes x86 asm @@ -37,15 +34,12 @@ ifeq ($(CONFIG_bcheck),no) SKIP += 132_bound_test.test endif ifeq ($(CONFIG_dll),no) - SKIP += 113_btdll.test 147_def_library.test # no shared lib support yet + SKIP += 113_btdll.test # no shared lib support yet SKIP += 149_end_copy_reloc.test 151_dso_linker_symbol.test endif ifeq (-$(findstring gcc,$(CC))-,--) SKIP += $(patsubst %.expect,%.test,$(GEN-ALWAYS)) endif -ifeq ($(CC_NAME),tcc) - SKIP += 146_tls_extern.test 150_linker_boundaries.test # needs external tools -endif ifeq (-$(CONFIG_WIN32)-$(CONFIG_i386)$(CONFIG_arm)-,--yes-) SKIP += 95_bitfields%.test # type_align is different on 32bit-non-windows endif @@ -53,20 +47,15 @@ ifeq (-$(CONFIG_WIN32)-,-yes-) SKIP += 106_versym.test # No pthread support SKIP += 114_bound_signal.test # No pthread support SKIP += 117_builtins.test # win32 port doesn't define __builtins - SKIP += 124_atomic_counter.test # No pthread support - SKIP += 144_tls.test 146_tls_extern.test # TLS runtime not supported SKIP += 148_linker_symbols.test 149_end_copy_reloc.test # ELF linker tests SKIP += 150_linker_boundaries.test 151_dso_linker_symbol.test endif -ifneq (,$(filter OpenBSD FreeBSD NetBSD,$(TARGETOS))) +ifneq (,$(filter OpenBSD FreeBSD NetBSD DragonFly,$(TARGETOS))) SKIP += 106_versym.test # no pthread_condattr_setpshared SKIP += 114_bound_signal.test # libc problem signal/fork SKIP += 116_bound_setjmp2.test # No TLS_FUNC/TLS_VAR in bcheck.c SKIP += 144_tls.test 146_tls_extern.test # No tls support endif -ifeq ($(TARGETOS),DragonFly) - SKIP += 144_tls.test 146_tls_extern.test # TLS runtime not supported -endif ifeq ($(CONFIG_OSX),yes) SKIP += 144_tls.test 146_tls_extern.test # TLS runtime not supported on Mach-O SKIP += 148_linker_symbols.test 149_end_copy_reloc.test # ELF linker tests @@ -89,9 +78,6 @@ endif ifneq (-$(ARCH)-$(CONFIG_WIN32)-,-arm64-yes-) SKIP += 145_winarm64_interlocked.test endif -ifneq ($(CONFIG_WIN32),yes) - SKIP += 147_def_library.test -endif # Some tests might need arguments ARGS = @@ -141,7 +127,6 @@ GEN-ALWAYS = # TLS needs executable and pthread, not -run 144_tls.test: FLAGS += -pthread 144_tls.test: NORUN = true - # Verify that undefined TLS symbols in TCC objects work with the system linker. 146_tls_extern.test: NORUN = true 146_tls_extern.test: T1 = \ @@ -150,6 +135,9 @@ GEN-ALWAYS = $(CC) -no-pie -Wl,-z,noexecstack 146_tls_extern-main.o \ 146_tls_extern-def.o -o $(basename $@).exe && \ ./$(basename $@).exe +ifeq (-$(CONFIG_WIN32)-,-yes-) +146_tls_extern.test : CC=$(TCC) +endif # Check optional ELF linker symbols, including references from an archive. 148_linker_symbols.test: NORUN = true @@ -204,13 +192,6 @@ GEN-ALWAYS = $(TCC) -bt $1 a1$(DLLSUF) a2$(DLLSUF) -Wl,-rpath=. -o $(basename $@).exe && \ ./$(basename $@).exe -# Verify quoted LIBRARY names and the default .dll extension in DEF files. -147_def_library.test: T1 = \ - cp $(SRC)/147_def_library.def.in 147_def_library.def && \ - $(TCC) -D DEF_LIBRARY_DLL -shared $1 -o "def library test.dll" && \ - $(TCC) $1 147_def_library.def -o $(basename $@).exe && \ - grep -a -q "def library test.dll" $(basename $@).exe && \ - ./$(basename $@).exe 114_bound_signal.test: FLAGS += -b 114_bound_signal.test: NORUN = true # tcc -run does not support fork and -b and SELINUX diff --git a/tests/tests2/threads.h b/tests/tests2/threads.h new file mode 100644 index 00000000..33b7aa2b --- /dev/null +++ b/tests/tests2/threads.h @@ -0,0 +1,22 @@ +#ifdef _WIN32 +#include +typedef HANDLE pthread_t; +int pthread_create(pthread_t *p, void *x, void *fn, void *args) +{ + DWORD tid; + *p = CreateThread(NULL, 0, fn, args, 0, &tid); + return *p ? 0 : -1; +} +int pthread_join(pthread_t h, void **ret) +{ + DWORD r; + WaitForSingleObject(h, INFINITE); + GetExitCodeThread(h, &r); + CloseHandle(h); + if (ret) + *ret = (void*)(size_t)r; + return 0; +} +#else +#include +#endif diff --git a/x86_64-gen.c b/x86_64-gen.c index 8509a3ae..ffa4cbc8 100644 --- a/x86_64-gen.c +++ b/x86_64-gen.c @@ -221,8 +221,11 @@ static void orex(int ll, int r, int r2, int b) r = 0; if ((r2 & VT_VALMASK) >= VT_CONST) r2 = 0; - if (ll || REX_BASE(r) || REX_BASE(r2)) + if (ll || REX_BASE(r) || REX_BASE(r2)) { + if ((b & 0xff) == 0x66) /* output prefix before rex byte */ + o(0x66), b >>= 8; o(0x40 | REX_BASE(r) | (REX_BASE(r2) << 2) | (ll << 3)); + } o(b); } @@ -301,6 +304,26 @@ static void gen_modrm_impl(int opcode, int ll, int op_reg_0, int r, Sym *sym, in { int op_reg = REG_VALUE(op_reg_0) << 3; + if ((r & VT_SYM) && (sym->type.t & VT_TLS)) { +#ifdef TCC_TARGET_PE + Sym *s2 = external_global_sym(TOK___tls_index, &int_type); + r = get_reg(RC_INT); + gen_modrm_impl(0x8B, 0, r, VT_SYM|VT_CONST, s2, 0); + o(0x03e0c148 | r << 16); /* shl 2,r */ + o(0x4865), oad(0x250403 | r << 11, 11*PTR_SIZE); /* add gs:0x58,r */ + gen_modrm_impl(0x8B, 1, r, r | VT_LVAL, 0, 0); /* mov (r),r */ + orex(ll, r, op_reg_0, opcode), oad(0x80 | op_reg | r, 0); + greloca(cur_text_section, sym, ind - 4, R_X86_64_TPOFF32, c); +#else + o(0x64); /* fs segment prefix */ + orex(ll, r, op_reg_0, opcode); + o(0x04 | op_reg); /* [sib] | destreg */ + oad(0x25, 0); /* disp32 (relocated) */ + greloca(cur_text_section, sym, ind - 4, R_X86_64_TPOFF32, c); +#endif + return; + } + orex(ll, r, op_reg_0, opcode); if ((r & VT_VALMASK) == VT_CONST) { @@ -309,10 +332,6 @@ static void gen_modrm_impl(int opcode, int ll, int op_reg_0, int r, Sym *sym, in /* Absolute memory reference */ o(0x04 | op_reg); /* [sib] | destreg */ oad(0x25, c); /* disp32 */ - } else if (sym->type.t & VT_TLS) { - o(0x04 | op_reg); /* [sib] | destreg */ - oad(0x25, 0); /* disp32 (relocated) */ - greloca(cur_text_section, sym, ind - 4, R_X86_64_TPOFF32, c); } else { o(0x05 | op_reg); /* (%rip)+disp32 | destreg */ if (op_reg_0 & TREG_MEM) { @@ -454,16 +473,12 @@ void load(int r, SValue *sv) ll = is64_type(ft); b = 0x8b; } - if ((sv->r & VT_SYM) && (sv->sym->type.t & VT_TLS)) - o(0x64); /* fs segment prefix */ gen_modrm_impl(b, ll, r, fr, sv->sym, fc); } else { if (v == VT_CONST) { if (fr & VT_SYM) { #ifdef TCC_TARGET_PE - orex(1,0,r,0x8d); - o(0x05 + REG_VALUE(r) * 8); /* lea xx(%rip), r */ - gen_addrpc32(fr, sv->sym, fc); + gen_modrm64(0x8d, r, fr, sv->sym, fc); #else if (sv->sym->type.t & VT_TLS) { /* mov fs:0, r */ @@ -563,7 +578,7 @@ void store(int r, SValue *v) { int fr, bt, fc; /* store the REX prefix in this variable when PIC is enabled */ - int prefix = 0, opc = 0, seg = 0, ll = 0; + int opc = 0, ll = 0; fr = v->r; fc = v->c.i; @@ -584,12 +599,10 @@ void store(int r, SValue *v) /* XXX: incorrect if float reg to reg */ if (bt == VT_FLOAT) { - prefix = 0x66; - opc = 0x7e0f; + opc = 0x7e0f66; r = REG_VALUE(r); } else if (bt == VT_DOUBLE) { - prefix = 0x66; - opc = 0xd60f; + opc = 0xd60f66; r = REG_VALUE(r); } else if (bt == VT_LDOUBLE) { o(0xc0d9); /* fld %st(0) */ @@ -600,15 +613,11 @@ void store(int r, SValue *v) } else { opc = 0x89; if (bt == VT_SHORT) - prefix = 0x66; + opc = 0x8966; else if (is64_type(bt)) ll = 1; } - - if ((fr & VT_SYM) && (v->sym->type.t & VT_TLS)) - seg = 0x64; /* fs segment prefix */ - - o(seg), o(prefix), gen_modrm_impl(opc, ll, r, fr, v->sym, fc); + gen_modrm_impl(opc, ll, r, fr, v->sym, fc); } /* 'is_jmp' is '1' if it is a jump */ @@ -1932,15 +1941,12 @@ void gen_opf(int op) } assert(!(vtop[-1].r & VT_LVAL)); - if ((vtop->r & VT_SYM) && (vtop->sym->type.t & VT_TLS)) - o(0x64); /* fs segment prefix */ - - if ((vtop->type.t & VT_BTYPE) == VT_DOUBLE) - o(0x66); if (op == TOK_EQ || op == TOK_NE) opc = 0x2e0f; /* ucomisd */ else opc = 0x2f0f; /* comisd */ + if ((vtop->type.t & VT_BTYPE) == VT_DOUBLE) + opc = opc << 8 | 0x66; gen_modrm32(opc, vtop[-1].r, vtop->r, vtop->sym, fc); vtop--;