From 0a1bb3ba6ccadd4b716cd259a097bb31435c0c6d Mon Sep 17 00:00:00 2001 From: bjorn3 Date: Wed, 26 Feb 2020 02:50:04 +0100 Subject: [PATCH] Add TLS support for ELF and MachO (#1174) * Add TLS support * Add binemit and legalize tests * Spill all caller-saved registers when necessary --- .../codegen/meta/src/cdsl/instructions.rs | 2 +- .../codegen/meta/src/isa/x86/encodings.rs | 9 +++ .../codegen/meta/src/isa/x86/instructions.rs | 36 ++++++++++ .../codegen/meta/src/isa/x86/legalize.rs | 3 + cranelift/codegen/meta/src/isa/x86/mod.rs | 1 + cranelift/codegen/meta/src/isa/x86/recipes.rs | 67 ++++++++++++++++++- .../codegen/meta/src/shared/instructions.rs | 12 ++++ cranelift/codegen/meta/src/shared/mod.rs | 4 +- cranelift/codegen/meta/src/shared/settings.rs | 8 +++ cranelift/codegen/src/binemit/mod.rs | 9 +++ cranelift/codegen/src/ir/globalvalue.rs | 7 +- cranelift/codegen/src/ir/libcall.rs | 5 ++ cranelift/codegen/src/isa/x86/binemit.rs | 5 +- cranelift/codegen/src/isa/x86/enc_tables.rs | 37 ++++++++++ .../codegen/src/legalizer/globalvalue.rs | 17 ++++- cranelift/codegen/src/regalloc/spilling.rs | 6 +- cranelift/codegen/src/settings.rs | 1 + cranelift/faerie/src/backend.rs | 4 ++ .../filetests/filetests/isa/x86/tls_elf.clif | 18 +++++ .../filetests/filetests/isa/x86/tls_enc.clif | 11 +++ .../filetests/isa/x86/tls_macho.clif | 18 +++++ cranelift/module/src/backend.rs | 4 ++ cranelift/module/src/module.rs | 16 ++++- cranelift/object/Cargo.toml | 1 + cranelift/object/src/backend.rs | 64 ++++++++++++++++-- cranelift/reader/src/parser.rs | 3 + cranelift/simplejit/src/backend.rs | 5 ++ 27 files changed, 354 insertions(+), 19 deletions(-) create mode 100644 cranelift/filetests/filetests/isa/x86/tls_elf.clif create mode 100644 cranelift/filetests/filetests/isa/x86/tls_enc.clif create mode 100644 cranelift/filetests/filetests/isa/x86/tls_macho.clif diff --git a/cranelift/codegen/meta/src/cdsl/instructions.rs b/cranelift/codegen/meta/src/cdsl/instructions.rs index f17202eb1c..d8d9c81466 100644 --- a/cranelift/codegen/meta/src/cdsl/instructions.rs +++ b/cranelift/codegen/meta/src/cdsl/instructions.rs @@ -340,7 +340,7 @@ impl InstructionBuilder { let polymorphic_info = verify_polymorphic(&operands_in, &operands_out, &self.format, &value_opnums); - // Infer from output operands whether an instruciton clobbers CPU flags or not. + // Infer from output operands whether an instruction clobbers CPU flags or not. let writes_cpu_flags = operands_out.iter().any(|op| op.is_cpu_flags()); let camel_name = camel_case(&self.name); diff --git a/cranelift/codegen/meta/src/isa/x86/encodings.rs b/cranelift/codegen/meta/src/isa/x86/encodings.rs index 81e2e48c58..5dd6edc380 100644 --- a/cranelift/codegen/meta/src/isa/x86/encodings.rs +++ b/cranelift/codegen/meta/src/isa/x86/encodings.rs @@ -2407,5 +2407,14 @@ pub(crate) fn define( define_control_flow(&mut e, shared_defs, settings, r); define_reftypes(&mut e, shared_defs, r); + let x86_elf_tls_get_addr = x86.by_name("x86_elf_tls_get_addr"); + let x86_macho_tls_get_addr = x86.by_name("x86_macho_tls_get_addr"); + + let rec_elf_tls_get_addr = r.recipe("elf_tls_get_addr"); + let rec_macho_tls_get_addr = r.recipe("macho_tls_get_addr"); + + e.enc64_rec(x86_elf_tls_get_addr, rec_elf_tls_get_addr, 0); + e.enc64_rec(x86_macho_tls_get_addr, rec_macho_tls_get_addr, 0); + e } diff --git a/cranelift/codegen/meta/src/isa/x86/instructions.rs b/cranelift/codegen/meta/src/isa/x86/instructions.rs index 04dc6cfe12..3be23cea9d 100644 --- a/cranelift/codegen/meta/src/isa/x86/instructions.rs +++ b/cranelift/codegen/meta/src/isa/x86/instructions.rs @@ -7,6 +7,7 @@ use crate::cdsl::operands::Operand; use crate::cdsl::types::ValueType; use crate::cdsl::typevar::{Interval, TypeSetBuilder, TypeVar}; +use crate::shared::entities::EntityRefs; use crate::shared::formats::Formats; use crate::shared::immediates::Immediates; use crate::shared::types; @@ -16,6 +17,7 @@ pub(crate) fn define( mut all_instructions: &mut AllInstructions, formats: &Formats, immediates: &Immediates, + entities: &EntityRefs, ) -> InstructionGroup { let mut ig = InstructionGroupBuilder::new(&mut all_instructions); @@ -542,5 +544,39 @@ pub(crate) fn define( .operands_out(vec![a]), ); + let i64_t = &TypeVar::new( + "i64_t", + "A scalar 64bit integer", + TypeSetBuilder::new().ints(64..64).build(), + ); + + let GV = &Operand::new("GV", &entities.global_value); + let addr = &Operand::new("addr", i64_t); + + ig.push( + Inst::new( + "x86_elf_tls_get_addr", + r#" + Elf tls get addr -- This implements the GD TLS model for ELF. The clobber output should + not be used. + "#, + &formats.unary_global_value, + ) + .operands_in(vec![GV]) + .operands_out(vec![addr]), + ); + ig.push( + Inst::new( + "x86_macho_tls_get_addr", + r#" + Mach-O tls get addr -- This implements TLS access for Mach-O. The clobber output should + not be used. + "#, + &formats.unary_global_value, + ) + .operands_in(vec![GV]) + .operands_out(vec![addr]), + ); + ig.build() } diff --git a/cranelift/codegen/meta/src/isa/x86/legalize.rs b/cranelift/codegen/meta/src/isa/x86/legalize.rs index aa91741dba..de748a0bae 100644 --- a/cranelift/codegen/meta/src/isa/x86/legalize.rs +++ b/cranelift/codegen/meta/src/isa/x86/legalize.rs @@ -61,6 +61,7 @@ pub(crate) fn define(shared: &mut SharedDefinitions, x86_instructions: &Instruct let shuffle = insts.by_name("shuffle"); let srem = insts.by_name("srem"); let sshr = insts.by_name("sshr"); + let tls_value = insts.by_name("tls_value"); let trueif = insts.by_name("trueif"); let udiv = insts.by_name("udiv"); let umax = insts.by_name("umax"); @@ -326,6 +327,8 @@ pub(crate) fn define(shared: &mut SharedDefinitions, x86_instructions: &Instruct group.custom_legalize(ineg, "convert_ineg"); + group.custom_legalize(tls_value, "expand_tls_value"); + group.build_and_add_to(&mut shared.transform_groups); let mut narrow = TransformGroupBuilder::new( diff --git a/cranelift/codegen/meta/src/isa/x86/mod.rs b/cranelift/codegen/meta/src/isa/x86/mod.rs index 519ad884ff..3b4848b166 100644 --- a/cranelift/codegen/meta/src/isa/x86/mod.rs +++ b/cranelift/codegen/meta/src/isa/x86/mod.rs @@ -24,6 +24,7 @@ pub(crate) fn define(shared_defs: &mut SharedDefinitions) -> TargetIsa { &mut shared_defs.all_instructions, &shared_defs.formats, &shared_defs.imm, + &shared_defs.entities, ); legalize::define(shared_defs, &inst_group); diff --git a/cranelift/codegen/meta/src/isa/x86/recipes.rs b/cranelift/codegen/meta/src/isa/x86/recipes.rs index 5c7cb0519b..4bab09c306 100644 --- a/cranelift/codegen/meta/src/isa/x86/recipes.rs +++ b/cranelift/codegen/meta/src/isa/x86/recipes.rs @@ -3259,10 +3259,73 @@ pub(crate) fn define<'shared>( recipes.add_recipe( EncodingRecipeBuilder::new("safepoint", &formats.multiary, 0).emit( r#" - sink.add_stackmap(args, func, isa); - "#, + sink.add_stackmap(args, func, isa); + "#, ), ); + // Both `elf_tls_get_addr` and `macho_tls_get_addr` require all caller-saved registers to be spilled. + // This is currently special cased in `regalloc/spilling.rs` in the `visit_inst` function. + + recipes.add_recipe( + EncodingRecipeBuilder::new("elf_tls_get_addr", &formats.unary_global_value, 16) + // FIXME Correct encoding for non rax registers + .operands_out(vec![reg_rax]) + .emit( + r#" + // output %rax + // clobbers %rdi + + // Those data16 prefixes are necessary to pad to 16 bytes. + + // data16 lea gv@tlsgd(%rip),%rdi + sink.put1(0x66); // data16 + sink.put1(0b01001000); // rex.w + const LEA: u8 = 0x8d; + sink.put1(LEA); // lea + modrm_riprel(0b111/*out_reg0*/, sink); // 0x3d + sink.reloc_external(Reloc::ElfX86_64TlsGd, + &func.global_values[global_value].symbol_name(), + -4); + sink.put4(0); + + // data16 data16 callq __tls_get_addr-4 + sink.put1(0x66); // data16 + sink.put1(0x66); // data16 + sink.put1(0b01001000); // rex.w + sink.put1(0xe8); // call + sink.reloc_external(Reloc::X86CallPLTRel4, + &ExternalName::LibCall(LibCall::ElfTlsGetAddr), + -4); + sink.put4(0); + "#, + ), + ); + + recipes.add_recipe( + EncodingRecipeBuilder::new("macho_tls_get_addr", &formats.unary_global_value, 9) + // FIXME Correct encoding for non rax registers + .operands_out(vec![reg_rax]) + .emit( + r#" + // output %rax + // clobbers %rdi + + // movq gv@tlv(%rip), %rdi + sink.put1(0x48); // rex + sink.put1(0x8b); // mov + modrm_riprel(0b111/*out_reg0*/, sink); // 0x3d + sink.reloc_external(Reloc::MachOX86_64Tlv, + &func.global_values[global_value].symbol_name(), + -4); + sink.put4(0); + + // callq *(%rdi) + sink.put1(0xff); + sink.put1(0x17); + "#, + ), + ); + recipes } diff --git a/cranelift/codegen/meta/src/shared/instructions.rs b/cranelift/codegen/meta/src/shared/instructions.rs index 471ad85a56..3e74da1b00 100644 --- a/cranelift/codegen/meta/src/shared/instructions.rs +++ b/cranelift/codegen/meta/src/shared/instructions.rs @@ -1127,6 +1127,18 @@ pub(crate) fn define( .operands_out(vec![a]), ); + ig.push( + Inst::new( + "tls_value", + r#" + Compute the value of global GV, which is a TLS (thread local storage) value. + "#, + &formats.unary_global_value, + ) + .operands_in(vec![GV]) + .operands_out(vec![a]), + ); + let HeapOffset = &TypeVar::new( "HeapOffset", "An unsigned heap offset", diff --git a/cranelift/codegen/meta/src/shared/mod.rs b/cranelift/codegen/meta/src/shared/mod.rs index 121e26b10a..b185262ccd 100644 --- a/cranelift/codegen/meta/src/shared/mod.rs +++ b/cranelift/codegen/meta/src/shared/mod.rs @@ -1,6 +1,6 @@ //! Shared definitions for the Cranelift intermediate language. -mod entities; +pub mod entities; pub mod formats; pub mod immediates; pub mod instructions; @@ -28,6 +28,7 @@ pub(crate) struct Definitions { pub imm: Immediates, pub formats: Formats, pub transform_groups: TransformGroups, + pub entities: EntityRefs, } pub(crate) fn define() -> Definitions { @@ -47,6 +48,7 @@ pub(crate) fn define() -> Definitions { imm: immediates, formats, transform_groups, + entities, } } diff --git a/cranelift/codegen/meta/src/shared/settings.rs b/cranelift/codegen/meta/src/shared/settings.rs index 545c2732b3..fd6063e852 100644 --- a/cranelift/codegen/meta/src/shared/settings.rs +++ b/cranelift/codegen/meta/src/shared/settings.rs @@ -131,6 +131,14 @@ pub(crate) fn define() -> SettingGroup { false, ); + settings.add_enum( + "tls_model", + r#" + Defines the model used to perform TLS accesses. + "#, + vec!["none", "elf_gd", "macho", "coff"], + ); + // Settings specific to the `baldrdash` calling convention. settings.add_enum( diff --git a/cranelift/codegen/src/binemit/mod.rs b/cranelift/codegen/src/binemit/mod.rs index 7a781e2b56..b52025e887 100644 --- a/cranelift/codegen/src/binemit/mod.rs +++ b/cranelift/codegen/src/binemit/mod.rs @@ -56,6 +56,12 @@ pub enum Reloc { Arm64Call, /// RISC-V call target RiscvCall, + + /// Elf x86_64 32 bit signed PC relative offset to two GOT entries for GD symbol. + ElfX86_64TlsGd, + + /// Mach-O x86_64 32 bit signed PC relative offset to a `__thread_vars` entry. + MachOX86_64Tlv, } impl fmt::Display for Reloc { @@ -71,6 +77,9 @@ impl fmt::Display for Reloc { Self::X86CallPLTRel4 => write!(f, "CallPLTRel4"), Self::X86GOTPCRel4 => write!(f, "GOTPCRel4"), Self::Arm32Call | Self::Arm64Call | Self::RiscvCall => write!(f, "Call"), + + Self::ElfX86_64TlsGd => write!(f, "ElfX86_64TlsGd"), + Self::MachOX86_64Tlv => write!(f, "MachOX86_64Tlv"), } } } diff --git a/cranelift/codegen/src/ir/globalvalue.rs b/cranelift/codegen/src/ir/globalvalue.rs index dbba5aa676..305654a95f 100644 --- a/cranelift/codegen/src/ir/globalvalue.rs +++ b/cranelift/codegen/src/ir/globalvalue.rs @@ -63,6 +63,9 @@ pub enum GlobalValueData { /// away, after linking? If so, references to it can avoid going through a GOT. Note that /// symbols meant to be preemptible cannot be colocated. colocated: bool, + + /// Does this symbol refer to a thread local storage value? + tls: bool, }, } @@ -110,11 +113,13 @@ impl fmt::Display for GlobalValueData { ref name, offset, colocated, + tls, } => { write!( f, - "symbol {}{}", + "symbol {}{}{}", if colocated { "colocated " } else { "" }, + if tls { "tls " } else { "" }, name )?; let offset_val: i64 = offset.into(); diff --git a/cranelift/codegen/src/ir/libcall.rs b/cranelift/codegen/src/ir/libcall.rs index 33d3fa6060..f4f6d941f8 100644 --- a/cranelift/codegen/src/ir/libcall.rs +++ b/cranelift/codegen/src/ir/libcall.rs @@ -46,6 +46,9 @@ pub enum LibCall { Memset, /// libc.memmove Memmove, + + /// Elf __tls_get_addr + ElfTlsGetAddr, } impl fmt::Display for LibCall { @@ -71,6 +74,8 @@ impl FromStr for LibCall { "Memcpy" => Ok(Self::Memcpy), "Memset" => Ok(Self::Memset), "Memmove" => Ok(Self::Memmove), + + "ElfTlsGetAddr" => Ok(Self::ElfTlsGetAddr), _ => Err(()), } } diff --git a/cranelift/codegen/src/isa/x86/binemit.rs b/cranelift/codegen/src/isa/x86/binemit.rs index 44c497e547..15defe69ee 100644 --- a/cranelift/codegen/src/isa/x86/binemit.rs +++ b/cranelift/codegen/src/isa/x86/binemit.rs @@ -4,7 +4,10 @@ use super::enc_tables::{needs_offset, needs_sib_byte}; use super::registers::RU; use crate::binemit::{bad_encoding, CodeSink, Reloc}; use crate::ir::condcodes::{CondCode, FloatCC, IntCC}; -use crate::ir::{Block, Constant, Function, Inst, InstructionData, JumpTable, Opcode, TrapCode}; +use crate::ir::{ + Block, Constant, ExternalName, Function, Inst, InstructionData, JumpTable, LibCall, Opcode, + TrapCode, +}; use crate::isa::{RegUnit, StackBase, StackBaseMask, StackRef, TargetIsa}; use crate::regalloc::RegDiversions; diff --git a/cranelift/codegen/src/isa/x86/enc_tables.rs b/cranelift/codegen/src/isa/x86/enc_tables.rs index 076fef3115..93e06d2795 100644 --- a/cranelift/codegen/src/isa/x86/enc_tables.rs +++ b/cranelift/codegen/src/isa/x86/enc_tables.rs @@ -1288,3 +1288,40 @@ fn convert_ineg( unreachable!() } } + +fn expand_tls_value( + inst: ir::Inst, + func: &mut ir::Function, + _cfg: &mut ControlFlowGraph, + isa: &dyn TargetIsa, +) { + use crate::settings::TlsModel; + + assert!( + isa.triple().architecture == target_lexicon::Architecture::X86_64, + "Not yet implemented for {:?}", + isa.triple(), + ); + + if let ir::InstructionData::UnaryGlobalValue { + opcode: ir::Opcode::TlsValue, + global_value, + } = func.dfg[inst] + { + let ctrl_typevar = func.dfg.ctrl_typevar(inst); + assert_eq!(ctrl_typevar, ir::types::I64); + + match isa.flags().tls_model() { + TlsModel::None => panic!("tls_model flag is not set."), + TlsModel::ElfGd => { + func.dfg.replace(inst).x86_elf_tls_get_addr(global_value); + } + TlsModel::Macho => { + func.dfg.replace(inst).x86_macho_tls_get_addr(global_value); + } + model => unimplemented!("tls_value for tls model {:?}", model), + } + } else { + unreachable!(); + } +} diff --git a/cranelift/codegen/src/legalizer/globalvalue.rs b/cranelift/codegen/src/legalizer/globalvalue.rs index 97ef58c2bb..5c7a72b45c 100644 --- a/cranelift/codegen/src/legalizer/globalvalue.rs +++ b/cranelift/codegen/src/legalizer/globalvalue.rs @@ -40,7 +40,7 @@ pub fn expand_global_value( global_type, readonly, } => load_addr(inst, func, base, offset, global_type, readonly, isa), - ir::GlobalValueData::Symbol { .. } => symbol(inst, func, gv, isa), + ir::GlobalValueData::Symbol { tls, .. } => symbol(inst, func, gv, isa, tls), } } @@ -123,7 +123,18 @@ fn load_addr( } /// Expand a `global_value` instruction for a symbolic name global. -fn symbol(inst: ir::Inst, func: &mut ir::Function, gv: ir::GlobalValue, isa: &dyn TargetIsa) { +fn symbol( + inst: ir::Inst, + func: &mut ir::Function, + gv: ir::GlobalValue, + isa: &dyn TargetIsa, + tls: bool, +) { let ptr_ty = isa.pointer_type(); - func.dfg.replace(inst).symbol_value(ptr_ty, gv); + + if tls { + func.dfg.replace(inst).tls_value(ptr_ty, gv); + } else { + func.dfg.replace(inst).symbol_value(ptr_ty, gv); + } } diff --git a/cranelift/codegen/src/regalloc/spilling.rs b/cranelift/codegen/src/regalloc/spilling.rs index d27c68ae42..e515543260 100644 --- a/cranelift/codegen/src/regalloc/spilling.rs +++ b/cranelift/codegen/src/regalloc/spilling.rs @@ -267,7 +267,11 @@ impl<'a> Context<'a> { // If inst is a call, spill all register values that are live across the call. // This means that we don't currently take advantage of callee-saved registers. // TODO: Be more sophisticated. - if call_sig.is_some() { + let opcode = self.cur.func.dfg[inst].opcode(); + if call_sig.is_some() + || opcode == crate::ir::Opcode::X86ElfTlsGetAddr + || opcode == crate::ir::Opcode::X86MachoTlsGetAddr + { for lv in throughs { if lv.affinity.is_reg() && !self.spills.contains(&lv.value) { self.spill_reg(lv.value); diff --git a/cranelift/codegen/src/settings.rs b/cranelift/codegen/src/settings.rs index b6e55b6147..57b9c18f89 100644 --- a/cranelift/codegen/src/settings.rs +++ b/cranelift/codegen/src/settings.rs @@ -379,6 +379,7 @@ mod tests { f.to_string(), "[shared]\n\ opt_level = \"none\"\n\ + tls_model = \"none\"\n\ libcall_call_conv = \"isa_default\"\n\ baldrdash_prologue_words = 0\n\ probestack_size_log2 = 12\n\ diff --git a/cranelift/faerie/src/backend.rs b/cranelift/faerie/src/backend.rs index e6439a69b9..40cca8eddc 100644 --- a/cranelift/faerie/src/backend.rs +++ b/cranelift/faerie/src/backend.rs @@ -136,8 +136,10 @@ impl Backend for FaerieBackend { name: &str, linkage: Linkage, writable: bool, + tls: bool, align: Option, ) { + assert!(!tls, "Faerie doesn't yet support TLS"); self.artifact .declare(name, translate_data_linkage(linkage, writable, align)) .expect("inconsistent declarations"); @@ -231,10 +233,12 @@ impl Backend for FaerieBackend { _id: DataId, name: &str, _writable: bool, + tls: bool, _align: Option, data_ctx: &DataContext, namespace: &ModuleNamespace, ) -> ModuleResult { + assert!(!tls, "Faerie doesn't yet support TLS"); let &DataDescription { ref init, ref function_decls, diff --git a/cranelift/filetests/filetests/isa/x86/tls_elf.clif b/cranelift/filetests/filetests/isa/x86/tls_elf.clif new file mode 100644 index 0000000000..3788dd7d27 --- /dev/null +++ b/cranelift/filetests/filetests/isa/x86/tls_elf.clif @@ -0,0 +1,18 @@ +test regalloc +set tls_model=elf_gd +target x86_64 + +function u0:0(i32) -> i32, i64 { +gv0 = symbol colocated tls u1:0 + +block0(v0: i32): + ; check: block0(v2: i32 [%rdi]): + ; nextln: [RexOp1spillSib32#89,ss0] v0 = spill v2 + v1 = global_value.i64 gv0 + ; nextln: [elf_tls_get_addr#00,%rax] v1 = x86_elf_tls_get_addr gv0 + ; nextln: [RexOp1fillSib32#8b,%r15] v3 = fill v0 + return v0, v1 + ; nextln: [RexOp1rmov#8089] regmove v1, %rax -> %rdx + ; nextln: [RexOp1rmov#89] regmove v3, %r15 -> %rax + ; nextln: [Op1ret#c3] return v3, v1 +} diff --git a/cranelift/filetests/filetests/isa/x86/tls_enc.clif b/cranelift/filetests/filetests/isa/x86/tls_enc.clif new file mode 100644 index 0000000000..acdda733a5 --- /dev/null +++ b/cranelift/filetests/filetests/isa/x86/tls_enc.clif @@ -0,0 +1,11 @@ +test binemit +target x86_64 + +function u0:0() -> i64, i64 { +gv0 = symbol colocated tls u1:0 + +block0: + [-, %rax] v0 = x86_elf_tls_get_addr gv0 ; bin: 66 48 8d 3d ElfX86_64TlsGd(u1:0-4) 00000000 66 66 48 e8 CallPLTRel4(%ElfTlsGetAddr-4) 00000000 + [-, %rax] v1 = x86_macho_tls_get_addr gv0; bin: 48 8b 3d MachOX86_64Tlv(u1:0-4) 00000000 ff 17 + return v0, v1 +} diff --git a/cranelift/filetests/filetests/isa/x86/tls_macho.clif b/cranelift/filetests/filetests/isa/x86/tls_macho.clif new file mode 100644 index 0000000000..d2c637d2e8 --- /dev/null +++ b/cranelift/filetests/filetests/isa/x86/tls_macho.clif @@ -0,0 +1,18 @@ +test regalloc +set tls_model=macho +target x86_64 + +function u0:0(i32) -> i32, i64 { +gv0 = symbol colocated tls u1:0 + +block0(v0: i32): + ; check: block0(v2: i32 [%rdi]): + ; nextln: [RexOp1spillSib32#89,ss0] v0 = spill v2 + v1 = global_value.i64 gv0 + ; nextln: [macho_tls_get_addr#00,%rax] v1 = x86_macho_tls_get_addr gv0 + ; nextln: [RexOp1fillSib32#8b,%r15] v3 = fill v0 + return v0, v1 + ; nextln: [RexOp1rmov#8089] regmove v1, %rax -> %rdx + ; nextln: [RexOp1rmov#89] regmove v3, %r15 -> %rax + ; nextln: [Op1ret#c3] return v3, v1 +} diff --git a/cranelift/module/src/backend.rs b/cranelift/module/src/backend.rs index 1464c4aefc..0fd8724afa 100644 --- a/cranelift/module/src/backend.rs +++ b/cranelift/module/src/backend.rs @@ -72,6 +72,7 @@ where name: &str, linkage: Linkage, writable: bool, + tls: bool, align: Option, ); @@ -107,6 +108,7 @@ where id: DataId, name: &str, writable: bool, + tls: bool, align: Option, data_ctx: &DataContext, namespace: &ModuleNamespace, @@ -188,5 +190,7 @@ pub fn default_libcall_names() -> Box String> { ir::LibCall::Memcpy => "memcpy".to_owned(), ir::LibCall::Memset => "memset".to_owned(), ir::LibCall::Memmove => "memmove".to_owned(), + + ir::LibCall::ElfTlsGetAddr => "__tls_get_addr".to_owned(), }) } diff --git a/cranelift/module/src/module.rs b/cranelift/module/src/module.rs index 10bca75ac7..5df095b610 100644 --- a/cranelift/module/src/module.rs +++ b/cranelift/module/src/module.rs @@ -188,6 +188,7 @@ pub struct DataDeclaration { pub name: String, pub linkage: Linkage, pub writable: bool, + pub tls: bool, pub align: Option, } @@ -206,10 +207,14 @@ impl ModuleData where B: Backend, { - fn merge(&mut self, linkage: Linkage, writable: bool, align: Option) { + fn merge(&mut self, linkage: Linkage, writable: bool, tls: bool, align: Option) { self.decl.linkage = Linkage::merge(self.decl.linkage, linkage); self.decl.writable = self.decl.writable || writable; self.decl.align = self.decl.align.max(align); + assert_eq!( + self.decl.tls, tls, + "Can't change TLS data object to normal or in the opposite way", + ); } } @@ -460,6 +465,7 @@ where name: &str, linkage: Linkage, writable: bool, + tls: bool, align: Option, // An alignment bigger than 128 is unlikely ) -> ModuleResult { // TODO: Can we avoid allocating names so often? @@ -468,12 +474,13 @@ where Occupied(entry) => match *entry.get() { FuncOrDataId::Data(id) => { let existing = &mut self.contents.data_objects[id]; - existing.merge(linkage, writable, align); + existing.merge(linkage, writable, tls, align); self.backend.declare_data( id, name, existing.decl.linkage, existing.decl.writable, + existing.decl.tls, existing.decl.align, ); Ok(id) @@ -489,13 +496,14 @@ where name: name.to_owned(), linkage, writable, + tls, align, }, compiled: None, }); entry.insert(FuncOrDataId::Data(id)); self.backend - .declare_data(id, name, linkage, writable, align); + .declare_data(id, name, linkage, writable, tls, align); Ok(id) } } @@ -526,6 +534,7 @@ where name: ir::ExternalName::user(1, data.as_u32()), offset: ir::immediates::Imm64::new(0), colocated, + tls: decl.tls, }) } @@ -634,6 +643,7 @@ where data, &info.decl.name, info.decl.writable, + info.decl.tls, info.decl.align, data_ctx, &ModuleNamespace:: { diff --git a/cranelift/object/Cargo.toml b/cranelift/object/Cargo.toml index 030d49c444..74007aab80 100644 --- a/cranelift/object/Cargo.toml +++ b/cranelift/object/Cargo.toml @@ -13,6 +13,7 @@ edition = "2018" cranelift-module = { path = "../cranelift-module", version = "0.59.0" } object = { version = "0.17", default-features = false, features = ["write"] } target-lexicon = "0.10" +goblin = "0.1.0" [dependencies.cranelift-codegen] path = "../cranelift-codegen" diff --git a/cranelift/object/src/backend.rs b/cranelift/object/src/backend.rs index c4fa5eaf72..044fdca815 100644 --- a/cranelift/object/src/backend.rs +++ b/cranelift/object/src/backend.rs @@ -17,7 +17,7 @@ use object::write::{ use object::{RelocationEncoding, RelocationKind, SymbolFlags, SymbolKind, SymbolScope}; use std::collections::HashMap; use std::mem; -use target_lexicon::PointerWidth; +use target_lexicon::{BinaryFormat, PointerWidth}; #[derive(Debug)] /// Setting to enable collection of traps. Setting this to `Enabled` in @@ -151,12 +151,19 @@ impl Backend for ObjectBackend { name: &str, linkage: Linkage, _writable: bool, + tls: bool, _align: Option, ) { + let kind = if tls { + SymbolKind::Tls + } else { + SymbolKind::Data + }; let (scope, weak) = translate_linkage(linkage); if let Some(data) = self.data_objects[id] { let symbol = self.object.symbol_mut(data); + symbol.kind = kind; symbol.scope = scope; symbol.weak = weak; } else { @@ -164,7 +171,7 @@ impl Backend for ObjectBackend { name: name.as_bytes().to_vec(), value: 0, size: 0, - kind: SymbolKind::Data, + kind, scope, weak, section: SymbolSection::Undefined, @@ -183,7 +190,7 @@ impl Backend for ObjectBackend { code_size: u32, ) -> ModuleResult { let mut code: Vec = vec![0; code_size as usize]; - let mut reloc_sink = ObjectRelocSink::default(); + let mut reloc_sink = ObjectRelocSink::new(self.object.format()); let mut trap_sink = ObjectTrapSink::default(); let mut stackmap_sink = NullStackmapSink {}; @@ -248,6 +255,7 @@ impl Backend for ObjectBackend { data_id: DataId, _name: &str, writable: bool, + tls: bool, align: Option, data_ctx: &DataContext, _namespace: &ModuleNamespace, @@ -289,7 +297,13 @@ impl Backend for ObjectBackend { let symbol = self.data_objects[data_id].unwrap(); let section_kind = if let Init::Zeros { .. } = *init { - StandardSection::UninitializedData + if tls { + StandardSection::UninitializedTls + } else { + StandardSection::UninitializedData + } + } else if tls { + StandardSection::Tls } else if writable { StandardSection::Data } else if relocs.is_empty() { @@ -519,11 +533,20 @@ struct RelocRecord { addend: Addend, } -#[derive(Default)] struct ObjectRelocSink { + format: BinaryFormat, relocs: Vec, } +impl ObjectRelocSink { + fn new(format: BinaryFormat) -> Self { + Self { + format, + relocs: vec![], + } + } +} + impl RelocSink for ObjectRelocSink { fn reloc_block(&mut self, _offset: CodeOffset, _reloc: Reloc, _block_offset: CodeOffset) { unimplemented!(); @@ -534,7 +557,7 @@ impl RelocSink for ObjectRelocSink { offset: CodeOffset, reloc: Reloc, name: &ir::ExternalName, - addend: Addend, + mut addend: Addend, ) { let (kind, encoding, size) = match reloc { Reloc::Abs4 => (RelocationKind::Absolute, RelocationEncoding::Generic, 32), @@ -549,6 +572,35 @@ impl RelocSink for ObjectRelocSink { 32, ), Reloc::X86GOTPCRel4 => (RelocationKind::GotRelative, RelocationEncoding::Generic, 32), + + Reloc::ElfX86_64TlsGd => { + assert_eq!( + self.format, + BinaryFormat::Elf, + "ElfX86_64TlsGd is not supported for this file format" + ); + ( + RelocationKind::Elf(goblin::elf64::reloc::R_X86_64_TLSGD), + RelocationEncoding::Generic, + 32, + ) + } + Reloc::MachOX86_64Tlv => { + assert_eq!( + self.format, + BinaryFormat::Macho, + "MachOX86_64Tlv is not supported for this file format" + ); + addend += 4; // X86_64_RELOC_TLV has an implicit addend of -4 + ( + RelocationKind::MachO { + value: goblin::mach::relocation::X86_64_RELOC_TLV, + relative: true, + }, + RelocationEncoding::Generic, + 32, + ) + } // FIXME _ => unimplemented!(), }; diff --git a/cranelift/reader/src/parser.rs b/cranelift/reader/src/parser.rs index 6cedb25b0d..02bf24ee8e 100644 --- a/cranelift/reader/src/parser.rs +++ b/cranelift/reader/src/parser.rs @@ -204,6 +204,7 @@ impl<'a> Context<'a> { name: ExternalName::testcase(""), offset: Imm64::new(0), colocated: false, + tls: false, }); } self.function.global_values[gv] = data; @@ -1443,12 +1444,14 @@ impl<'a> Parser<'a> { } "symbol" => { let colocated = self.optional(Token::Identifier("colocated")); + let tls = self.optional(Token::Identifier("tls")); let name = self.parse_external_name()?; let offset = self.optional_offset_imm64()?; GlobalValueData::Symbol { name, offset, colocated, + tls, } } other => return err!(self.loc, "Unknown global value kind '{}'", other), diff --git a/cranelift/simplejit/src/backend.rs b/cranelift/simplejit/src/backend.rs index 8d37b99920..08bc565133 100644 --- a/cranelift/simplejit/src/backend.rs +++ b/cranelift/simplejit/src/backend.rs @@ -264,8 +264,10 @@ impl<'simple_jit_backend> Backend for SimpleJITBackend { _name: &str, _linkage: Linkage, _writable: bool, + tls: bool, _align: Option, ) { + assert!(!tls, "SimpleJIT doesn't yet support TLS"); // Nothing to do. } @@ -341,10 +343,13 @@ impl<'simple_jit_backend> Backend for SimpleJITBackend { _id: DataId, _name: &str, writable: bool, + tls: bool, align: Option, data: &DataContext, _namespace: &ModuleNamespace, ) -> ModuleResult { + assert!(!tls, "SimpleJIT doesn't yet support TLS"); + let &DataDescription { ref init, ref function_decls,