Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

aarch64: Implement TLSDESC for TLS GD accesses #7201

Merged
merged 2 commits into from
Oct 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 18 additions & 10 deletions cranelift/codegen/src/binemit/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,15 +64,21 @@ pub enum Reloc {
/// Offset within page of TLVP slot.
MachOAarch64TlsAdrPageOff12,

/// Aarch64 TLS GD
/// Set an ADRP immediate field to the top 21 bits of the final address. Checks for overflow.
/// This is equivalent to `R_AARCH64_TLSGD_ADR_PAGE21` in the [aaelf64](https://github.com/ARM-software/abi-aa/blob/2bcab1e3b22d55170c563c3c7940134089176746/aaelf64/aaelf64.rst#relocations-for-thread-local-storage)
Aarch64TlsGdAdrPage21,
/// Aarch64 TLSDESC Adr Page21
/// This is equivalent to `R_AARCH64_TLSDESC_ADR_PAGE21` in the [aaelf64](https://github.com/ARM-software/abi-aa/blob/2bcab1e3b22d55170c563c3c7940134089176746/aaelf64/aaelf64.rst#57105thread-local-storage-descriptors)
Aarch64TlsDescAdrPage21,

/// Aarch64 TLS GD
/// Set the add immediate field to the low 12 bits of the final address. Does not check for overflow.
/// This is equivalent to `R_AARCH64_TLSGD_ADD_LO12_NC` in the [aaelf64](https://github.com/ARM-software/abi-aa/blob/2bcab1e3b22d55170c563c3c7940134089176746/aaelf64/aaelf64.rst#relocations-for-thread-local-storage)
Aarch64TlsGdAddLo12Nc,
/// Aarch64 TLSDESC Ld64 Lo12
/// This is equivalent to `R_AARCH64_TLSDESC_LD64_LO12` in the [aaelf64](https://github.com/ARM-software/abi-aa/blob/2bcab1e3b22d55170c563c3c7940134089176746/aaelf64/aaelf64.rst#57105thread-local-storage-descriptors)
Aarch64TlsDescLd64Lo12,

/// Aarch64 TLSDESC Add Lo12
/// This is equivalent to `R_AARCH64_TLSGD_ADD_LO12` in the [aaelf64](https://github.com/ARM-software/abi-aa/blob/2bcab1e3b22d55170c563c3c7940134089176746/aaelf64/aaelf64.rst#57105thread-local-storage-descriptors)
Aarch64TlsDescAddLo12,

/// Aarch64 TLSDESC Call
/// This is equivalent to `R_AARCH64_TLSDESC_CALL` in the [aaelf64](https://github.com/ARM-software/abi-aa/blob/2bcab1e3b22d55170c563c3c7940134089176746/aaelf64/aaelf64.rst#57105thread-local-storage-descriptors)
Aarch64TlsDescCall,

/// AArch64 GOT Page
/// Set the immediate value of an ADRP to bits 32:12 of X; check that –232 <= X < 232
Expand Down Expand Up @@ -141,8 +147,10 @@ impl fmt::Display for Reloc {
Self::MachOX86_64Tlv => write!(f, "MachOX86_64Tlv"),
Self::MachOAarch64TlsAdrPage21 => write!(f, "MachOAarch64TlsAdrPage21"),
Self::MachOAarch64TlsAdrPageOff12 => write!(f, "MachOAarch64TlsAdrPageOff12"),
Self::Aarch64TlsGdAdrPage21 => write!(f, "Aarch64TlsGdAdrPage21"),
Self::Aarch64TlsGdAddLo12Nc => write!(f, "Aarch64TlsGdAddLo12Nc"),
Self::Aarch64TlsDescAdrPage21 => write!(f, "Aarch64TlsDescAdrPage21"),
Self::Aarch64TlsDescLd64Lo12 => write!(f, "Aarch64TlsDescLd64Lo12"),
Self::Aarch64TlsDescAddLo12 => write!(f, "Aarch64TlsDescAddLo12"),
Self::Aarch64TlsDescCall => write!(f, "Aarch64TlsDescCall"),
Self::Aarch64AdrGotPage21 => write!(f, "Aarch64AdrGotPage21"),
Self::Aarch64Ld64GotLo12Nc => write!(f, "Aarch64AdrGotLo12Nc"),
Self::S390xTlsGd64 => write!(f, "TlsGd64"),
Expand Down
8 changes: 5 additions & 3 deletions cranelift/codegen/src/isa/aarch64/inst.isle
Original file line number Diff line number Diff line change
Expand Up @@ -947,8 +947,9 @@

;; A call to the `ElfTlsGetAddr` libcall. Returns address of TLS symbol in x0.
(ElfTlsGetAddr
(symbol ExternalName)
(rd WritableReg))
(symbol BoxExternalName)
(rd WritableReg)
(tmp WritableReg))

(MachOTlsGetAddr
(symbol ExternalName)
Expand Down Expand Up @@ -3761,7 +3762,8 @@
(decl elf_tls_get_addr (ExternalName) Reg)
(rule (elf_tls_get_addr name)
(let ((dst WritableReg (temp_writable_reg $I64))
(_ Unit (emit (MInst.ElfTlsGetAddr name dst))))
(tmp WritableReg (temp_writable_reg $I64))
(_ Unit (emit (MInst.ElfTlsGetAddr (box_external_name name) dst tmp))))
dst))

(decl macho_tls_get_addr (ExternalName) Reg)
Expand Down
87 changes: 68 additions & 19 deletions cranelift/codegen/src/isa/aarch64/inst/emit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use cranelift_control::ControlPlane;
use regalloc2::Allocation;

use crate::binemit::{Reloc, StackMap};
use crate::ir::{self, types::*, LibCall, MemFlags, RelSourceLoc, TrapCode};
use crate::ir::{self, types::*, MemFlags, RelSourceLoc, TrapCode};
use crate::isa::aarch64::inst::*;
use crate::machinst::{ty_bits, Reg, RegClass, Writable};
use crate::trace;
Expand Down Expand Up @@ -3537,32 +3537,81 @@ impl MachInstEmit for Inst {
}
}

&Inst::ElfTlsGetAddr { ref symbol, rd } => {
&Inst::ElfTlsGetAddr {
ref symbol,
rd,
tmp,
} => {
let rd = allocs.next_writable(rd);
let tmp = allocs.next_writable(tmp);
assert_eq!(xreg(0), rd.to_reg());

// See the original proposal for TLSDESC.
// http://www.fsfla.org/~lxoliva/writeups/TLS/paper-lk2006.pdf
//
// Implement the TLSDESC instruction sequence:
// adrp x0, :tlsdesc:tlsvar
// ldr tmp, [x0, :tlsdesc_lo12:tlsvar]
// add x0, x0, :tlsdesc_lo12:tlsvar
// blr tmp
// mrs tmp, tpidr_el0
// add x0, x0, tmp
//
// This is the instruction sequence that GCC emits for ELF GD TLS Relocations in aarch64
// See: https://gcc.godbolt.org/z/KhMh5Gvra
// See: https://gcc.godbolt.org/z/e4j7MdErh

// adrp x0, <label>
sink.add_reloc(Reloc::Aarch64TlsGdAdrPage21, symbol, 0);
let inst = Inst::Adrp { rd, off: 0 };
inst.emit(&[], sink, emit_info, state);
// adrp x0, :tlsdesc:tlsvar
sink.add_reloc(Reloc::Aarch64TlsDescAdrPage21, &**symbol, 0);
Inst::Adrp { rd, off: 0 }.emit(&[], sink, emit_info, state);

// add x0, x0, <label>
sink.add_reloc(Reloc::Aarch64TlsGdAddLo12Nc, symbol, 0);
sink.put4(0x91000000);
// ldr tmp, [x0, :tlsdesc_lo12:tlsvar]
sink.add_reloc(Reloc::Aarch64TlsDescLd64Lo12, &**symbol, 0);
Inst::ULoad64 {
rd: tmp,
mem: AMode::reg(rd.to_reg()),
flags: MemFlags::trusted(),
}
.emit(&[], sink, emit_info, state);

// bl __tls_get_addr
sink.add_reloc(
Reloc::Arm64Call,
&ExternalName::LibCall(LibCall::ElfTlsGetAddr),
0,
);
sink.put4(0x94000000);
// add x0, x0, :tlsdesc_lo12:tlsvar
sink.add_reloc(Reloc::Aarch64TlsDescAddLo12, &**symbol, 0);
Inst::AluRRImm12 {
alu_op: ALUOp::Add,
size: OperandSize::Size64,
rd,
rn: rd.to_reg(),
imm12: Imm12::maybe_from_u64(0).unwrap(),
}
.emit(&[], sink, emit_info, state);

// nop
sink.put4(0xd503201f);
// blr tmp
sink.add_reloc(Reloc::Aarch64TlsDescCall, &**symbol, 0);
Inst::CallInd {
info: crate::isa::Box::new(CallIndInfo {
rn: tmp.to_reg(),
uses: smallvec![],
defs: smallvec![],
clobbers: PRegSet::empty(),
opcode: Opcode::CallIndirect,
caller_callconv: CallConv::SystemV,
callee_callconv: CallConv::SystemV,
callee_pop_size: 0,
}),
}
.emit(&[], sink, emit_info, state);

// mrs tmp, tpidr_el0
sink.put4(0xd53bd040 | machreg_to_gpr(tmp.to_reg()));

// add x0, x0, tmp
Inst::AluRRR {
alu_op: ALUOp::Add,
size: OperandSize::Size64,
rd,
rn: rd.to_reg(),
rm: tmp.to_reg(),
}
.emit(&[], sink, emit_info, state);
}

&Inst::MachOTlsGetAddr { ref symbol, rd } => {
Expand Down
21 changes: 15 additions & 6 deletions cranelift/codegen/src/isa/aarch64/inst/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -939,11 +939,15 @@ fn aarch64_get_operands<F: Fn(VReg) -> VReg>(inst: &Inst, collector: &mut Operan
&Inst::Bti { .. } => {}
&Inst::VirtualSPOffsetAdj { .. } => {}

&Inst::ElfTlsGetAddr { rd, .. } => {
&Inst::ElfTlsGetAddr { rd, tmp, .. } => {
// TLSDESC has a very neat calling convention. It is required to preserve
// all registers except x0 and x30. X30 is non allocatable in cranelift since
// its the link register.
//
// Additionally we need a second register as a temporary register for the
// TLSDESC sequence. This register can be any register other than x0 (and x30).
collector.reg_fixed_def(rd, regs::xreg(0));
let mut clobbers = AArch64MachineDeps::get_regs_clobbered_by_call(CallConv::SystemV);
clobbers.remove(regs::xreg_preg(0));
collector.reg_clobbers(clobbers);
collector.reg_early_def(tmp);
}
&Inst::MachOTlsGetAddr { rd, .. } => {
collector.reg_fixed_def(rd, regs::xreg(0));
Expand Down Expand Up @@ -2842,9 +2846,14 @@ impl Inst {
}
&Inst::EmitIsland { needed_space } => format!("emit_island {}", needed_space),

&Inst::ElfTlsGetAddr { ref symbol, rd } => {
&Inst::ElfTlsGetAddr {
ref symbol,
rd,
tmp,
} => {
let rd = pretty_print_reg(rd.to_reg(), allocs);
format!("elf_tls_get_addr {}, {}", rd, symbol.display(None))
let tmp = pretty_print_reg(tmp.to_reg(), allocs);
format!("elf_tls_get_addr {}, {}, {}", rd, tmp, symbol.display(None))
}
&Inst::MachOTlsGetAddr { ref symbol, rd } => {
let rd = pretty_print_reg(rd.to_reg(), allocs);
Expand Down
42 changes: 12 additions & 30 deletions cranelift/filetests/filetests/isa/aarch64/tls-elf-gd.clif
Original file line number Diff line number Diff line change
Expand Up @@ -13,46 +13,28 @@ block0(v0: i32):
; VCode:
; stp fp, lr, [sp, #-16]!
; mov fp, sp
; str x24, [sp, #-16]!
; stp d14, d15, [sp, #-16]!
; stp d12, d13, [sp, #-16]!
; stp d10, d11, [sp, #-16]!
; stp d8, d9, [sp, #-16]!
; block0:
; mov x24, x0
; elf_tls_get_addr x0, userextname0
; mov x5, x0
; elf_tls_get_addr x0, x3, userextname0
; mov x1, x0
; mov x0, x24
; ldp d8, d9, [sp], #16
; ldp d10, d11, [sp], #16
; ldp d12, d13, [sp], #16
; ldp d14, d15, [sp], #16
; ldr x24, [sp], #16
; mov x0, x5
; ldp fp, lr, [sp], #16
; ret
;
; Disassembled:
; block0: ; offset 0x0
; stp x29, x30, [sp, #-0x10]!
; mov x29, sp
; str x24, [sp, #-0x10]!
; stp d14, d15, [sp, #-0x10]!
; stp d12, d13, [sp, #-0x10]!
; stp d10, d11, [sp, #-0x10]!
; stp d8, d9, [sp, #-0x10]!
; block1: ; offset 0x1c
; mov x24, x0
; adrp x0, #0 ; reloc_external Aarch64TlsGdAdrPage21 u1:0 0
; add x0, x0, #0 ; reloc_external Aarch64TlsGdAddLo12Nc u1:0 0
; bl #0x28 ; reloc_external Call %ElfTlsGetAddr 0
; nop
; block1: ; offset 0x8
; mov x5, x0
; adrp x0, #0 ; reloc_external Aarch64TlsDescAdrPage21 u1:0 0
; ldr x3, [x0] ; reloc_external Aarch64TlsDescLd64Lo12 u1:0 0
; add x0, x0, #0 ; reloc_external Aarch64TlsDescAddLo12 u1:0 0
; blr x3 ; reloc_external Aarch64TlsDescCall u1:0 0
; mrs x3, tpidr_el0
; add x0, x0, x3
; mov x1, x0
; mov x0, x24
; ldp d8, d9, [sp], #0x10
; ldp d10, d11, [sp], #0x10
; ldp d12, d13, [sp], #0x10
; ldp d14, d15, [sp], #0x10
; ldr x24, [sp], #0x10
; mov x0, x5
; ldp x29, x30, [sp], #0x10
; ret

37 changes: 31 additions & 6 deletions cranelift/object/src/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -718,30 +718,55 @@ impl ObjectModule {
12,
)
}
Reloc::Aarch64TlsGdAdrPage21 => {
Reloc::Aarch64TlsDescAdrPage21 => {
assert_eq!(
self.object.format(),
object::BinaryFormat::Elf,
"Aarch64TlsGdAdrPrel21 is not supported for this file format"
"Aarch64TlsDescAdrPage21 is not supported for this file format"
);
(
RelocationKind::Elf(object::elf::R_AARCH64_TLSGD_ADR_PAGE21),
RelocationKind::Elf(object::elf::R_AARCH64_TLSDESC_ADR_PAGE21),
RelocationEncoding::Generic,
21,
)
}
Reloc::Aarch64TlsGdAddLo12Nc => {
Reloc::Aarch64TlsDescLd64Lo12 => {
assert_eq!(
self.object.format(),
object::BinaryFormat::Elf,
"Aarch64TlsGdAddLo12Nc is not supported for this file format"
"Aarch64TlsDescLd64Lo12 is not supported for this file format"
);
(
RelocationKind::Elf(object::elf::R_AARCH64_TLSGD_ADD_LO12_NC),
RelocationKind::Elf(object::elf::R_AARCH64_TLSDESC_LD64_LO12),
RelocationEncoding::Generic,
12,
)
}
Reloc::Aarch64TlsDescAddLo12 => {
assert_eq!(
self.object.format(),
object::BinaryFormat::Elf,
"Aarch64TlsDescAddLo12 is not supported for this file format"
);
(
RelocationKind::Elf(object::elf::R_AARCH64_TLSDESC_ADD_LO12),
RelocationEncoding::Generic,
12,
)
}
Reloc::Aarch64TlsDescCall => {
assert_eq!(
self.object.format(),
object::BinaryFormat::Elf,
"Aarch64TlsDescCall is not supported for this file format"
);
(
RelocationKind::Elf(object::elf::R_AARCH64_TLSDESC_CALL),
RelocationEncoding::Generic,
0,
)
}

Reloc::Aarch64AdrGotPage21 => match self.object.format() {
object::BinaryFormat::Elf => (
RelocationKind::Elf(object::elf::R_AARCH64_ADR_GOT_PAGE),
Expand Down