Add support for macho relocations. (#378)

This requires splitting X86PCRel4 into two separate relocations, to
distinguish the case where the instruction is a call, as Mach-O uses a
different relocation in that case.

This also makes it explicit that only x86-64 relocations are supported
currently.
This commit is contained in:
Dan Gohman
2018-06-28 10:15:10 -07:00
committed by GitHub
parent cc94adca3b
commit c5aad1eb5f
8 changed files with 70 additions and 39 deletions

View File

@@ -352,7 +352,7 @@ ebb0:
[-,%rsi] v351 = bint.i32 v301 ; bin: 0f b6 f2 [-,%rsi] v351 = bint.i32 v301 ; bin: 0f b6 f2
; asm: call foo ; asm: call foo
call fn0() ; bin: stk_ovf e8 PCRel4(%foo-4) 00000000 call fn0() ; bin: stk_ovf e8 CallPCRel4(%foo-4) 00000000
; asm: movl $0, %ecx ; asm: movl $0, %ecx
[-,%rcx] v400 = func_addr.i32 fn0 ; bin: b9 Abs4(%foo) 00000000 [-,%rcx] v400 = func_addr.i32 fn0 ; bin: b9 Abs4(%foo) 00000000

View File

@@ -30,7 +30,7 @@ ebb0:
; Colocated functions. ; Colocated functions.
; asm: call foo ; asm: call foo
call fn1() ; bin: stk_ovf e8 PCRel4(%bar-4) 00000000 call fn1() ; bin: stk_ovf e8 CallPCRel4(%bar-4) 00000000
; asm: lea 0x0(%rip), %rax ; asm: lea 0x0(%rip), %rax
[-,%rax] v0 = func_addr.i64 fn1 ; bin: 48 8d 05 PCRel4(%bar-4) 00000000 [-,%rax] v0 = func_addr.i64 fn1 ; bin: 48 8d 05 PCRel4(%bar-4) 00000000
@@ -49,7 +49,7 @@ ebb0:
; Non-colocated functions. ; Non-colocated functions.
; asm: call foo@PLT ; asm: call foo@PLT
call fn0() ; bin: stk_ovf e8 PLTRel4(%foo-4) 00000000 call fn0() ; bin: stk_ovf e8 CallPLTRel4(%foo-4) 00000000
; asm: mov 0x0(%rip), %rax ; asm: mov 0x0(%rip), %rax
[-,%rax] v100 = func_addr.i64 fn0 ; bin: 48 8b 05 GOTPCRel4(%foo-4) 00000000 [-,%rax] v100 = func_addr.i64 fn0 ; bin: 48 8b 05 GOTPCRel4(%foo-4) 00000000

View File

@@ -485,7 +485,7 @@ ebb0:
; Colocated functions. ; Colocated functions.
; asm: call bar ; asm: call bar
call fn1() ; bin: stk_ovf e8 PCRel4(%bar-4) 00000000 call fn1() ; bin: stk_ovf e8 CallPCRel4(%bar-4) 00000000
; asm: lea 0x0(%rip), %rcx ; asm: lea 0x0(%rip), %rcx
[-,%rcx] v400 = func_addr.i64 fn1 ; bin: 48 8d 0d PCRel4(%bar-4) 00000000 [-,%rcx] v400 = func_addr.i64 fn1 ; bin: 48 8d 0d PCRel4(%bar-4) 00000000

View File

@@ -1388,7 +1388,7 @@ call_id = TailRecipe(
PUT_OP(bits, BASE_REX, sink); PUT_OP(bits, BASE_REX, sink);
// The addend adjusts for the difference between the end of the // The addend adjusts for the difference between the end of the
// instruction and the beginning of the immediate field. // instruction and the beginning of the immediate field.
sink.reloc_external(Reloc::X86PCRel4, sink.reloc_external(Reloc::X86CallPCRel4,
&func.dfg.ext_funcs[func_ref].name, &func.dfg.ext_funcs[func_ref].name,
-4); -4);
sink.put4(0); sink.put4(0);
@@ -1399,7 +1399,7 @@ call_plt_id = TailRecipe(
emit=''' emit='''
sink.trap(TrapCode::StackOverflow, func.srclocs[inst]); sink.trap(TrapCode::StackOverflow, func.srclocs[inst]);
PUT_OP(bits, BASE_REX, sink); PUT_OP(bits, BASE_REX, sink);
sink.reloc_external(Reloc::X86PLTRel4, sink.reloc_external(Reloc::X86CallPLTRel4,
&func.dfg.ext_funcs[func_ref].name, &func.dfg.ext_funcs[func_ref].name,
-4); -4);
sink.put4(0); sink.put4(0);

View File

@@ -33,10 +33,12 @@ pub enum Reloc {
Abs8, Abs8,
/// x86 PC-relative 4-byte /// x86 PC-relative 4-byte
X86PCRel4, X86PCRel4,
/// x86 call to PC-relative 4-byte
X86CallPCRel4,
/// x86 call to PLT-relative 4-byte
X86CallPLTRel4,
/// x86 GOT PC-relative 4-byte /// x86 GOT PC-relative 4-byte
X86GOTPCRel4, X86GOTPCRel4,
/// x86 PLT-relative 4-byte
X86PLTRel4,
/// Arm32 call target /// Arm32 call target
Arm32Call, Arm32Call,
/// Arm64 call target /// Arm64 call target
@@ -53,8 +55,9 @@ impl fmt::Display for Reloc {
Reloc::Abs4 => write!(f, "Abs4"), Reloc::Abs4 => write!(f, "Abs4"),
Reloc::Abs8 => write!(f, "Abs8"), Reloc::Abs8 => write!(f, "Abs8"),
Reloc::X86PCRel4 => write!(f, "PCRel4"), Reloc::X86PCRel4 => write!(f, "PCRel4"),
Reloc::X86CallPCRel4 => write!(f, "CallPCRel4"),
Reloc::X86CallPLTRel4 => write!(f, "CallPLTRel4"),
Reloc::X86GOTPCRel4 => write!(f, "GOTPCRel4"), Reloc::X86GOTPCRel4 => write!(f, "GOTPCRel4"),
Reloc::X86PLTRel4 => write!(f, "PLTRel4"),
Reloc::Arm32Call | Reloc::Arm64Call | Reloc::RiscvCall => write!(f, "Call"), Reloc::Arm32Call | Reloc::Arm64Call | Reloc::RiscvCall => write!(f, "Call"),
} }
} }

View File

@@ -11,7 +11,7 @@ use cretonne_module::{
use faerie; use faerie;
use failure::Error; use failure::Error;
use std::fs::File; use std::fs::File;
use target_lexicon::BinaryFormat; use target_lexicon::Triple;
use traps::{FaerieTrapManifest, FaerieTrapSink}; use traps::{FaerieTrapManifest, FaerieTrapSink};
#[derive(Debug)] #[derive(Debug)]
@@ -29,7 +29,6 @@ pub enum FaerieTrapCollection {
pub struct FaerieBuilder { pub struct FaerieBuilder {
isa: Box<TargetIsa>, isa: Box<TargetIsa>,
name: String, name: String,
format: BinaryFormat,
collect_traps: FaerieTrapCollection, collect_traps: FaerieTrapCollection,
libcall_names: Box<Fn(ir::LibCall) -> String>, libcall_names: Box<Fn(ir::LibCall) -> String>,
} }
@@ -51,7 +50,6 @@ impl FaerieBuilder {
pub fn new( pub fn new(
isa: Box<TargetIsa>, isa: Box<TargetIsa>,
name: String, name: String,
format: BinaryFormat,
collect_traps: FaerieTrapCollection, collect_traps: FaerieTrapCollection,
libcall_names: Box<Fn(ir::LibCall) -> String>, libcall_names: Box<Fn(ir::LibCall) -> String>,
) -> ModuleResult<Self> { ) -> ModuleResult<Self> {
@@ -63,7 +61,6 @@ impl FaerieBuilder {
Ok(Self { Ok(Self {
isa, isa,
name, name,
format,
collect_traps, collect_traps,
libcall_names, libcall_names,
}) })
@@ -91,7 +88,6 @@ impl FaerieBuilder {
pub struct FaerieBackend { pub struct FaerieBackend {
isa: Box<TargetIsa>, isa: Box<TargetIsa>,
artifact: faerie::Artifact, artifact: faerie::Artifact,
format: BinaryFormat,
trap_manifest: Option<FaerieTrapManifest>, trap_manifest: Option<FaerieTrapManifest>,
libcall_names: Box<Fn(ir::LibCall) -> String>, libcall_names: Box<Fn(ir::LibCall) -> String>,
} }
@@ -120,7 +116,6 @@ impl Backend for FaerieBackend {
Self { Self {
artifact: faerie::Artifact::new(builder.isa.triple().clone(), builder.name), artifact: faerie::Artifact::new(builder.isa.triple().clone(), builder.name),
isa: builder.isa, isa: builder.isa,
format: builder.format,
trap_manifest: match builder.collect_traps { trap_manifest: match builder.collect_traps {
FaerieTrapCollection::Enabled => Some(FaerieTrapManifest::new()), FaerieTrapCollection::Enabled => Some(FaerieTrapManifest::new()),
FaerieTrapCollection::Disabled => None, FaerieTrapCollection::Disabled => None,
@@ -158,7 +153,7 @@ impl Backend for FaerieBackend {
// Non-lexical lifetimes would obviate the braces here. // Non-lexical lifetimes would obviate the braces here.
{ {
let mut reloc_sink = FaerieRelocSink { let mut reloc_sink = FaerieRelocSink {
format: self.format, triple: self.isa.triple().clone(),
artifact: &mut self.artifact, artifact: &mut self.artifact,
name, name,
namespace, namespace,
@@ -348,7 +343,7 @@ fn translate_data_linkage(linkage: Linkage, writable: bool) -> faerie::Decl {
} }
struct FaerieRelocSink<'a> { struct FaerieRelocSink<'a> {
format: BinaryFormat, triple: Triple,
artifact: &'a mut faerie::Artifact, artifact: &'a mut faerie::Artifact,
name: &'a str, name: &'a str,
namespace: &'a ModuleNamespace<'a, FaerieBackend>, namespace: &'a ModuleNamespace<'a, FaerieBackend>,
@@ -384,9 +379,11 @@ impl<'a> RelocSink for FaerieRelocSink<'a> {
} }
_ => panic!("invalid ExternalName {}", name), _ => panic!("invalid ExternalName {}", name),
}; };
let addend_i32 = addend as i32; let (raw_reloc, raw_addend) = container::raw_relocation(reloc, &self.triple);
debug_assert!(i64::from(addend_i32) == addend); // TODO: Handle overflow.
let raw_reloc = container::raw_relocation(reloc, self.format); let final_addend = addend + raw_addend;
let addend_i32 = final_addend as i32;
debug_assert!(i64::from(addend_i32) == final_addend);
self.artifact self.artifact
.link_with( .link_with(
faerie::Link { faerie::Link {

View File

@@ -1,7 +1,7 @@
//! Utilities for working with Faerie container formats. //! Utilities for working with Faerie container formats.
use cretonne_codegen::binemit::Reloc; use cretonne_codegen::binemit::Reloc;
use target_lexicon::BinaryFormat; use target_lexicon::{Architecture, BinaryFormat, Triple};
/// An object file format. /// An object file format.
#[derive(Copy, Clone, Debug, Eq, PartialEq)] #[derive(Copy, Clone, Debug, Eq, PartialEq)]
@@ -13,23 +13,53 @@ pub enum Format {
} }
/// Translate from a Cretonne `Reloc` to a raw object-file-format-specific /// Translate from a Cretonne `Reloc` to a raw object-file-format-specific
/// relocation code. /// relocation code and relocation-implied addend.
pub fn raw_relocation(reloc: Reloc, format: BinaryFormat) -> u32 { pub fn raw_relocation(reloc: Reloc, triple: &Triple) -> (u32, i64) {
match format { match triple.binary_format {
BinaryFormat::Elf => { BinaryFormat::Elf => {
use goblin::elf; use goblin::elf;
match reloc { (
Reloc::Abs4 => elf::reloc::R_X86_64_32, match triple.architecture {
Reloc::Abs8 => elf::reloc::R_X86_64_64, Architecture::X86_64 => {
Reloc::X86PCRel4 => elf::reloc::R_X86_64_PC32, match reloc {
// TODO: Get Cretonne to tell us when we can use Reloc::Abs4 => elf::reloc::R_X86_64_32,
// R_X86_64_GOTPCRELX/R_X86_64_REX_GOTPCRELX. Reloc::Abs8 => elf::reloc::R_X86_64_64,
Reloc::X86GOTPCRel4 => elf::reloc::R_X86_64_GOTPCREL, Reloc::X86PCRel4 | Reloc::X86CallPCRel4 => elf::reloc::R_X86_64_PC32,
Reloc::X86PLTRel4 => elf::reloc::R_X86_64_PLT32, // TODO: Get Cretonne to tell us when we can use
_ => unimplemented!(), // R_X86_64_GOTPCRELX/R_X86_64_REX_GOTPCRELX.
Reloc::X86CallPLTRel4 => elf::reloc::R_X86_64_PLT32,
Reloc::X86GOTPCRel4 => elf::reloc::R_X86_64_GOTPCREL,
_ => unimplemented!(),
}
}
_ => unimplemented!("unsupported architecture: {}", triple),
},
// Most ELF relocations do not include an implicit addend.
0,
)
}
BinaryFormat::Macho => {
use goblin::mach;
match triple.architecture {
Architecture::X86_64 => {
match reloc {
Reloc::Abs8 => (u32::from(mach::relocation::R_ABS), 0),
// Mach-O doesn't need us to distinguish between PC-relative calls
// and PLT calls, but it does need us to distinguish between calls
// and non-calls. And, it includes the 4-byte addend implicitly.
Reloc::X86PCRel4 => (u32::from(mach::relocation::X86_64_RELOC_SIGNED), 4),
Reloc::X86CallPCRel4 | Reloc::X86CallPLTRel4 => {
(u32::from(mach::relocation::X86_64_RELOC_BRANCH), 4)
}
Reloc::X86GOTPCRel4 => {
(u32::from(mach::relocation::X86_64_RELOC_GOT_LOAD), 4)
}
_ => unimplemented!("unsupported mach-o reloc: {}", reloc),
}
}
_ => unimplemented!("unsupported architecture: {}", triple),
} }
} }
BinaryFormat::Macho => unimplemented!("macho relocations"),
_ => unimplemented!("unsupported format"), _ => unimplemented!("unsupported format"),
} }
} }

View File

@@ -271,7 +271,7 @@ impl<'simple_jit_backend> Backend for SimpleJITBackend {
write_unaligned(at as *mut u64, what as u64) write_unaligned(at as *mut u64, what as u64)
}; };
} }
Reloc::X86PCRel4 => { Reloc::X86PCRel4 | Reloc::X86CallPCRel4 => {
// TODO: Handle overflow. // TODO: Handle overflow.
let pcrel = ((what as isize) - (at as isize)) as i32; let pcrel = ((what as isize) - (at as isize)) as i32;
#[cfg_attr(feature = "cargo-clippy", allow(cast_ptr_alignment))] #[cfg_attr(feature = "cargo-clippy", allow(cast_ptr_alignment))]
@@ -279,7 +279,7 @@ impl<'simple_jit_backend> Backend for SimpleJITBackend {
write_unaligned(at as *mut i32, pcrel) write_unaligned(at as *mut i32, pcrel)
}; };
} }
Reloc::X86GOTPCRel4 | Reloc::X86PLTRel4 => panic!("unexpected PIC relocation"), Reloc::X86GOTPCRel4 | Reloc::X86CallPLTRel4 => panic!("unexpected PIC relocation"),
_ => unimplemented!(), _ => unimplemented!(),
} }
} }
@@ -336,9 +336,10 @@ impl<'simple_jit_backend> Backend for SimpleJITBackend {
write_unaligned(at as *mut u64, what as u64) write_unaligned(at as *mut u64, what as u64)
}; };
} }
Reloc::X86PCRel4 | Reloc::X86GOTPCRel4 | Reloc::X86PLTRel4 => { Reloc::X86PCRel4
panic!("unexpected text relocation in data") | Reloc::X86CallPCRel4
} | Reloc::X86GOTPCRel4
| Reloc::X86CallPLTRel4 => panic!("unexpected text relocation in data"),
_ => unimplemented!(), _ => unimplemented!(),
} }
} }