Write .debug_frame information (#53)
* Write .debug_frame information * mv map_reg
This commit is contained in:
@@ -6,8 +6,12 @@ use std::boxed::Box;
|
|||||||
|
|
||||||
use crate::HashMap;
|
use crate::HashMap;
|
||||||
|
|
||||||
|
#[cfg(feature = "enable-serde")]
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
/// Change in the frame layout information.
|
/// Change in the frame layout information.
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
|
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
|
||||||
|
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
|
||||||
pub enum FrameLayoutChange {
|
pub enum FrameLayoutChange {
|
||||||
/// Base CallFrameAddress (CFA) pointer moved to different register/offset.
|
/// Base CallFrameAddress (CFA) pointer moved to different register/offset.
|
||||||
CallFrameAddressAt {
|
CallFrameAddressAt {
|
||||||
|
|||||||
@@ -4,8 +4,12 @@ use core::fmt;
|
|||||||
use core::str;
|
use core::str;
|
||||||
use target_lexicon::{CallingConvention, Triple};
|
use target_lexicon::{CallingConvention, Triple};
|
||||||
|
|
||||||
|
#[cfg(feature = "enable-serde")]
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
/// Calling convention identifiers.
|
/// Calling convention identifiers.
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||||
|
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
|
||||||
pub enum CallConv {
|
pub enum CallConv {
|
||||||
/// Best performance, not ABI-stable
|
/// Best performance, not ABI-stable
|
||||||
Fast,
|
Fast,
|
||||||
|
|||||||
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)]
|
#![allow(clippy::cast_ptr_alignment)]
|
||||||
|
|
||||||
|
use crate::frame::get_debug_frame_bytes;
|
||||||
use anyhow::Error;
|
use anyhow::Error;
|
||||||
use faerie::{Artifact, Decl};
|
use faerie::{Artifact, Decl};
|
||||||
use more_asserts::assert_gt;
|
use more_asserts::assert_gt;
|
||||||
use target_lexicon::BinaryFormat;
|
use target_lexicon::BinaryFormat;
|
||||||
use wasmtime_environ::isa::TargetIsa;
|
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::read_debuginfo::{read_debuginfo, DebugInfoData, WasmFileInfo};
|
||||||
pub use crate::transform::transform_dwarf;
|
pub use crate::transform::transform_dwarf;
|
||||||
pub use crate::write_debuginfo::{emit_dwarf, ResolvedSymbol, SymbolResolver};
|
pub use crate::write_debuginfo::{emit_dwarf, ResolvedSymbol, SymbolResolver};
|
||||||
|
|
||||||
|
mod frame;
|
||||||
mod gc;
|
mod gc;
|
||||||
mod read_debuginfo;
|
mod read_debuginfo;
|
||||||
mod transform;
|
mod transform;
|
||||||
@@ -33,10 +35,21 @@ pub fn emit_debugsections(
|
|||||||
debuginfo_data: &DebugInfoData,
|
debuginfo_data: &DebugInfoData,
|
||||||
at: &ModuleAddressMap,
|
at: &ModuleAddressMap,
|
||||||
ranges: &ValueLabelsRanges,
|
ranges: &ValueLabelsRanges,
|
||||||
|
frame_layouts: &FrameLayouts,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let resolver = FunctionRelocResolver {};
|
let resolver = FunctionRelocResolver {};
|
||||||
let dwarf = transform_dwarf(isa, debuginfo_data, at, vmctx_info, ranges)?;
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,6 +70,7 @@ pub fn emit_debugsections_image(
|
|||||||
vmctx_info: &ModuleVmctxInfo,
|
vmctx_info: &ModuleVmctxInfo,
|
||||||
at: &ModuleAddressMap,
|
at: &ModuleAddressMap,
|
||||||
ranges: &ValueLabelsRanges,
|
ranges: &ValueLabelsRanges,
|
||||||
|
frame_layouts: &FrameLayouts,
|
||||||
funcs: &[(*const u8, usize)],
|
funcs: &[(*const u8, usize)],
|
||||||
) -> Result<Vec<u8>, Error> {
|
) -> Result<Vec<u8>, Error> {
|
||||||
let func_offsets = &funcs
|
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) };
|
let body = unsafe { std::slice::from_raw_parts(segment_body.0, segment_body.1) };
|
||||||
obj.declare_with("all", Decl::function(), body.to_vec())?;
|
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
|
// LLDB is too "magical" about mach-o, generating elf
|
||||||
let mut bytes = obj.emit_as(BinaryFormat::Elf)?;
|
let mut bytes = obj.emit_as(BinaryFormat::Elf)?;
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
use super::address_transform::AddressTransform;
|
use super::address_transform::AddressTransform;
|
||||||
use anyhow::{bail, Context, Error, Result};
|
use super::map_reg::map_reg;
|
||||||
use gimli::{self, write, Expression, Operation, Reader, ReaderOffset, Register, X86_64};
|
use anyhow::{Context, Error, Result};
|
||||||
|
use gimli::{self, write, Expression, Operation, Reader, ReaderOffset, X86_64};
|
||||||
use more_asserts::{assert_le, assert_lt};
|
use more_asserts::{assert_le, assert_lt};
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
use wasmtime_environ::entity::EntityRef;
|
use wasmtime_environ::entity::EntityRef;
|
||||||
use wasmtime_environ::ir::{StackSlots, ValueLabel, ValueLabelsRanges, ValueLoc};
|
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::wasm::{get_vmctx_value_label, DefinedFuncIndex};
|
||||||
use wasmtime_environ::ModuleMemoryOffset;
|
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(
|
fn translate_loc(
|
||||||
loc: ValueLoc,
|
loc: ValueLoc,
|
||||||
frame_info: Option<&FunctionFrameInfo>,
|
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};
|
use wasmtime_environ::{ModuleAddressMap, ModuleVmctxInfo, ValueLabelsRanges};
|
||||||
|
|
||||||
pub use address_transform::AddressTransform;
|
pub use address_transform::AddressTransform;
|
||||||
|
pub(crate) use map_reg::map_reg;
|
||||||
|
|
||||||
mod address_transform;
|
mod address_transform;
|
||||||
mod attr;
|
mod attr;
|
||||||
mod expression;
|
mod expression;
|
||||||
mod line_program;
|
mod line_program;
|
||||||
|
mod map_reg;
|
||||||
mod range_info_builder;
|
mod range_info_builder;
|
||||||
mod refs;
|
mod refs;
|
||||||
mod simulate;
|
mod simulate;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use faerie::artifact::{Decl, SectionKind};
|
use faerie::artifact::{Decl, SectionKind};
|
||||||
use faerie::*;
|
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};
|
use gimli::{RunTimeEndian, SectionId};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@@ -24,6 +24,7 @@ pub fn emit_dwarf(
|
|||||||
artifact: &mut Artifact,
|
artifact: &mut Artifact,
|
||||||
mut dwarf: Dwarf,
|
mut dwarf: Dwarf,
|
||||||
symbol_resolver: &dyn SymbolResolver,
|
symbol_resolver: &dyn SymbolResolver,
|
||||||
|
frames: Option<FrameTable>,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
let endian = RunTimeEndian::Little;
|
let endian = RunTimeEndian::Little;
|
||||||
|
|
||||||
@@ -52,6 +53,30 @@ pub fn emit_dwarf(
|
|||||||
}
|
}
|
||||||
Ok(())
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
use crate::address_map::{ModuleAddressMap, ValueLabelsRanges};
|
use crate::address_map::{ModuleAddressMap, ValueLabelsRanges};
|
||||||
use crate::compilation::{Compilation, Relocations, Traps};
|
use crate::compilation::{Compilation, Relocations, Traps};
|
||||||
|
use crate::frame_layout::FrameLayouts;
|
||||||
use cranelift_codegen::ir;
|
use cranelift_codegen::ir;
|
||||||
use cranelift_entity::PrimaryMap;
|
use cranelift_entity::PrimaryMap;
|
||||||
use cranelift_wasm::DefinedFuncIndex;
|
use cranelift_wasm::DefinedFuncIndex;
|
||||||
@@ -35,6 +36,7 @@ pub struct ModuleCacheData {
|
|||||||
value_ranges: ValueLabelsRanges,
|
value_ranges: ValueLabelsRanges,
|
||||||
stack_slots: PrimaryMap<DefinedFuncIndex, ir::StackSlots>,
|
stack_slots: PrimaryMap<DefinedFuncIndex, ir::StackSlots>,
|
||||||
traps: Traps,
|
traps: Traps,
|
||||||
|
frame_layouts: FrameLayouts,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A type alias over the module cache data as a tuple.
|
/// A type alias over the module cache data as a tuple.
|
||||||
@@ -45,6 +47,7 @@ pub type ModuleCacheDataTupleType = (
|
|||||||
ValueLabelsRanges,
|
ValueLabelsRanges,
|
||||||
PrimaryMap<DefinedFuncIndex, ir::StackSlots>,
|
PrimaryMap<DefinedFuncIndex, ir::StackSlots>,
|
||||||
Traps,
|
Traps,
|
||||||
|
FrameLayouts,
|
||||||
);
|
);
|
||||||
|
|
||||||
struct Sha256Hasher(Sha256);
|
struct Sha256Hasher(Sha256);
|
||||||
@@ -204,6 +207,7 @@ impl ModuleCacheData {
|
|||||||
value_ranges: data.3,
|
value_ranges: data.3,
|
||||||
stack_slots: data.4,
|
stack_slots: data.4,
|
||||||
traps: data.5,
|
traps: data.5,
|
||||||
|
frame_layouts: data.6,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -215,6 +219,7 @@ impl ModuleCacheData {
|
|||||||
self.value_ranges,
|
self.value_ranges,
|
||||||
self.stack_slots,
|
self.stack_slots,
|
||||||
self.traps,
|
self.traps,
|
||||||
|
self.frame_layouts,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
1
crates/environ/src/cache/tests.rs
vendored
1
crates/environ/src/cache/tests.rs
vendored
@@ -100,5 +100,6 @@ fn new_module_cache_data() -> Result<ModuleCacheDataTupleType, ()> {
|
|||||||
PrimaryMap::new(),
|
PrimaryMap::new(),
|
||||||
PrimaryMap::new(),
|
PrimaryMap::new(),
|
||||||
PrimaryMap::new(),
|
PrimaryMap::new(),
|
||||||
|
PrimaryMap::new(),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ use crate::compilation::{
|
|||||||
Compilation, CompileError, CompiledFunction, CompiledFunctionUnwindInfo, Relocation,
|
Compilation, CompileError, CompiledFunction, CompiledFunctionUnwindInfo, Relocation,
|
||||||
RelocationTarget, TrapInformation,
|
RelocationTarget, TrapInformation,
|
||||||
};
|
};
|
||||||
|
use crate::frame_layout::FrameLayout;
|
||||||
use crate::func_environ::{get_func_name, FuncEnvironment};
|
use crate::func_environ::{get_func_name, FuncEnvironment};
|
||||||
use crate::module::{Module, ModuleLocal};
|
use crate::module::{Module, ModuleLocal};
|
||||||
use crate::module_environ::FunctionBodyData;
|
use crate::module_environ::FunctionBodyData;
|
||||||
@@ -154,6 +155,38 @@ fn get_function_address_map<'data>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_frame_layout(
|
||||||
|
context: &Context,
|
||||||
|
isa: &dyn isa::TargetIsa,
|
||||||
|
) -> (
|
||||||
|
Box<[ir::FrameLayoutChange]>,
|
||||||
|
Box<[(usize, ir::FrameLayoutChange)]>,
|
||||||
|
) {
|
||||||
|
let func = &context.func;
|
||||||
|
assert!(func.frame_layout.is_some(), "expected func.frame_layout");
|
||||||
|
|
||||||
|
let mut blocks = func.layout.blocks().collect::<Vec<_>>();
|
||||||
|
blocks.sort_by_key(|b| func.offsets[*b]); // Ensure inst offsets always increase
|
||||||
|
|
||||||
|
let encinfo = isa.encoding_info();
|
||||||
|
let mut last_offset = 0;
|
||||||
|
let mut commands = Vec::new();
|
||||||
|
for b in blocks {
|
||||||
|
for (offset, inst, size) in func.inst_offsets(b, &encinfo) {
|
||||||
|
if let Some(cmds) = func.frame_layout.as_ref().unwrap().instructions.get(&inst) {
|
||||||
|
let address_offset = (offset + size) as usize;
|
||||||
|
assert!(last_offset < address_offset);
|
||||||
|
for cmd in cmds.iter() {
|
||||||
|
commands.push((address_offset, cmd.clone()));
|
||||||
|
}
|
||||||
|
last_offset = address_offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let initial = func.frame_layout.as_ref().unwrap().initial.clone();
|
||||||
|
(initial, commands.into_boxed_slice())
|
||||||
|
}
|
||||||
|
|
||||||
/// A compiler that compiles a WebAssembly module with Cranelift, translating the Wasm to Cranelift IR,
|
/// A compiler that compiles a WebAssembly module with Cranelift, translating the Wasm to Cranelift IR,
|
||||||
/// optimizing it and then translating to assembly.
|
/// optimizing it and then translating to assembly.
|
||||||
pub struct Cranelift;
|
pub struct Cranelift;
|
||||||
@@ -206,6 +239,7 @@ fn compile(
|
|||||||
let mut value_ranges = PrimaryMap::with_capacity(function_body_inputs.len());
|
let mut value_ranges = PrimaryMap::with_capacity(function_body_inputs.len());
|
||||||
let mut stack_slots = PrimaryMap::with_capacity(function_body_inputs.len());
|
let mut stack_slots = PrimaryMap::with_capacity(function_body_inputs.len());
|
||||||
let mut traps = PrimaryMap::with_capacity(function_body_inputs.len());
|
let mut traps = PrimaryMap::with_capacity(function_body_inputs.len());
|
||||||
|
let mut frame_layouts = PrimaryMap::with_capacity(function_body_inputs.len());
|
||||||
|
|
||||||
function_body_inputs
|
function_body_inputs
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@@ -254,6 +288,17 @@ fn compile(
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let frame_layout = if generate_debug_info {
|
||||||
|
let (initial_commands, commands) = get_frame_layout(&context, isa);
|
||||||
|
Some(FrameLayout {
|
||||||
|
call_conv: context.func.signature.call_conv,
|
||||||
|
initial_commands,
|
||||||
|
commands,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
let ranges = if generate_debug_info {
|
let ranges = if generate_debug_info {
|
||||||
let ranges = context.build_value_labels_ranges(isa).map_err(|error| {
|
let ranges = context.build_value_labels_ranges(isa).map_err(|error| {
|
||||||
CompileError::Codegen(pretty_error(&context.func, Some(isa), error))
|
CompileError::Codegen(pretty_error(&context.func, Some(isa), error))
|
||||||
@@ -268,6 +313,7 @@ fn compile(
|
|||||||
context.func.jt_offsets,
|
context.func.jt_offsets,
|
||||||
reloc_sink.func_relocs,
|
reloc_sink.func_relocs,
|
||||||
address_transform,
|
address_transform,
|
||||||
|
frame_layout,
|
||||||
ranges,
|
ranges,
|
||||||
context.func.stack_slots,
|
context.func.stack_slots,
|
||||||
trap_sink.traps,
|
trap_sink.traps,
|
||||||
@@ -282,6 +328,7 @@ fn compile(
|
|||||||
func_jt_offsets,
|
func_jt_offsets,
|
||||||
relocs,
|
relocs,
|
||||||
address_transform,
|
address_transform,
|
||||||
|
frame_layout,
|
||||||
ranges,
|
ranges,
|
||||||
sss,
|
sss,
|
||||||
function_traps,
|
function_traps,
|
||||||
@@ -299,6 +346,9 @@ fn compile(
|
|||||||
value_ranges.push(ranges.unwrap_or_default());
|
value_ranges.push(ranges.unwrap_or_default());
|
||||||
stack_slots.push(sss);
|
stack_slots.push(sss);
|
||||||
traps.push(function_traps);
|
traps.push(function_traps);
|
||||||
|
if let Some(frame_layout) = frame_layout {
|
||||||
|
frame_layouts.push(frame_layout);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -311,6 +361,7 @@ fn compile(
|
|||||||
value_ranges,
|
value_ranges,
|
||||||
stack_slots,
|
stack_slots,
|
||||||
traps,
|
traps,
|
||||||
|
frame_layouts,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
21
crates/environ/src/frame_layout.rs
Normal file
21
crates/environ/src/frame_layout.rs
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
use cranelift_codegen::isa::CallConv;
|
||||||
|
use cranelift_entity::PrimaryMap;
|
||||||
|
use cranelift_wasm::DefinedFuncIndex;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
pub use cranelift_codegen::ir::FrameLayoutChange;
|
||||||
|
|
||||||
|
/// Frame layout information: call convention and
|
||||||
|
/// registers save/restore commands.
|
||||||
|
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
|
||||||
|
pub struct FrameLayout {
|
||||||
|
/// Call convention.
|
||||||
|
pub call_conv: CallConv,
|
||||||
|
/// Frame default/initial commands.
|
||||||
|
pub initial_commands: Box<[FrameLayoutChange]>,
|
||||||
|
/// Frame commands at specific offset.
|
||||||
|
pub commands: Box<[(usize, FrameLayoutChange)]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Functions frame layouts.
|
||||||
|
pub type FrameLayouts = PrimaryMap<DefinedFuncIndex, FrameLayout>;
|
||||||
@@ -27,6 +27,7 @@
|
|||||||
mod address_map;
|
mod address_map;
|
||||||
mod compilation;
|
mod compilation;
|
||||||
mod data_structures;
|
mod data_structures;
|
||||||
|
mod frame_layout;
|
||||||
mod func_environ;
|
mod func_environ;
|
||||||
mod module;
|
mod module;
|
||||||
mod module_environ;
|
mod module_environ;
|
||||||
@@ -52,6 +53,7 @@ pub use crate::compilation::{
|
|||||||
};
|
};
|
||||||
pub use crate::cranelift::Cranelift;
|
pub use crate::cranelift::Cranelift;
|
||||||
pub use crate::data_structures::*;
|
pub use crate::data_structures::*;
|
||||||
|
pub use crate::frame_layout::{FrameLayout, FrameLayoutChange, FrameLayouts};
|
||||||
pub use crate::func_environ::BuiltinFunctionIndex;
|
pub use crate::func_environ::BuiltinFunctionIndex;
|
||||||
#[cfg(feature = "lightbeam")]
|
#[cfg(feature = "lightbeam")]
|
||||||
pub use crate::lightbeam::Lightbeam;
|
pub use crate::lightbeam::Lightbeam;
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ impl crate::compilation::Compiler for Lightbeam {
|
|||||||
ValueLabelsRanges::new(),
|
ValueLabelsRanges::new(),
|
||||||
PrimaryMap::new(),
|
PrimaryMap::new(),
|
||||||
Traps::new(),
|
Traps::new(),
|
||||||
|
PrimaryMap::new(),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -110,33 +110,40 @@ impl Compiler {
|
|||||||
),
|
),
|
||||||
SetupError,
|
SetupError,
|
||||||
> {
|
> {
|
||||||
let (compilation, relocations, address_transform, value_ranges, stack_slots, traps) =
|
let (
|
||||||
match self.strategy {
|
compilation,
|
||||||
// For now, interpret `Auto` as `Cranelift` since that's the most stable
|
relocations,
|
||||||
// implementation.
|
address_transform,
|
||||||
CompilationStrategy::Auto | CompilationStrategy::Cranelift => {
|
value_ranges,
|
||||||
wasmtime_environ::cranelift::Cranelift::compile_module(
|
stack_slots,
|
||||||
module,
|
traps,
|
||||||
module_translation,
|
frame_layouts,
|
||||||
function_body_inputs,
|
) = match self.strategy {
|
||||||
&*self.isa,
|
// For now, interpret `Auto` as `Cranelift` since that's the most stable
|
||||||
debug_data.is_some(),
|
// implementation.
|
||||||
&self.cache_config,
|
CompilationStrategy::Auto | CompilationStrategy::Cranelift => {
|
||||||
)
|
wasmtime_environ::cranelift::Cranelift::compile_module(
|
||||||
}
|
module,
|
||||||
#[cfg(feature = "lightbeam")]
|
module_translation,
|
||||||
CompilationStrategy::Lightbeam => {
|
function_body_inputs,
|
||||||
wasmtime_environ::lightbeam::Lightbeam::compile_module(
|
&*self.isa,
|
||||||
module,
|
debug_data.is_some(),
|
||||||
module_translation,
|
&self.cache_config,
|
||||||
function_body_inputs,
|
)
|
||||||
&*self.isa,
|
|
||||||
debug_data.is_some(),
|
|
||||||
&self.cache_config,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.map_err(SetupError::Compile)?;
|
#[cfg(feature = "lightbeam")]
|
||||||
|
CompilationStrategy::Lightbeam => {
|
||||||
|
wasmtime_environ::lightbeam::Lightbeam::compile_module(
|
||||||
|
module,
|
||||||
|
module_translation,
|
||||||
|
function_body_inputs,
|
||||||
|
&*self.isa,
|
||||||
|
debug_data.is_some(),
|
||||||
|
&self.cache_config,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.map_err(SetupError::Compile)?;
|
||||||
|
|
||||||
let allocated_functions =
|
let allocated_functions =
|
||||||
allocate_functions(&mut self.code_memory, &compilation).map_err(|message| {
|
allocate_functions(&mut self.code_memory, &compilation).map_err(|message| {
|
||||||
@@ -179,6 +186,7 @@ impl Compiler {
|
|||||||
&module_vmctx_info,
|
&module_vmctx_info,
|
||||||
&address_transform,
|
&address_transform,
|
||||||
&value_ranges,
|
&value_ranges,
|
||||||
|
&frame_layouts,
|
||||||
&funcs,
|
&funcs,
|
||||||
)
|
)
|
||||||
.map_err(SetupError::DebugInfo)?;
|
.map_err(SetupError::DebugInfo)?;
|
||||||
|
|||||||
56
src/obj.rs
56
src/obj.rs
@@ -79,30 +79,37 @@ pub fn compile_to_obj(
|
|||||||
};
|
};
|
||||||
|
|
||||||
// TODO: use the traps information
|
// TODO: use the traps information
|
||||||
let (compilation, relocations, address_transform, value_ranges, stack_slots, _traps) =
|
let (
|
||||||
match strategy {
|
compilation,
|
||||||
Strategy::Auto | Strategy::Cranelift => Cranelift::compile_module(
|
relocations,
|
||||||
&module,
|
address_transform,
|
||||||
&module_translation,
|
value_ranges,
|
||||||
lazy_function_body_inputs,
|
stack_slots,
|
||||||
&*isa,
|
_traps,
|
||||||
debug_info,
|
frame_layouts,
|
||||||
cache_config,
|
) = match strategy {
|
||||||
),
|
Strategy::Auto | Strategy::Cranelift => Cranelift::compile_module(
|
||||||
#[cfg(feature = "lightbeam")]
|
&module,
|
||||||
Strategy::Lightbeam => Lightbeam::compile_module(
|
&module_translation,
|
||||||
&module,
|
lazy_function_body_inputs,
|
||||||
&module_translation,
|
&*isa,
|
||||||
lazy_function_body_inputs,
|
debug_info,
|
||||||
&*isa,
|
cache_config,
|
||||||
debug_info,
|
),
|
||||||
cache_config,
|
#[cfg(feature = "lightbeam")]
|
||||||
),
|
Strategy::Lightbeam => Lightbeam::compile_module(
|
||||||
#[cfg(not(feature = "lightbeam"))]
|
&module,
|
||||||
Strategy::Lightbeam => bail!("lightbeam support not enabled"),
|
&module_translation,
|
||||||
other => bail!("unsupported compilation strategy {:?}", other),
|
lazy_function_body_inputs,
|
||||||
}
|
&*isa,
|
||||||
.context("failed to compile module")?;
|
debug_info,
|
||||||
|
cache_config,
|
||||||
|
),
|
||||||
|
#[cfg(not(feature = "lightbeam"))]
|
||||||
|
Strategy::Lightbeam => bail!("lightbeam support not enabled"),
|
||||||
|
other => bail!("unsupported compilation strategy {:?}", other),
|
||||||
|
}
|
||||||
|
.context("failed to compile module")?;
|
||||||
|
|
||||||
if compilation.is_empty() {
|
if compilation.is_empty() {
|
||||||
bail!("no functions were found/compiled");
|
bail!("no functions were found/compiled");
|
||||||
@@ -144,6 +151,7 @@ pub fn compile_to_obj(
|
|||||||
&debug_data,
|
&debug_data,
|
||||||
&address_transform,
|
&address_transform,
|
||||||
&value_ranges,
|
&value_ranges,
|
||||||
|
&frame_layouts,
|
||||||
)
|
)
|
||||||
.context("failed to emit debug sections")?;
|
.context("failed to emit debug sections")?;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user