-
Notifications
You must be signed in to change notification settings - Fork 201
FrameLayoutChange-based .eh_frame support #902
Changes from all commits
33784f2
88d6c2c
dc3dc2e
c865e87
5e586cd
b1c81fd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,13 +9,16 @@ use cranelift_codegen::binemit::{ | |
use cranelift_codegen::isa::TargetIsa; | ||
use cranelift_codegen::{self, binemit, ir}; | ||
use cranelift_module::{ | ||
Backend, DataContext, DataDescription, DataId, FuncId, Init, Linkage, ModuleError, | ||
Backend, DataContext, DataDescription, DataId, FrameSink, FuncId, Init, Linkage, ModuleError, | ||
ModuleNamespace, ModuleResult, | ||
}; | ||
use faerie; | ||
use std::fs::File; | ||
use target_lexicon::Triple; | ||
|
||
use gimli::write::{Address, FrameDescriptionEntry}; | ||
use gimli::{DW_EH_PE_pcrel, DW_EH_PE_sdata4}; | ||
|
||
#[derive(Debug)] | ||
/// Setting to enable collection of traps. Setting this to `Enabled` in | ||
/// `FaerieBuilder` means that a `FaerieTrapManifest` will be present | ||
|
@@ -76,9 +79,76 @@ pub struct FaerieBackend { | |
isa: Box<dyn TargetIsa>, | ||
artifact: faerie::Artifact, | ||
trap_manifest: Option<FaerieTrapManifest>, | ||
frame_sink: Option<FrameSink>, | ||
libcall_names: Box<dyn Fn(ir::LibCall) -> String>, | ||
} | ||
|
||
struct FaerieDebugSink<'a> { | ||
pub data: &'a mut Vec<u8>, | ||
pub functions: &'a [String], | ||
pub artifact: &'a mut faerie::Artifact, | ||
} | ||
|
||
impl<'a> gimli::write::Writer for FaerieDebugSink<'a> { | ||
type Endian = gimli::LittleEndian; | ||
|
||
fn endian(&self) -> Self::Endian { | ||
gimli::LittleEndian | ||
} | ||
fn len(&self) -> usize { | ||
self.data.len() | ||
} | ||
fn write(&mut self, bytes: &[u8]) -> gimli::write::Result<()> { | ||
self.data.extend_from_slice(bytes); | ||
Ok(()) | ||
} | ||
|
||
fn write_at(&mut self, offset: usize, bytes: &[u8]) -> gimli::write::Result<()> { | ||
if offset + bytes.len() > self.data.len() { | ||
return Err(gimli::write::Error::LengthOutOfBounds); | ||
} | ||
self.data[offset..][..bytes.len()].copy_from_slice(bytes); | ||
Ok(()) | ||
} | ||
|
||
fn write_eh_pointer( | ||
&mut self, | ||
address: Address, | ||
eh_pe: gimli::DwEhPe, | ||
size: u8, | ||
) -> gimli::write::Result<()> { | ||
match address { | ||
Address::Constant(val) => self.write_udata(val, size), | ||
Address::Symbol { symbol, addend } => { | ||
assert_eq!(addend, 0); | ||
|
||
assert_eq!(eh_pe.format(), DW_EH_PE_sdata4, "faerie backend currently only supports PC-relative 4-byte offsets for DWARF pointers."); | ||
assert_eq!(eh_pe.application(), DW_EH_PE_pcrel, "faerie backend currently only supports PC-relative 4-byte offsets for DWARF pointers."); | ||
|
||
let name = self.functions[symbol].as_str(); | ||
|
||
let reloc = faerie::artifact::Reloc::Raw { | ||
reloc: goblin::elf::reloc::R_X86_64_PC32, | ||
addend: 0, | ||
}; | ||
|
||
self.artifact | ||
.link_with( | ||
faerie::Link { | ||
to: name, | ||
from: ".eh_frame", | ||
at: self.data.len() as u64, | ||
}, | ||
reloc, | ||
) | ||
.map_err(|_link_err| gimli::write::Error::InvalidAddress)?; | ||
|
||
self.write_udata(0, size) | ||
} | ||
} | ||
} | ||
} | ||
|
||
pub struct FaerieCompiledFunction { | ||
code_length: u32, | ||
} | ||
|
@@ -108,13 +178,15 @@ impl Backend for FaerieBackend { | |
|
||
/// Create a new `FaerieBackend` using the given Cranelift target. | ||
fn new(builder: FaerieBuilder) -> Self { | ||
let frame_sink = FrameSink::new(&builder.isa); | ||
Self { | ||
artifact: faerie::Artifact::new(builder.isa.triple().clone(), builder.name), | ||
isa: builder.isa, | ||
trap_manifest: match builder.collect_traps { | ||
FaerieTrapCollection::Enabled => Some(FaerieTrapManifest::new()), | ||
FaerieTrapCollection::Disabled => None, | ||
}, | ||
frame_sink: Some(frame_sink), | ||
libcall_names: builder.libcall_names, | ||
} | ||
} | ||
|
@@ -193,6 +265,44 @@ impl Backend for FaerieBackend { | |
// because `define` will take ownership of code, this is our last chance | ||
let code_length = code.len() as u32; | ||
|
||
if let Some(ref mut frame_sink) = self.frame_sink { | ||
if let Some(layout) = ctx.func.frame_layout.as_ref() { | ||
let (cie, mut encoder) = frame_sink.cie_for(&layout.initial); | ||
|
||
let mut fd_entry = | ||
FrameDescriptionEntry::new(frame_sink.address_for(name), code_length); | ||
|
||
let mut frame_changes = vec![]; | ||
for ebb in ctx.func.layout.ebbs() { | ||
for (offset, inst, size) in | ||
ctx.func.inst_offsets(ebb, &self.isa.encoding_info()) | ||
{ | ||
if let Some(changes) = layout.instructions.get(&inst) { | ||
for change in changes.iter() { | ||
frame_changes.push((offset + size, change.clone())); | ||
} | ||
} | ||
} | ||
} | ||
|
||
frame_changes.sort_by(|a, b| a.0.cmp(&b.0)); | ||
|
||
let fde_insts = frame_changes | ||
.into_iter() | ||
.flat_map(|(addr, change)| encoder.translate(&change).map(|inst| (addr, inst))); | ||
|
||
for (addr, inst) in fde_insts.into_iter() { | ||
fd_entry.add_instruction(addr, inst); | ||
} | ||
|
||
frame_sink.add_fde(cie, fd_entry); | ||
} else { | ||
// we have a frame sink to write .eh_frames into, but are not collecting debug | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please panic here to make it easier to debug in that case. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I realized this morning that there's a case where we will reach this arm but not be a bug: if the user of cranelift has a per-function Given that, would you rather a panic to identify the case for later discussion, or should I discard the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I believe unwinding enabled/disabled is done per crate (using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. rustc has the unstable |
||
// information for at least the current function. This might be a bug in the code | ||
// using cranelift-faerie? | ||
} | ||
} | ||
|
||
self.artifact | ||
.define(name, code) | ||
.expect("inconsistent declaration"); | ||
|
@@ -308,7 +418,25 @@ impl Backend for FaerieBackend { | |
} | ||
|
||
fn publish(&mut self) { | ||
// Nothing to do. | ||
if let Some(ref mut frame_sink) = self.frame_sink { | ||
self.artifact | ||
.declare( | ||
".eh_frame", | ||
faerie::Decl::section(faerie::SectionKind::Data), | ||
) | ||
.unwrap(); | ||
|
||
let mut eh_frame_bytes = Vec::new(); | ||
|
||
let mut eh_frame_writer = gimli::write::EhFrame(FaerieDebugSink { | ||
data: &mut eh_frame_bytes, | ||
functions: frame_sink.fn_names_slice(), | ||
artifact: &mut self.artifact, | ||
}); | ||
frame_sink.write_to(&mut eh_frame_writer).unwrap(); | ||
|
||
self.artifact.define(".eh_frame", eh_frame_bytes).unwrap(); | ||
} | ||
} | ||
|
||
fn finish(self) -> FaerieProduct { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this
__eh_frame
for machO?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a good question, I'll at least see what other toolchains name it. I also need to use a machO-appropriate relocation.