Write .debug_frame information (#53)
* Write .debug_frame information * mv map_reg
This commit is contained in:
140
crates/debug/src/frame.rs
Normal file
140
crates/debug/src/frame.rs
Normal file
@@ -0,0 +1,140 @@
|
||||
use crate::transform::map_reg;
|
||||
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 map_reg(isa, *reg) {
|
||||
Ok(r) => 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 map_reg(isa, *reg) {
|
||||
Ok(r) => 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))
|
||||
}
|
||||
@@ -2,17 +2,19 @@
|
||||
|
||||
#![allow(clippy::cast_ptr_alignment)]
|
||||
|
||||
use crate::frame::get_debug_frame_bytes;
|
||||
use anyhow::Error;
|
||||
use faerie::{Artifact, Decl};
|
||||
use more_asserts::assert_gt;
|
||||
use target_lexicon::BinaryFormat;
|
||||
use wasmtime_environ::isa::TargetIsa;
|
||||
use wasmtime_environ::{ModuleAddressMap, ModuleVmctxInfo, ValueLabelsRanges};
|
||||
use wasmtime_environ::{FrameLayouts, ModuleAddressMap, ModuleVmctxInfo, ValueLabelsRanges};
|
||||
|
||||
pub use crate::read_debuginfo::{read_debuginfo, DebugInfoData, WasmFileInfo};
|
||||
pub use crate::transform::transform_dwarf;
|
||||
pub use crate::write_debuginfo::{emit_dwarf, ResolvedSymbol, SymbolResolver};
|
||||
|
||||
mod frame;
|
||||
mod gc;
|
||||
mod read_debuginfo;
|
||||
mod transform;
|
||||
@@ -33,10 +35,21 @@ pub fn emit_debugsections(
|
||||
debuginfo_data: &DebugInfoData,
|
||||
at: &ModuleAddressMap,
|
||||
ranges: &ValueLabelsRanges,
|
||||
frame_layouts: &FrameLayouts,
|
||||
) -> Result<(), Error> {
|
||||
let resolver = FunctionRelocResolver {};
|
||||
let dwarf = transform_dwarf(isa, debuginfo_data, at, vmctx_info, ranges)?;
|
||||
emit_dwarf(obj, dwarf, &resolver)?;
|
||||
|
||||
let max = at.values().map(|v| v.body_len).fold(0, usize::max);
|
||||
let mut funcs_bodies = Vec::with_capacity(max as usize);
|
||||
funcs_bodies.resize(max as usize, 0);
|
||||
let funcs = at
|
||||
.values()
|
||||
.map(|v| (::std::ptr::null(), v.body_len))
|
||||
.collect::<Vec<(*const u8, usize)>>();
|
||||
let frames = get_debug_frame_bytes(&funcs, isa, frame_layouts)?;
|
||||
|
||||
emit_dwarf(obj, dwarf, &resolver, frames)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -57,6 +70,7 @@ pub fn emit_debugsections_image(
|
||||
vmctx_info: &ModuleVmctxInfo,
|
||||
at: &ModuleAddressMap,
|
||||
ranges: &ValueLabelsRanges,
|
||||
frame_layouts: &FrameLayouts,
|
||||
funcs: &[(*const u8, usize)],
|
||||
) -> Result<Vec<u8>, Error> {
|
||||
let func_offsets = &funcs
|
||||
@@ -79,7 +93,8 @@ pub fn emit_debugsections_image(
|
||||
let body = unsafe { std::slice::from_raw_parts(segment_body.0, segment_body.1) };
|
||||
obj.declare_with("all", Decl::function(), body.to_vec())?;
|
||||
|
||||
emit_dwarf(&mut obj, dwarf, &resolver)?;
|
||||
let frames = get_debug_frame_bytes(funcs, isa, frame_layouts)?;
|
||||
emit_dwarf(&mut obj, dwarf, &resolver, frames)?;
|
||||
|
||||
// LLDB is too "magical" about mach-o, generating elf
|
||||
let mut bytes = obj.emit_as(BinaryFormat::Elf)?;
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
use super::address_transform::AddressTransform;
|
||||
use anyhow::{bail, Context, Error, Result};
|
||||
use gimli::{self, write, Expression, Operation, Reader, ReaderOffset, Register, X86_64};
|
||||
use super::map_reg::map_reg;
|
||||
use anyhow::{Context, Error, Result};
|
||||
use gimli::{self, write, Expression, Operation, Reader, ReaderOffset, X86_64};
|
||||
use more_asserts::{assert_le, assert_lt};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use wasmtime_environ::entity::EntityRef;
|
||||
use wasmtime_environ::ir::{StackSlots, ValueLabel, ValueLabelsRanges, ValueLoc};
|
||||
use wasmtime_environ::isa::{RegUnit, TargetIsa};
|
||||
use wasmtime_environ::isa::TargetIsa;
|
||||
use wasmtime_environ::wasm::{get_vmctx_value_label, DefinedFuncIndex};
|
||||
use wasmtime_environ::ModuleMemoryOffset;
|
||||
|
||||
@@ -70,61 +71,6 @@ impl<'a> CompiledExpression<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn map_reg(isa: &dyn TargetIsa, reg: RegUnit) -> Result<Register> {
|
||||
// TODO avoid duplication with fde.rs
|
||||
assert!(isa.name() == "x86" && isa.pointer_bits() == 64);
|
||||
// Mapping from https://github.com/bytecodealliance/cranelift/pull/902 by @iximeow
|
||||
const X86_GP_REG_MAP: [gimli::Register; 16] = [
|
||||
X86_64::RAX,
|
||||
X86_64::RCX,
|
||||
X86_64::RDX,
|
||||
X86_64::RBX,
|
||||
X86_64::RSP,
|
||||
X86_64::RBP,
|
||||
X86_64::RSI,
|
||||
X86_64::RDI,
|
||||
X86_64::R8,
|
||||
X86_64::R9,
|
||||
X86_64::R10,
|
||||
X86_64::R11,
|
||||
X86_64::R12,
|
||||
X86_64::R13,
|
||||
X86_64::R14,
|
||||
X86_64::R15,
|
||||
];
|
||||
const X86_XMM_REG_MAP: [gimli::Register; 16] = [
|
||||
X86_64::XMM0,
|
||||
X86_64::XMM1,
|
||||
X86_64::XMM2,
|
||||
X86_64::XMM3,
|
||||
X86_64::XMM4,
|
||||
X86_64::XMM5,
|
||||
X86_64::XMM6,
|
||||
X86_64::XMM7,
|
||||
X86_64::XMM8,
|
||||
X86_64::XMM9,
|
||||
X86_64::XMM10,
|
||||
X86_64::XMM11,
|
||||
X86_64::XMM12,
|
||||
X86_64::XMM13,
|
||||
X86_64::XMM14,
|
||||
X86_64::XMM15,
|
||||
];
|
||||
let reg_info = isa.register_info();
|
||||
let bank = reg_info.bank_containing_regunit(reg).unwrap();
|
||||
match bank.name {
|
||||
"IntRegs" => {
|
||||
// x86 GP registers have a weird mapping to DWARF registers, so we use a
|
||||
// lookup table.
|
||||
Ok(X86_GP_REG_MAP[(reg - bank.first_unit) as usize])
|
||||
}
|
||||
"FloatRegs" => Ok(X86_XMM_REG_MAP[(reg - bank.first_unit) as usize]),
|
||||
bank_name => {
|
||||
bail!("unsupported register bank: {}", bank_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn translate_loc(
|
||||
loc: ValueLoc,
|
||||
frame_info: Option<&FunctionFrameInfo>,
|
||||
|
||||
58
crates/debug/src/transform/map_reg.rs
Normal file
58
crates/debug/src/transform/map_reg.rs
Normal file
@@ -0,0 +1,58 @@
|
||||
use anyhow::{bail, Result};
|
||||
use gimli::{Register, X86_64};
|
||||
use wasmtime_environ::isa::{RegUnit, TargetIsa};
|
||||
|
||||
pub(crate) fn map_reg(isa: &dyn TargetIsa, reg: RegUnit) -> Result<Register> {
|
||||
// TODO avoid duplication with fde.rs
|
||||
assert!(isa.name() == "x86" && isa.pointer_bits() == 64);
|
||||
// Mapping from https://github.com/bytecodealliance/cranelift/pull/902 by @iximeow
|
||||
const X86_GP_REG_MAP: [Register; 16] = [
|
||||
X86_64::RAX,
|
||||
X86_64::RCX,
|
||||
X86_64::RDX,
|
||||
X86_64::RBX,
|
||||
X86_64::RSP,
|
||||
X86_64::RBP,
|
||||
X86_64::RSI,
|
||||
X86_64::RDI,
|
||||
X86_64::R8,
|
||||
X86_64::R9,
|
||||
X86_64::R10,
|
||||
X86_64::R11,
|
||||
X86_64::R12,
|
||||
X86_64::R13,
|
||||
X86_64::R14,
|
||||
X86_64::R15,
|
||||
];
|
||||
const X86_XMM_REG_MAP: [Register; 16] = [
|
||||
X86_64::XMM0,
|
||||
X86_64::XMM1,
|
||||
X86_64::XMM2,
|
||||
X86_64::XMM3,
|
||||
X86_64::XMM4,
|
||||
X86_64::XMM5,
|
||||
X86_64::XMM6,
|
||||
X86_64::XMM7,
|
||||
X86_64::XMM8,
|
||||
X86_64::XMM9,
|
||||
X86_64::XMM10,
|
||||
X86_64::XMM11,
|
||||
X86_64::XMM12,
|
||||
X86_64::XMM13,
|
||||
X86_64::XMM14,
|
||||
X86_64::XMM15,
|
||||
];
|
||||
let reg_info = isa.register_info();
|
||||
let bank = reg_info.bank_containing_regunit(reg).unwrap();
|
||||
match bank.name {
|
||||
"IntRegs" => {
|
||||
// x86 GP registers have a weird mapping to DWARF registers, so we use a
|
||||
// lookup table.
|
||||
Ok(X86_GP_REG_MAP[(reg - bank.first_unit) as usize])
|
||||
}
|
||||
"FloatRegs" => Ok(X86_XMM_REG_MAP[(reg - bank.first_unit) as usize]),
|
||||
bank_name => {
|
||||
bail!("unsupported register bank: {}", bank_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,11 +14,13 @@ use wasmtime_environ::isa::TargetIsa;
|
||||
use wasmtime_environ::{ModuleAddressMap, ModuleVmctxInfo, ValueLabelsRanges};
|
||||
|
||||
pub use address_transform::AddressTransform;
|
||||
pub(crate) use map_reg::map_reg;
|
||||
|
||||
mod address_transform;
|
||||
mod attr;
|
||||
mod expression;
|
||||
mod line_program;
|
||||
mod map_reg;
|
||||
mod range_info_builder;
|
||||
mod refs;
|
||||
mod simulate;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use faerie::artifact::{Decl, SectionKind};
|
||||
use faerie::*;
|
||||
use gimli::write::{Address, Dwarf, EndianVec, Result, Sections, Writer};
|
||||
use gimli::write::{Address, DebugFrame, Dwarf, EndianVec, FrameTable, Result, Sections, Writer};
|
||||
use gimli::{RunTimeEndian, SectionId};
|
||||
|
||||
#[derive(Clone)]
|
||||
@@ -24,6 +24,7 @@ pub fn emit_dwarf(
|
||||
artifact: &mut Artifact,
|
||||
mut dwarf: Dwarf,
|
||||
symbol_resolver: &dyn SymbolResolver,
|
||||
frames: Option<FrameTable>,
|
||||
) -> anyhow::Result<()> {
|
||||
let endian = RunTimeEndian::Little;
|
||||
|
||||
@@ -52,6 +53,30 @@ pub fn emit_dwarf(
|
||||
}
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
if let Some(frames) = frames {
|
||||
let mut debug_frame = DebugFrame::from(WriterRelocate::new(endian, symbol_resolver));
|
||||
frames.write_debug_frame(&mut debug_frame).unwrap();
|
||||
artifact.declare_with(
|
||||
SectionId::DebugFrame.name(),
|
||||
Decl::section(SectionKind::Debug),
|
||||
debug_frame.writer.take(),
|
||||
)?;
|
||||
for reloc in &debug_frame.relocs {
|
||||
artifact.link_with(
|
||||
faerie::Link {
|
||||
from: SectionId::DebugFrame.name(),
|
||||
to: &reloc.name,
|
||||
at: u64::from(reloc.offset),
|
||||
},
|
||||
faerie::Reloc::Debug {
|
||||
size: reloc.size,
|
||||
addend: reloc.addend as i32,
|
||||
},
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user