Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Write .debug_frame information #53

Merged
merged 3 commits into from
Mar 11, 2020
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions cranelift/codegen/src/ir/framelayout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,12 @@ use std::boxed::Box;

use crate::HashMap;

#[cfg(feature = "enable-serde")]
use serde::{Deserialize, Serialize};

/// Change in the frame layout information.
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub enum FrameLayoutChange {
/// Base CallFrameAddress (CFA) pointer moved to different register/offset.
CallFrameAddressAt {
Expand Down
4 changes: 4 additions & 0 deletions cranelift/codegen/src/isa/call_conv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,12 @@ use core::fmt;
use core::str;
use target_lexicon::{CallingConvention, Triple};

#[cfg(feature = "enable-serde")]
use serde::{Deserialize, Serialize};

/// Calling convention identifiers.
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub enum CallConv {
/// Best performance, not ABI-stable
Fast,
Expand Down
140 changes: 140 additions & 0 deletions crates/debug/src/frame.rs
Original file line number Diff line number Diff line change
@@ -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 process only functions with SystemV-like prologue.
yurydelendik marked this conversation as resolved.
Show resolved Hide resolved
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,
yurydelendik marked this conversation as resolved.
Show resolved Hide resolved
/* 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))
}
21 changes: 18 additions & 3 deletions crates/debug/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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(())
}

Expand All @@ -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
Expand All @@ -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)?;
Expand Down
62 changes: 4 additions & 58 deletions crates/debug/src/transform/expression.rs
Original file line number Diff line number Diff line change
@@ -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;

Expand Down Expand Up @@ -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>,
Expand Down
58 changes: 58 additions & 0 deletions crates/debug/src/transform/map_reg.rs
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
2 changes: 2 additions & 0 deletions crates/debug/src/transform/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Loading