feat(arm64): add TLS Local Exec code generation and linker relocations

This commit is contained in:
Meng Zhuo 2026-05-12 20:02:08 +08:00
parent 11f5c6e1f9
commit 8abaf10ab5
3 changed files with 75 additions and 1 deletions

View File

@ -552,6 +552,23 @@ ST_FUNC void load(int r, SValue *sv)
} }
if (svr == (VT_CONST | VT_LVAL | VT_SYM)) { if (svr == (VT_CONST | VT_LVAL | VT_SYM)) {
if (sv->sym->type.t & VT_TLS) {
o(0xd53bd05e); /* mrs x30, tpidr_el0 */
greloca(cur_text_section, sv->sym, ind,
R_AARCH64_TLSLE_ADD_TPREL_HI12, 0);
o(ARM64_ADD_IMM | ARM64_SF(1) | ARM64_SH(1) |
ARM64_RN(30) | ARM64_RD(30)); /* add x30, x30, #0, lsl #12 */
greloca(cur_text_section, sv->sym, ind,
R_AARCH64_TLSLE_ADD_TPREL_LO12, 0);
o(ARM64_ADD_IMM | ARM64_SF(1) |
ARM64_RN(30) | ARM64_RD(30)); /* add x30, x30, #0 */
if (IS_FREG(r))
arm64_ldrv(arm64_type_size(svtt), fltr(r), 30, svcoff);
else
arm64_ldrx(!(svtt&VT_UNSIGNED), arm64_type_size(svtt),
intr(r), 30, svcoff);
return;
}
arm64_sym(30, sv->sym, // use x30 for address arm64_sym(30, sv->sym, // use x30 for address
arm64_check_offset(0, arm64_type_size(svtt), svcoff)); arm64_check_offset(0, arm64_type_size(svtt), svcoff));
if (IS_FREG(r)) if (IS_FREG(r))
@ -645,6 +662,22 @@ ST_FUNC void store(int r, SValue *sv)
if (svr == (VT_CONST | VT_LVAL)) { if (svr == (VT_CONST | VT_LVAL)) {
uint64_t i = sv->c.i; uint64_t i = sv->c.i;
if (sv->sym && (sv->sym->type.t & VT_TLS)) {
o(0xd53bd05e);
greloca(cur_text_section, sv->sym, ind,
R_AARCH64_TLSLE_ADD_TPREL_HI12, 0);
o(ARM64_ADD_IMM | ARM64_SF(1) | ARM64_SH(1) |
ARM64_RN(30) | ARM64_RD(30));
greloca(cur_text_section, sv->sym, ind,
R_AARCH64_TLSLE_ADD_TPREL_LO12, 0);
o(ARM64_ADD_IMM | ARM64_SF(1) |
ARM64_RN(30) | ARM64_RD(30));
if (IS_FREG(r))
arm64_strv(arm64_type_size(svtt), fltr(r), 30, i);
else
arm64_strx(arm64_type_size(svtt), intr(r), 30, i);
return;
}
if (sv->sym) if (sv->sym)
arm64_sym(30, sv->sym, // use x30 for address arm64_sym(30, sv->sym, // use x30 for address
arm64_check_offset(0, arm64_type_size(svtt), i)); arm64_check_offset(0, arm64_type_size(svtt), i));
@ -668,6 +701,22 @@ ST_FUNC void store(int r, SValue *sv)
} }
if (svr == (VT_CONST | VT_LVAL | VT_SYM)) { if (svr == (VT_CONST | VT_LVAL | VT_SYM)) {
if (sv->sym->type.t & VT_TLS) {
o(0xd53bd05e); /* mrs x30, tpidr_el0 */
greloca(cur_text_section, sv->sym, ind,
R_AARCH64_TLSLE_ADD_TPREL_HI12, 0);
o(ARM64_ADD_IMM | ARM64_SF(1) | ARM64_SH(1) |
ARM64_RN(30) | ARM64_RD(30)); /* add x30, x30, #0, lsl #12 */
greloca(cur_text_section, sv->sym, ind,
R_AARCH64_TLSLE_ADD_TPREL_LO12, 0);
o(ARM64_ADD_IMM | ARM64_SF(1) |
ARM64_RN(30) | ARM64_RD(30)); /* add x30, x30, #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_sym(30, sv->sym, // use x30 for address
arm64_check_offset(0, arm64_type_size(svtt), svcoff)); arm64_check_offset(0, arm64_type_size(svtt), svcoff));
if (IS_FREG(r)) if (IS_FREG(r))

View File

@ -43,6 +43,8 @@ ST_FUNC int code_reloc (int reloc_type)
case R_AARCH64_LDST32_ABS_LO12_NC: case R_AARCH64_LDST32_ABS_LO12_NC:
case R_AARCH64_LDST16_ABS_LO12_NC: case R_AARCH64_LDST16_ABS_LO12_NC:
case R_AARCH64_LDST8_ABS_LO12_NC: case R_AARCH64_LDST8_ABS_LO12_NC:
case R_AARCH64_TLSLE_ADD_TPREL_HI12:
case R_AARCH64_TLSLE_ADD_TPREL_LO12:
case R_AARCH64_GLOB_DAT: case R_AARCH64_GLOB_DAT:
case R_AARCH64_COPY: case R_AARCH64_COPY:
return 0; return 0;
@ -80,6 +82,8 @@ ST_FUNC int gotplt_entry_type (int reloc_type)
case R_AARCH64_COPY: case R_AARCH64_COPY:
case R_AARCH64_CONDBR19: case R_AARCH64_CONDBR19:
case R_AARCH64_TSTBR14: case R_AARCH64_TSTBR14:
case R_AARCH64_TLSLE_ADD_TPREL_HI12:
case R_AARCH64_TLSLE_ADD_TPREL_LO12:
return NO_GOTPLT_ENTRY; return NO_GOTPLT_ENTRY;
case R_AARCH64_ABS32: case R_AARCH64_ABS32:
@ -365,6 +369,27 @@ ST_FUNC void relocate(TCCState *s1, ElfW_Rel *rel, int type, unsigned char *ptr,
#endif #endif
write64le(ptr, val - rel->r_addend); write64le(ptr, val - rel->r_addend);
return; return;
case R_AARCH64_TLSLE_ADD_TPREL_HI12:
case R_AARCH64_TLSLE_ADD_TPREL_LO12: {
addr_t tls_start = 0;
int i;
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;
}
}
/* glibc arm64: tp points to tcbhead_t (DTV), TLS data starts after it */
int64_t tp_offset = val - tls_start + 16;
int64_t imm;
if (type == R_AARCH64_TLSLE_ADD_TPREL_HI12)
imm = (tp_offset >> 12) & 0xfff;
else
imm = tp_offset & 0xfff;
write32le(ptr, ((read32le(ptr) & 0xffc003ff) | (imm << 10)));
return;
}
case R_AARCH64_RELATIVE: case R_AARCH64_RELATIVE:
#ifdef TCC_TARGET_PE #ifdef TCC_TARGET_PE
add32le(ptr, val - s1->pe_imagebase); add32le(ptr, val - s1->pe_imagebase);

View File

@ -19,7 +19,7 @@ ifeq (,$(filter i386 x86_64,$(ARCH)))
SKIP += 85_asm-outside-function.test # x86 asm SKIP += 85_asm-outside-function.test # x86 asm
SKIP += 127_asm_goto.test # hardcodes x86 asm SKIP += 127_asm_goto.test # hardcodes x86 asm
endif endif
ifeq (,$(filter x86_64 riscv64,$(ARCH))) ifeq (,$(filter x86_64 riscv64 arm64,$(ARCH)))
SKIP += 144_tls.test # TLS only implemented on x86_64 so far SKIP += 144_tls.test # TLS only implemented on x86_64 so far
endif endif
ifeq ($(CONFIG_backtrace),no) ifeq ($(CONFIG_backtrace),no)