Add ELF TLS support in new x64 backend.

This follows the implementation in the legacy x86 backend, including
hardcoded sequence that is compatible with what the linker expects. We
could potentially do better here, but it is likely not necessary.

Thanks to @bjorn3 for a bugfix to an earlier version of this.
This commit is contained in:
Chris Fallin
2020-12-13 18:05:38 -08:00
parent 8640025d8b
commit 0f563f786a
5 changed files with 100 additions and 2 deletions

View File

@@ -1,5 +1,6 @@
use crate::binemit::{Addend, Reloc}; use crate::binemit::{Addend, Reloc};
use crate::ir::immediates::{Ieee32, Ieee64}; use crate::ir::immediates::{Ieee32, Ieee64};
use crate::ir::LibCall;
use crate::ir::TrapCode; use crate::ir::TrapCode;
use crate::isa::x64::inst::args::*; use crate::isa::x64::inst::args::*;
use crate::isa::x64::inst::*; use crate::isa::x64::inst::*;
@@ -2988,6 +2989,33 @@ pub(crate) fn emit(
Inst::EpiloguePlaceholder => { Inst::EpiloguePlaceholder => {
// Generate no code. // Generate no code.
} }
Inst::ElfTlsGetAddr { ref symbol } => {
// N.B.: Must be exactly this byte sequence; the linker requires it,
// because it must know how to rewrite the bytes.
// data16 lea gv@tlsgd(%rip),%rdi
sink.put1(0x66); // data16
sink.put1(0b01001000); // REX.W
sink.put1(0x8d); // LEA
sink.put1(0x3d); // ModRM byte
emit_reloc(sink, state, Reloc::ElfX86_64TlsGd, symbol, -4);
sink.put4(0); // offset
// data16 data16 callq __tls_get_addr-4
sink.put1(0x66); // data16
sink.put1(0x66); // data16
sink.put1(0b01001000); // REX.W
sink.put1(0xe8); // CALL
emit_reloc(
sink,
state,
Reloc::X86CallPLTRel4,
&ExternalName::LibCall(LibCall::ElfTlsGetAddr),
-4,
);
sink.put4(0); // offset
}
} }
state.clear_post_insn(); state.clear_post_insn();

View File

@@ -3904,6 +3904,17 @@ fn test_x64_emit() {
let trap_code = TrapCode::UnreachableCodeReached; let trap_code = TrapCode::UnreachableCodeReached;
insns.push((Inst::Ud2 { trap_code }, "0F0B", "ud2 unreachable")); insns.push((Inst::Ud2 { trap_code }, "0F0B", "ud2 unreachable"));
insns.push((
Inst::ElfTlsGetAddr {
symbol: ExternalName::User {
namespace: 0,
index: 0,
},
},
"66488D3D00000000666648E800000000",
"elf_tls_get_addr User { namespace: 0, index: 0 }",
));
// ======================================================== // ========================================================
// Actually run the tests! // Actually run the tests!
let mut flag_builder = settings::builder(); let mut flag_builder = settings::builder();

View File

@@ -2,7 +2,9 @@
use crate::binemit::{CodeOffset, StackMap}; use crate::binemit::{CodeOffset, StackMap};
use crate::ir::{types, ExternalName, Opcode, SourceLoc, TrapCode, Type}; use crate::ir::{types, ExternalName, Opcode, SourceLoc, TrapCode, Type};
use crate::isa::x64::abi::X64ABIMachineSpec;
use crate::isa::x64::settings as x64_settings; use crate::isa::x64::settings as x64_settings;
use crate::isa::CallConv;
use crate::machinst::*; use crate::machinst::*;
use crate::{settings, settings::Flags, CodegenError, CodegenResult}; use crate::{settings, settings::Flags, CodegenError, CodegenResult};
use alloc::boxed::Box; use alloc::boxed::Box;
@@ -474,6 +476,10 @@ pub enum Inst {
/// reports its own `def`s/`use`s/`mod`s; this adds complexity (the instruction list is no /// reports its own `def`s/`use`s/`mod`s; this adds complexity (the instruction list is no
/// longer flat) and requires knowledge about semantics and initial-value independence anyway. /// longer flat) and requires knowledge about semantics and initial-value independence anyway.
XmmUninitializedValue { dst: Writable<Reg> }, XmmUninitializedValue { dst: Writable<Reg> },
/// A call to the `ElfTlsGetAddr` libcall. Returns address
/// of TLS symbol in rax.
ElfTlsGetAddr { symbol: ExternalName },
} }
pub(crate) fn low32_will_sign_extend_to_64(x: u64) -> bool { pub(crate) fn low32_will_sign_extend_to_64(x: u64) -> bool {
@@ -532,7 +538,8 @@ impl Inst {
| Inst::XmmCmpRmR { .. } | Inst::XmmCmpRmR { .. }
| Inst::XmmLoadConst { .. } | Inst::XmmLoadConst { .. }
| Inst::XmmMinMaxSeq { .. } | Inst::XmmMinMaxSeq { .. }
| Inst::XmmUninitializedValue { .. } => None, | Inst::XmmUninitializedValue { .. }
| Inst::ElfTlsGetAddr { .. } => None,
// These use dynamic SSE opcodes. // These use dynamic SSE opcodes.
Inst::GprToXmm { op, .. } Inst::GprToXmm { op, .. }
@@ -1780,6 +1787,10 @@ impl PrettyPrint for Inst {
Inst::Hlt => "hlt".into(), Inst::Hlt => "hlt".into(),
Inst::Ud2 { trap_code } => format!("ud2 {}", trap_code), Inst::Ud2 { trap_code } => format!("ud2 {}", trap_code),
Inst::ElfTlsGetAddr { ref symbol } => {
format!("elf_tls_get_addr {:?}", symbol)
}
} }
} }
} }
@@ -2039,6 +2050,18 @@ fn x64_get_regs(inst: &Inst, collector: &mut RegUsageCollector) {
| Inst::Fence { .. } => { | Inst::Fence { .. } => {
// No registers are used. // No registers are used.
} }
Inst::ElfTlsGetAddr { .. } => {
// All caller-saves are clobbered.
//
// We use the SysV calling convention here because the
// pseudoinstruction (and relocation that it emits) is specific to
// ELF systems; other x86-64 targets with other conventions (i.e.,
// Windows) use different TLS strategies.
for reg in X64ABIMachineSpec::get_regs_clobbered_by_call(CallConv::SystemV) {
collector.add_def(reg);
}
}
} }
} }
@@ -2425,6 +2448,7 @@ fn x64_map_regs<RUM: RegUsageMapper>(inst: &mut Inst, mapper: &RUM) {
| Inst::Ud2 { .. } | Inst::Ud2 { .. }
| Inst::Hlt | Inst::Hlt
| Inst::AtomicRmwSeq { .. } | Inst::AtomicRmwSeq { .. }
| Inst::ElfTlsGetAddr { .. }
| Inst::Fence { .. } => { | Inst::Fence { .. } => {
// Instruction doesn't explicitly mention any regs, so it can't have any virtual // Instruction doesn't explicitly mention any regs, so it can't have any virtual
// regs that we'd need to remap. Hence no action required. // regs that we'd need to remap. Hence no action required.

View File

@@ -12,7 +12,7 @@ use crate::isa::{x64::X64Backend, CallConv};
use crate::machinst::lower::*; use crate::machinst::lower::*;
use crate::machinst::*; use crate::machinst::*;
use crate::result::CodegenResult; use crate::result::CodegenResult;
use crate::settings::Flags; use crate::settings::{Flags, TlsModel};
use alloc::boxed::Box; use alloc::boxed::Box;
use alloc::vec::Vec; use alloc::vec::Vec;
use cranelift_codegen_shared::condcodes::CondCode; use cranelift_codegen_shared::condcodes::CondCode;
@@ -5324,6 +5324,22 @@ fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
ctx.emit(Inst::gen_move(dst_hi, src.regs()[1], types::I64)); ctx.emit(Inst::gen_move(dst_hi, src.regs()[1], types::I64));
} }
Opcode::TlsValue => match flags.tls_model() {
TlsModel::ElfGd => {
let dst = get_output_reg(ctx, outputs[0]).only_reg().unwrap();
let (name, _, _) = ctx.symbol_value(insn).unwrap();
let symbol = name.clone();
ctx.emit(Inst::ElfTlsGetAddr { symbol });
ctx.emit(Inst::gen_move(dst, regs::rax(), types::I64));
}
_ => {
todo!(
"Unimplemented TLS model in x64 backend: {:?}",
flags.tls_model()
);
}
},
Opcode::IaddImm Opcode::IaddImm
| Opcode::ImulImm | Opcode::ImulImm
| Opcode::UdivImm | Opcode::UdivImm

View File

@@ -0,0 +1,19 @@
test compile
set tls_model=elf_gd
target x86_64
feature "experimental_x64"
function u0:0(i32) -> i64 {
gv0 = symbol colocated tls u1:0
block0(v0: i32):
v1 = global_value.i64 gv0
return v1
}
; check: pushq %rbp
; nextln: movq %rsp, %rbp
; nextln: elf_tls_get_addr User { namespace: 1, index: 0 }
; nextln: movq %rbp, %rsp
; nextln: popq %rbp
; nextln: ret