diff --git a/cranelift/codegen/src/isa/x64/inst/emit.rs b/cranelift/codegen/src/isa/x64/inst/emit.rs index 0bd8af840f..580d469b8d 100644 --- a/cranelift/codegen/src/isa/x64/inst/emit.rs +++ b/cranelift/codegen/src/isa/x64/inst/emit.rs @@ -2716,16 +2716,41 @@ pub(crate) fn emit( } Inst::LoadExtName { dst, name, offset } => { - // The full address can be encoded in the register, with a relocation. - // Generates: movabsq $name, %dst - let enc_dst = int_reg_enc(dst.to_reg()); - sink.put1(0x48 | ((enc_dst >> 3) & 1)); - sink.put1(0xB8 | (enc_dst & 7)); - emit_reloc(sink, state, Reloc::Abs8, name, *offset); - if info.flags().emit_all_ones_funcaddrs() { - sink.put8(u64::max_value()); + if info.flags().is_pic() { + // Generates: movq symbol@GOTPCREL(%rip), %dst + let enc_dst = int_reg_enc(dst.to_reg()); + sink.put1(0x48 | ((enc_dst >> 3) & 1) << 2); + sink.put1(0x8B); + sink.put1(0x05 | ((enc_dst & 7) << 3)); + emit_reloc(sink, state, Reloc::X86GOTPCRel4, name, -4); + sink.put4(0); + // Offset in the relocation above applies to the address of the *GOT entry*, not + // the loaded address; so we emit a separate add or sub instruction if needed. + if *offset < 0 { + assert!(*offset >= -i32::MAX as i64); + sink.put1(0x48 | ((enc_dst >> 3) & 1)); + sink.put1(0x81); + sink.put1(0xe8 | (enc_dst & 7)); + sink.put4((-*offset) as u32); + } else if *offset > 0 { + assert!(*offset <= i32::MAX as i64); + sink.put1(0x48 | ((enc_dst >> 3) & 1)); + sink.put1(0x81); + sink.put1(0xc0 | (enc_dst & 7)); + sink.put4(*offset as u32); + } } else { - sink.put8(0); + // The full address can be encoded in the register, with a relocation. + // Generates: movabsq $name, %dst + let enc_dst = int_reg_enc(dst.to_reg()); + sink.put1(0x48 | ((enc_dst >> 3) & 1)); + sink.put1(0xB8 | (enc_dst & 7)); + emit_reloc(sink, state, Reloc::Abs8, name, *offset); + if info.flags().emit_all_ones_funcaddrs() { + sink.put8(u64::max_value()); + } else { + sink.put8(0); + } } } diff --git a/cranelift/codegen/src/isa/x64/inst/emit_tests.rs b/cranelift/codegen/src/isa/x64/inst/emit_tests.rs index 48e831b2d8..c3489089b9 100644 --- a/cranelift/codegen/src/isa/x64/inst/emit_tests.rs +++ b/cranelift/codegen/src/isa/x64/inst/emit_tests.rs @@ -2860,6 +2860,46 @@ fn test_x64_emit() { "call *321(%r10,%rdx,4)", )); + // ======================================================== + // LoadExtName + // N.B.: test harness below sets is_pic. + insns.push(( + Inst::LoadExtName { + dst: Writable::from_reg(r11), + name: Box::new(ExternalName::User { + namespace: 0, + index: 0, + }), + offset: 0, + }, + "4C8B1D00000000", + "load_ext_name u0:0+0, %r11", + )); + insns.push(( + Inst::LoadExtName { + dst: Writable::from_reg(r11), + name: Box::new(ExternalName::User { + namespace: 0, + index: 0, + }), + offset: 0x12345678, + }, + "4C8B1D000000004981C378563412", + "load_ext_name u0:0+305419896, %r11", + )); + insns.push(( + Inst::LoadExtName { + dst: Writable::from_reg(r11), + name: Box::new(ExternalName::User { + namespace: 0, + index: 0, + }), + offset: -0x12345678, + }, + "4C8B1D000000004981EB78563412", + "load_ext_name u0:0+-305419896, %r11", + )); + // ======================================================== // Ret insns.push((Inst::ret(), "C3", "ret")); @@ -3781,7 +3821,9 @@ fn test_x64_emit() { // ======================================================== // Actually run the tests! - let flags = settings::Flags::new(settings::builder()); + let mut flag_builder = settings::builder(); + flag_builder.enable("is_pic").unwrap(); + let flags = settings::Flags::new(flag_builder); use crate::settings::Configurable; let mut isa_flag_builder = x64::settings::builder(); diff --git a/cranelift/codegen/src/isa/x64/inst/mod.rs b/cranelift/codegen/src/isa/x64/inst/mod.rs index 66a68a001b..09c469498d 100644 --- a/cranelift/codegen/src/isa/x64/inst/mod.rs +++ b/cranelift/codegen/src/isa/x64/inst/mod.rs @@ -397,7 +397,10 @@ pub enum Inst { /// An instruction that will always trigger the illegal instruction exception. Ud2 { trap_code: TrapCode }, - /// Loads an external symbol in a register, with a relocation: movabsq $name, dst + /// Loads an external symbol in a register, with a relocation: + /// + /// movq $name@GOTPCREL(%rip), dst if PIC is enabled, or + /// movabsq $name, dst otherwise. LoadExtName { dst: Writable, name: Box, @@ -1726,7 +1729,7 @@ impl PrettyPrint for Inst { dst, name, offset, .. } => format!( "{} {}+{}, {}", - ljustify("movaps".into()), + ljustify("load_ext_name".into()), name, offset, show_ireg_sized(dst.to_reg(), mb_rru, 8),