This exposes the functionality of `fde::map_reg` on the `TargetIsa` trait, avoiding compilation errors on architectures where register mapping is not yet supported. The change is conditially compiled under the `unwind` feature.
140 lines
4.9 KiB
Rust
140 lines
4.9 KiB
Rust
use std::collections::HashMap;
|
|
use wasmtime_environ::entity::EntityRef;
|
|
use wasmtime_environ::isa::{CallConv, TargetIsa};
|
|
use wasmtime_environ::wasm::DefinedFuncIndex;
|
|
use wasmtime_environ::{FrameLayoutChange, FrameLayouts};
|
|
|
|
use gimli::write::{
|
|
Address, CallFrameInstruction, CommonInformationEntry as CIEEntry, Error,
|
|
FrameDescriptionEntry as FDEEntry, FrameTable,
|
|
};
|
|
use gimli::{Encoding, Format, Register, X86_64};
|
|
|
|
fn to_cfi(
|
|
isa: &dyn TargetIsa,
|
|
change: &FrameLayoutChange,
|
|
cfa_def_reg: &mut Register,
|
|
cfa_def_offset: &mut i32,
|
|
) -> Option<CallFrameInstruction> {
|
|
Some(match change {
|
|
FrameLayoutChange::CallFrameAddressAt { reg, offset } => {
|
|
let mapped = match isa.map_dwarf_register(*reg) {
|
|
Ok(r) => Register(r),
|
|
Err(_) => return None,
|
|
};
|
|
let offset = (*offset) as i32;
|
|
if mapped != *cfa_def_reg && offset != *cfa_def_offset {
|
|
*cfa_def_reg = mapped;
|
|
*cfa_def_offset = offset;
|
|
CallFrameInstruction::Cfa(mapped, offset)
|
|
} else if offset != *cfa_def_offset {
|
|
*cfa_def_offset = offset;
|
|
CallFrameInstruction::CfaOffset(offset)
|
|
} else if mapped != *cfa_def_reg {
|
|
*cfa_def_reg = mapped;
|
|
CallFrameInstruction::CfaRegister(mapped)
|
|
} else {
|
|
return None;
|
|
}
|
|
}
|
|
FrameLayoutChange::RegAt { reg, cfa_offset } => {
|
|
assert!(cfa_offset % -8 == 0);
|
|
let cfa_offset = *cfa_offset as i32;
|
|
let mapped = match isa.map_dwarf_register(*reg) {
|
|
Ok(r) => Register(r),
|
|
Err(_) => return None,
|
|
};
|
|
CallFrameInstruction::Offset(mapped, cfa_offset)
|
|
}
|
|
FrameLayoutChange::ReturnAddressAt { cfa_offset } => {
|
|
assert!(cfa_offset % -8 == 0);
|
|
let cfa_offset = *cfa_offset as i32;
|
|
CallFrameInstruction::Offset(X86_64::RA, cfa_offset)
|
|
}
|
|
FrameLayoutChange::Preserve => CallFrameInstruction::RememberState,
|
|
FrameLayoutChange::Restore => CallFrameInstruction::RestoreState,
|
|
})
|
|
}
|
|
|
|
pub fn get_debug_frame_bytes(
|
|
funcs: &[(*const u8, usize)],
|
|
isa: &dyn TargetIsa,
|
|
layouts: &FrameLayouts,
|
|
) -> Result<Option<FrameTable>, Error> {
|
|
// FIXME Only x86-64 at this moment.
|
|
if isa.name() != "x86" || isa.pointer_bits() != 64 {
|
|
return Ok(None);
|
|
}
|
|
|
|
let address_size = isa.pointer_bytes();
|
|
let encoding = Encoding {
|
|
format: Format::Dwarf64,
|
|
version: 4,
|
|
address_size,
|
|
};
|
|
|
|
let mut frames = FrameTable::default();
|
|
|
|
let mut cached_cies = HashMap::new();
|
|
|
|
for (i, f) in funcs.into_iter().enumerate() {
|
|
let layout = &layouts[DefinedFuncIndex::new(i)];
|
|
|
|
// FIXME Can only process functions with SystemV-like prologue.
|
|
if layout.call_conv != CallConv::Fast
|
|
&& layout.call_conv != CallConv::Cold
|
|
&& layout.call_conv != CallConv::SystemV
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Caching CIE with similar initial_commands.
|
|
let (cie_id, mut cfa_def_reg, mut cfa_def_offset) = {
|
|
use std::collections::hash_map::Entry;
|
|
match cached_cies.entry(&layout.initial_commands) {
|
|
Entry::Occupied(o) => *o.get(),
|
|
Entry::Vacant(v) => {
|
|
// cfa_def_reg and cfa_def_offset initialized with some random values.
|
|
let mut cfa_def_reg = X86_64::RA;
|
|
let mut cfa_def_offset = 0i32;
|
|
|
|
// TODO adjust code_alignment_factor and data_alignment_factor based on ISA.
|
|
let mut cie = CIEEntry::new(
|
|
encoding,
|
|
/* code_alignment_factor = */ 1,
|
|
/* data_alignment_factor = */ -8,
|
|
/* return_address_register = */ X86_64::RA,
|
|
);
|
|
for cmd in layout.initial_commands.iter() {
|
|
if let Some(instr) = to_cfi(isa, cmd, &mut cfa_def_reg, &mut cfa_def_offset)
|
|
{
|
|
cie.add_instruction(instr);
|
|
}
|
|
}
|
|
let cie_id = frames.add_cie(cie);
|
|
*v.insert((cie_id, cfa_def_reg, cfa_def_offset))
|
|
}
|
|
}
|
|
};
|
|
|
|
let f_len = f.1 as u32;
|
|
let mut fde = FDEEntry::new(
|
|
Address::Symbol {
|
|
symbol: i,
|
|
addend: 0,
|
|
},
|
|
f_len,
|
|
);
|
|
|
|
for (offset, cmd) in layout.commands.into_iter() {
|
|
if let Some(instr) = to_cfi(isa, cmd, &mut cfa_def_reg, &mut cfa_def_offset) {
|
|
fde.add_instruction(*offset as u32, instr);
|
|
}
|
|
}
|
|
|
|
frames.add_fde(cie_id, fde);
|
|
}
|
|
|
|
Ok(Some(frames))
|
|
}
|