Initial reorg.

This is largely the same as #305, but updated for the current tree.
This commit is contained in:
Dan Gohman
2019-11-07 17:11:06 -08:00
parent 2c69546a24
commit 22641de629
351 changed files with 52 additions and 52 deletions

View File

@@ -0,0 +1,658 @@
use crate::HashMap;
use crate::WasmFileInfo;
use alloc::boxed::Box;
use alloc::collections::BTreeMap;
use alloc::vec::Vec;
use core::iter::FromIterator;
use cranelift_codegen::ir::SourceLoc;
use cranelift_entity::{EntityRef, PrimaryMap};
use cranelift_wasm::DefinedFuncIndex;
use gimli::write;
use wasmtime_environ::{FunctionAddressMap, ModuleAddressMap};
pub type GeneratedAddress = usize;
pub type WasmAddress = u64;
/// Contains mapping of the generated address to its original
/// source location.
#[derive(Debug)]
pub struct AddressMap {
pub generated: GeneratedAddress,
pub wasm: WasmAddress,
}
/// Information about generated function code: its body start,
/// length, and instructions addresses.
#[derive(Debug)]
pub struct FunctionMap {
pub offset: GeneratedAddress,
pub len: GeneratedAddress,
pub wasm_start: WasmAddress,
pub wasm_end: WasmAddress,
pub addresses: Box<[AddressMap]>,
}
/// Mapping of the source location to its generated code range.
#[derive(Debug)]
struct Position {
wasm_pos: WasmAddress,
gen_start: GeneratedAddress,
gen_end: GeneratedAddress,
}
/// Mapping of continuous range of source location to its generated
/// code. The positions are always in accending order for search.
#[derive(Debug)]
struct Range {
wasm_start: WasmAddress,
wasm_end: WasmAddress,
gen_start: GeneratedAddress,
gen_end: GeneratedAddress,
positions: Box<[Position]>,
}
/// Helper function address lookup data. Contains ranges start positions
/// index and ranges data. The multiple ranges can include the same
/// original source position. The index (B-Tree) uses range start
/// position as a key.
#[derive(Debug)]
struct FuncLookup {
index: Vec<(WasmAddress, Box<[usize]>)>,
ranges: Box<[Range]>,
}
/// Mapping of original functions to generated code locations/ranges.
#[derive(Debug)]
struct FuncTransform {
start: WasmAddress,
end: WasmAddress,
index: DefinedFuncIndex,
lookup: FuncLookup,
}
/// Module functions mapping to generated code.
#[derive(Debug)]
pub struct AddressTransform {
map: PrimaryMap<DefinedFuncIndex, FunctionMap>,
func: Vec<(WasmAddress, FuncTransform)>,
}
/// Returns a wasm bytecode offset in the code section from SourceLoc.
pub fn get_wasm_code_offset(loc: SourceLoc, code_section_offset: u64) -> WasmAddress {
// Code section size <= 4GB, allow wrapped SourceLoc to recover the overflow.
loc.bits().wrapping_sub(code_section_offset as u32) as WasmAddress
}
fn build_function_lookup(
ft: &FunctionAddressMap,
code_section_offset: u64,
) -> (WasmAddress, WasmAddress, FuncLookup) {
assert!(code_section_offset <= ft.start_srcloc.bits() as u64);
let fn_start = get_wasm_code_offset(ft.start_srcloc, code_section_offset);
let fn_end = get_wasm_code_offset(ft.end_srcloc, code_section_offset);
assert!(fn_start <= fn_end);
// Build ranges of continuous source locations. The new ranges starts when
// non-descending order is interrupted. Assuming the same origin location can
// be present in multiple ranges.
let mut range_wasm_start = fn_start;
let mut range_gen_start = ft.body_offset;
let mut last_wasm_pos = range_wasm_start;
let mut ranges = Vec::new();
let mut ranges_index = BTreeMap::new();
let mut current_range = Vec::new();
for t in &ft.instructions {
if t.srcloc.is_default() {
continue;
}
let offset = get_wasm_code_offset(t.srcloc, code_section_offset);
assert!(fn_start <= offset && offset <= fn_end);
let inst_gen_start = t.code_offset;
let inst_gen_end = t.code_offset + t.code_len;
if last_wasm_pos > offset {
// Start new range.
ranges_index.insert(range_wasm_start, ranges.len());
ranges.push(Range {
wasm_start: range_wasm_start,
wasm_end: last_wasm_pos,
gen_start: range_gen_start,
gen_end: inst_gen_start,
positions: current_range.into_boxed_slice(),
});
range_wasm_start = offset;
range_gen_start = inst_gen_start;
current_range = Vec::new();
}
// Continue existing range: add new wasm->generated code position.
current_range.push(Position {
wasm_pos: offset,
gen_start: inst_gen_start,
gen_end: inst_gen_end,
});
last_wasm_pos = offset;
}
let last_gen_addr = ft.body_offset + ft.body_len;
ranges_index.insert(range_wasm_start, ranges.len());
ranges.push(Range {
wasm_start: range_wasm_start,
wasm_end: fn_end,
gen_start: range_gen_start,
gen_end: last_gen_addr,
positions: current_range.into_boxed_slice(),
});
// Making ranges lookup faster by building index: B-tree with every range
// start position that maps into list of active ranges at this position.
let ranges = ranges.into_boxed_slice();
let mut active_ranges = Vec::new();
let mut index = BTreeMap::new();
let mut last_wasm_pos = None;
for (wasm_start, range_index) in ranges_index {
if Some(wasm_start) == last_wasm_pos {
active_ranges.push(range_index);
continue;
}
if last_wasm_pos.is_some() {
index.insert(
last_wasm_pos.unwrap(),
active_ranges.clone().into_boxed_slice(),
);
}
active_ranges.retain(|r| ranges[*r].wasm_end.cmp(&wasm_start) != core::cmp::Ordering::Less);
active_ranges.push(range_index);
last_wasm_pos = Some(wasm_start);
}
index.insert(last_wasm_pos.unwrap(), active_ranges.into_boxed_slice());
let index = Vec::from_iter(index.into_iter());
(fn_start, fn_end, FuncLookup { index, ranges })
}
fn build_function_addr_map(
at: &ModuleAddressMap,
code_section_offset: u64,
) -> PrimaryMap<DefinedFuncIndex, FunctionMap> {
let mut map = PrimaryMap::new();
for (_, ft) in at {
let mut fn_map = Vec::new();
for t in &ft.instructions {
if t.srcloc.is_default() {
continue;
}
let offset = get_wasm_code_offset(t.srcloc, code_section_offset);
fn_map.push(AddressMap {
generated: t.code_offset,
wasm: offset,
});
}
if cfg!(debug) {
// fn_map is sorted by the generated field -- see FunctionAddressMap::instructions.
for i in 1..fn_map.len() {
assert!(fn_map[i - 1].generated <= fn_map[i].generated);
}
}
map.push(FunctionMap {
offset: ft.body_offset,
len: ft.body_len,
wasm_start: get_wasm_code_offset(ft.start_srcloc, code_section_offset),
wasm_end: get_wasm_code_offset(ft.end_srcloc, code_section_offset),
addresses: fn_map.into_boxed_slice(),
});
}
map
}
struct TransformRangeIter<'a> {
addr: u64,
indicies: &'a [usize],
ranges: &'a [Range],
}
impl<'a> TransformRangeIter<'a> {
fn new(func: &'a FuncTransform, addr: u64) -> Self {
let found = match func
.lookup
.index
.binary_search_by(|entry| entry.0.cmp(&addr))
{
Ok(i) => Some(&func.lookup.index[i].1),
Err(i) => {
if i > 0 {
Some(&func.lookup.index[i - 1].1)
} else {
None
}
}
};
if let Some(range_indices) = found {
TransformRangeIter {
addr,
indicies: range_indices,
ranges: &func.lookup.ranges,
}
} else {
unreachable!();
}
}
}
impl<'a> Iterator for TransformRangeIter<'a> {
type Item = (usize, usize);
fn next(&mut self) -> Option<Self::Item> {
if let Some((first, tail)) = self.indicies.split_first() {
let range_index = *first;
let range = &self.ranges[range_index];
self.indicies = tail;
let address = match range
.positions
.binary_search_by(|a| a.wasm_pos.cmp(&self.addr))
{
Ok(i) => range.positions[i].gen_start,
Err(i) => {
if i == 0 {
range.gen_start
} else {
range.positions[i - 1].gen_end
}
}
};
Some((address, range_index))
} else {
None
}
}
}
struct TransformRangeEndIter<'a> {
addr: u64,
indicies: &'a [usize],
ranges: &'a [Range],
}
impl<'a> TransformRangeEndIter<'a> {
fn new(func: &'a FuncTransform, addr: u64) -> Self {
let found = match func
.lookup
.index
.binary_search_by(|entry| entry.0.cmp(&addr))
{
Ok(i) => Some(&func.lookup.index[i].1),
Err(i) => {
if i > 0 {
Some(&func.lookup.index[i - 1].1)
} else {
None
}
}
};
if let Some(range_indices) = found {
TransformRangeEndIter {
addr,
indicies: range_indices,
ranges: &func.lookup.ranges,
}
} else {
unreachable!();
}
}
}
impl<'a> Iterator for TransformRangeEndIter<'a> {
type Item = (usize, usize);
fn next(&mut self) -> Option<Self::Item> {
while let Some((first, tail)) = self.indicies.split_first() {
let range_index = *first;
let range = &self.ranges[range_index];
if range.wasm_start >= self.addr {
continue;
}
self.indicies = tail;
let address = match range
.positions
.binary_search_by(|a| a.wasm_pos.cmp(&self.addr))
{
Ok(i) => range.positions[i].gen_end,
Err(i) => {
if i == range.positions.len() {
range.gen_end
} else {
range.positions[i].gen_start
}
}
};
return Some((address, range_index));
}
None
}
}
impl AddressTransform {
pub fn new(at: &ModuleAddressMap, wasm_file: &WasmFileInfo) -> Self {
let code_section_offset = wasm_file.code_section_offset;
let mut func = BTreeMap::new();
for (i, ft) in at {
let (fn_start, fn_end, lookup) = build_function_lookup(ft, code_section_offset);
func.insert(
fn_start,
FuncTransform {
start: fn_start,
end: fn_end,
index: i,
lookup,
},
);
}
let map = build_function_addr_map(at, code_section_offset);
let func = Vec::from_iter(func.into_iter());
AddressTransform { map, func }
}
fn find_func(&self, addr: u64) -> Option<&FuncTransform> {
// TODO check if we need to include end address
let func = match self.func.binary_search_by(|entry| entry.0.cmp(&addr)) {
Ok(i) => &self.func[i].1,
Err(i) => {
if i > 0 {
&self.func[i - 1].1
} else {
return None;
}
}
};
if addr >= func.start {
return Some(func);
}
None
}
pub fn find_func_index(&self, addr: u64) -> Option<DefinedFuncIndex> {
self.find_func(addr).map(|f| f.index)
}
pub fn translate_raw(&self, addr: u64) -> Option<(DefinedFuncIndex, GeneratedAddress)> {
if addr == 0 {
// It's normally 0 for debug info without the linked code.
return None;
}
if let Some(func) = self.find_func(addr) {
if addr == func.end {
// Clamp last address to the end to extend translation to the end
// of the function.
let map = &self.map[func.index];
return Some((func.index, map.len));
}
let first_result = TransformRangeIter::new(func, addr).next();
first_result.map(|(address, _)| (func.index, address))
} else {
// Address was not found: function was not compiled?
None
}
}
pub fn can_translate_address(&self, addr: u64) -> bool {
self.translate(addr).is_some()
}
pub fn translate(&self, addr: u64) -> Option<write::Address> {
self.translate_raw(addr)
.map(|(func_index, address)| write::Address::Symbol {
symbol: func_index.index(),
addend: address as i64,
})
}
pub fn translate_ranges_raw(
&self,
start: u64,
end: u64,
) -> Option<(DefinedFuncIndex, Vec<(GeneratedAddress, GeneratedAddress)>)> {
if start == 0 {
// It's normally 0 for debug info without the linked code.
return None;
}
if let Some(func) = self.find_func(start) {
let mut starts: HashMap<usize, usize> =
HashMap::from_iter(TransformRangeIter::new(func, start).map(|(a, r)| (r, a)));
let mut result = Vec::new();
TransformRangeEndIter::new(func, end).for_each(|(a, r)| {
let range_start = if let Some(range_start) = starts.get(&r) {
let range_start = *range_start;
starts.remove(&r);
range_start
} else {
let range = &func.lookup.ranges[r];
range.gen_start
};
result.push((range_start, a));
});
for (r, range_start) in starts {
let range = &func.lookup.ranges[r];
result.push((range_start, range.gen_end));
}
return Some((func.index, result));
}
// Address was not found: function was not compiled?
None
}
pub fn translate_ranges(&self, start: u64, end: u64) -> Vec<(write::Address, u64)> {
self.translate_ranges_raw(start, end)
.map_or(vec![], |(func_index, ranges)| {
ranges
.iter()
.map(|(start, end)| {
(
write::Address::Symbol {
symbol: func_index.index(),
addend: *start as i64,
},
(*end - *start) as u64,
)
})
.collect::<Vec<_>>()
})
}
pub fn map(&self) -> &PrimaryMap<DefinedFuncIndex, FunctionMap> {
&self.map
}
pub fn func_range(&self, index: DefinedFuncIndex) -> (GeneratedAddress, GeneratedAddress) {
let map = &self.map[index];
(map.offset, map.offset + map.len)
}
pub fn func_source_range(&self, index: DefinedFuncIndex) -> (WasmAddress, WasmAddress) {
let map = &self.map[index];
(map.wasm_start, map.wasm_end)
}
pub fn convert_to_code_range(
&self,
addr: write::Address,
len: u64,
) -> (GeneratedAddress, GeneratedAddress) {
let start = if let write::Address::Symbol { addend, .. } = addr {
// TODO subtract self.map[symbol].offset ?
addend as GeneratedAddress
} else {
unreachable!();
};
(start, start + len as GeneratedAddress)
}
}
#[cfg(test)]
mod tests {
use super::{build_function_lookup, get_wasm_code_offset, AddressTransform};
use crate::read_debuginfo::WasmFileInfo;
use core::iter::FromIterator;
use cranelift_codegen::ir::SourceLoc;
use cranelift_entity::PrimaryMap;
use gimli::write::Address;
use wasmtime_environ::{FunctionAddressMap, InstructionAddressMap, ModuleAddressMap};
#[test]
fn test_get_wasm_code_offset() {
let offset = get_wasm_code_offset(SourceLoc::new(3), 1);
assert_eq!(2, offset);
let offset = get_wasm_code_offset(SourceLoc::new(16), 0xF000_0000);
assert_eq!(0x1000_0010, offset);
let offset = get_wasm_code_offset(SourceLoc::new(1), 0x20_8000_0000);
assert_eq!(0x8000_0001, offset);
}
fn create_simple_func(wasm_offset: u32) -> FunctionAddressMap {
FunctionAddressMap {
instructions: vec![
InstructionAddressMap {
srcloc: SourceLoc::new(wasm_offset + 2),
code_offset: 5,
code_len: 3,
},
InstructionAddressMap {
srcloc: SourceLoc::new(wasm_offset + 7),
code_offset: 15,
code_len: 8,
},
],
start_srcloc: SourceLoc::new(wasm_offset),
end_srcloc: SourceLoc::new(wasm_offset + 10),
body_offset: 0,
body_len: 30,
}
}
fn create_simple_module(func: FunctionAddressMap) -> ModuleAddressMap {
PrimaryMap::from_iter(vec![func])
}
#[test]
fn test_build_function_lookup_simple() {
let input = create_simple_func(11);
let (start, end, lookup) = build_function_lookup(&input, 1);
assert_eq!(10, start);
assert_eq!(20, end);
assert_eq!(1, lookup.index.len());
let index_entry = lookup.index.into_iter().next().unwrap();
assert_eq!((10u64, vec![0].into_boxed_slice()), index_entry);
assert_eq!(1, lookup.ranges.len());
let range = &lookup.ranges[0];
assert_eq!(10, range.wasm_start);
assert_eq!(20, range.wasm_end);
assert_eq!(0, range.gen_start);
assert_eq!(30, range.gen_end);
let positions = &range.positions;
assert_eq!(2, positions.len());
assert_eq!(12, positions[0].wasm_pos);
assert_eq!(5, positions[0].gen_start);
assert_eq!(8, positions[0].gen_end);
assert_eq!(17, positions[1].wasm_pos);
assert_eq!(15, positions[1].gen_start);
assert_eq!(23, positions[1].gen_end);
}
#[test]
fn test_build_function_lookup_two_ranges() {
let mut input = create_simple_func(11);
// append instruction with same srcloc as input.instructions[0]
input.instructions.push(InstructionAddressMap {
srcloc: SourceLoc::new(11 + 2),
code_offset: 23,
code_len: 3,
});
let (start, end, lookup) = build_function_lookup(&input, 1);
assert_eq!(10, start);
assert_eq!(20, end);
assert_eq!(2, lookup.index.len());
let index_entries = Vec::from_iter(lookup.index.into_iter());
assert_eq!((10u64, vec![0].into_boxed_slice()), index_entries[0]);
assert_eq!((12u64, vec![0, 1].into_boxed_slice()), index_entries[1]);
assert_eq!(2, lookup.ranges.len());
let range = &lookup.ranges[0];
assert_eq!(10, range.wasm_start);
assert_eq!(17, range.wasm_end);
assert_eq!(0, range.gen_start);
assert_eq!(23, range.gen_end);
let positions = &range.positions;
assert_eq!(2, positions.len());
assert_eq!(12, positions[0].wasm_pos);
assert_eq!(5, positions[0].gen_start);
assert_eq!(8, positions[0].gen_end);
assert_eq!(17, positions[1].wasm_pos);
assert_eq!(15, positions[1].gen_start);
assert_eq!(23, positions[1].gen_end);
let range = &lookup.ranges[1];
assert_eq!(12, range.wasm_start);
assert_eq!(20, range.wasm_end);
assert_eq!(23, range.gen_start);
assert_eq!(30, range.gen_end);
let positions = &range.positions;
assert_eq!(1, positions.len());
assert_eq!(12, positions[0].wasm_pos);
assert_eq!(23, positions[0].gen_start);
assert_eq!(26, positions[0].gen_end);
}
#[test]
fn test_addr_translate() {
let input = create_simple_module(create_simple_func(11));
let at = AddressTransform::new(
&input,
&WasmFileInfo {
path: None,
code_section_offset: 1,
funcs: Box::new([]),
},
);
let addr = at.translate(10);
assert_eq!(
Some(Address::Symbol {
symbol: 0,
addend: 0,
}),
addr
);
let addr = at.translate(20);
assert_eq!(
Some(Address::Symbol {
symbol: 0,
addend: 30,
}),
addr
);
let addr = at.translate(0);
assert_eq!(None, addr);
let addr = at.translate(12);
assert_eq!(
Some(Address::Symbol {
symbol: 0,
addend: 5,
}),
addr
);
let addr = at.translate(18);
assert_eq!(
Some(Address::Symbol {
symbol: 0,
addend: 23,
}),
addr
);
}
}

View File

@@ -0,0 +1,294 @@
use crate::HashMap;
use alloc::vec::Vec;
use failure::Error;
use gimli;
use gimli::{AttributeValue, DebugLineOffset, DebugStr, DebuggingInformationEntry, UnitOffset};
use gimli::write;
use super::address_transform::AddressTransform;
use super::expression::{compile_expression, CompiledExpression, FunctionFrameInfo};
use super::range_info_builder::RangeInfoBuilder;
use super::unit::PendingDieRef;
use super::{DebugInputContext, Reader, TransformError};
pub(crate) enum FileAttributeContext<'a> {
Root(Option<DebugLineOffset>),
Children(&'a Vec<write::FileId>, Option<&'a CompiledExpression>),
}
fn is_exprloc_to_loclist_allowed(attr_name: gimli::constants::DwAt) -> bool {
match attr_name {
gimli::DW_AT_location
| gimli::DW_AT_string_length
| gimli::DW_AT_return_addr
| gimli::DW_AT_data_member_location
| gimli::DW_AT_frame_base
| gimli::DW_AT_segment
| gimli::DW_AT_static_link
| gimli::DW_AT_use_location
| gimli::DW_AT_vtable_elem_location => true,
_ => false,
}
}
pub(crate) fn clone_die_attributes<'a, R>(
entry: &DebuggingInformationEntry<R>,
context: &DebugInputContext<R>,
addr_tr: &'a AddressTransform,
frame_info: Option<&FunctionFrameInfo>,
unit_encoding: gimli::Encoding,
out_unit: &mut write::Unit,
current_scope_id: write::UnitEntryId,
subprogram_range_builder: Option<RangeInfoBuilder>,
scope_ranges: Option<&Vec<(u64, u64)>>,
cu_low_pc: u64,
out_strings: &mut write::StringTable,
die_ref_map: &HashMap<UnitOffset, write::UnitEntryId>,
pending_die_refs: &mut Vec<PendingDieRef>,
file_context: FileAttributeContext<'a>,
) -> Result<(), Error>
where
R: Reader,
{
let _tag = &entry.tag();
let endian = gimli::RunTimeEndian::Little;
let range_info = if let Some(subprogram_range_builder) = subprogram_range_builder {
subprogram_range_builder
} else if entry.tag() == gimli::DW_TAG_compile_unit {
// FIXME currently address_transform operate on a single func range,
// once it is fixed we can properly set DW_AT_ranges attribute.
// Using for now DW_AT_low_pc = 0.
RangeInfoBuilder::Position(0)
} else {
RangeInfoBuilder::from(entry, context, unit_encoding, cu_low_pc)?
};
range_info.build(addr_tr, out_unit, current_scope_id);
let mut attrs = entry.attrs();
while let Some(attr) = attrs.next()? {
let attr_value = match attr.value() {
AttributeValue::Addr(_) if attr.name() == gimli::DW_AT_low_pc => {
continue;
}
AttributeValue::Udata(_) if attr.name() == gimli::DW_AT_high_pc => {
continue;
}
AttributeValue::RangeListsRef(_) if attr.name() == gimli::DW_AT_ranges => {
continue;
}
AttributeValue::Exprloc(_) if attr.name() == gimli::DW_AT_frame_base => {
continue;
}
AttributeValue::Addr(u) => {
let addr = addr_tr.translate(u).unwrap_or(write::Address::Constant(0));
write::AttributeValue::Address(addr)
}
AttributeValue::Udata(u) => write::AttributeValue::Udata(u),
AttributeValue::Data1(d) => write::AttributeValue::Data1(d),
AttributeValue::Data2(d) => write::AttributeValue::Data2(d),
AttributeValue::Data4(d) => write::AttributeValue::Data4(d),
AttributeValue::Sdata(d) => write::AttributeValue::Sdata(d),
AttributeValue::Flag(f) => write::AttributeValue::Flag(f),
AttributeValue::DebugLineRef(line_program_offset) => {
if let FileAttributeContext::Root(o) = file_context {
if o != Some(line_program_offset) {
return Err(TransformError("invalid debug_line offset").into());
}
write::AttributeValue::LineProgramRef
} else {
return Err(TransformError("unexpected debug_line index attribute").into());
}
}
AttributeValue::FileIndex(i) => {
if let FileAttributeContext::Children(file_map, _) = file_context {
write::AttributeValue::FileIndex(Some(file_map[(i - 1) as usize]))
} else {
return Err(TransformError("unexpected file index attribute").into());
}
}
AttributeValue::DebugStrRef(str_offset) => {
let s = context.debug_str.get_str(str_offset)?.to_slice()?.to_vec();
write::AttributeValue::StringRef(out_strings.add(s))
}
AttributeValue::RangeListsRef(r) => {
let range_info =
RangeInfoBuilder::from_ranges_ref(r, context, unit_encoding, cu_low_pc)?;
let range_list_id = range_info.build_ranges(addr_tr, &mut out_unit.ranges);
write::AttributeValue::RangeListRef(range_list_id)
}
AttributeValue::LocationListsRef(r) => {
let low_pc = 0;
let mut locs = context.loclists.locations(
r,
unit_encoding,
low_pc,
&context.debug_addr,
context.debug_addr_base,
)?;
let frame_base = if let FileAttributeContext::Children(_, frame_base) = file_context
{
frame_base
} else {
None
};
let mut result = None;
while let Some(loc) = locs.next()? {
if let Some(expr) = compile_expression(&loc.data, unit_encoding, frame_base)? {
if result.is_none() {
result = Some(Vec::new());
}
for (start, len, expr) in expr.build_with_locals(
&[(loc.range.begin, loc.range.end)],
addr_tr,
frame_info,
endian,
) {
if len == 0 {
// Ignore empty range
continue;
}
result.as_mut().unwrap().push(write::Location::StartLength {
begin: start,
length: len,
data: expr,
});
}
} else {
// FIXME _expr contains invalid expression
continue; // ignore entry
}
}
if result.is_none() {
continue; // no valid locations
}
let list_id = out_unit.locations.add(write::LocationList(result.unwrap()));
write::AttributeValue::LocationListRef(list_id)
}
AttributeValue::Exprloc(ref expr) => {
let frame_base = if let FileAttributeContext::Children(_, frame_base) = file_context
{
frame_base
} else {
None
};
if let Some(expr) = compile_expression(expr, unit_encoding, frame_base)? {
if expr.is_simple() {
if let Some(expr) = expr.build() {
write::AttributeValue::Exprloc(expr)
} else {
continue;
}
} else {
// Conversion to loclist is required.
if let Some(scope_ranges) = scope_ranges {
let exprs =
expr.build_with_locals(scope_ranges, addr_tr, frame_info, endian);
if exprs.is_empty() {
continue;
}
let found_single_expr = {
// Micro-optimization all expressions alike, use one exprloc.
let mut found_expr: Option<write::Expression> = None;
for (_, _, expr) in &exprs {
if let Some(ref prev_expr) = found_expr {
if expr.0.eq(&prev_expr.0) {
continue; // the same expression
}
found_expr = None;
break;
}
found_expr = Some(expr.clone())
}
found_expr
};
if found_single_expr.is_some() {
write::AttributeValue::Exprloc(found_single_expr.unwrap())
} else if is_exprloc_to_loclist_allowed(attr.name()) {
// Converting exprloc to loclist.
let mut locs = Vec::new();
for (begin, length, data) in exprs {
if length == 0 {
// Ignore empty range
continue;
}
locs.push(write::Location::StartLength {
begin,
length,
data,
});
}
let list_id = out_unit.locations.add(write::LocationList(locs));
write::AttributeValue::LocationListRef(list_id)
} else {
continue;
}
} else {
continue;
}
}
} else {
// FIXME _expr contains invalid expression
continue; // ignore attribute
}
}
AttributeValue::Encoding(e) => write::AttributeValue::Encoding(e),
AttributeValue::DecimalSign(e) => write::AttributeValue::DecimalSign(e),
AttributeValue::Endianity(e) => write::AttributeValue::Endianity(e),
AttributeValue::Accessibility(e) => write::AttributeValue::Accessibility(e),
AttributeValue::Visibility(e) => write::AttributeValue::Visibility(e),
AttributeValue::Virtuality(e) => write::AttributeValue::Virtuality(e),
AttributeValue::Language(e) => write::AttributeValue::Language(e),
AttributeValue::AddressClass(e) => write::AttributeValue::AddressClass(e),
AttributeValue::IdentifierCase(e) => write::AttributeValue::IdentifierCase(e),
AttributeValue::CallingConvention(e) => write::AttributeValue::CallingConvention(e),
AttributeValue::Inline(e) => write::AttributeValue::Inline(e),
AttributeValue::Ordering(e) => write::AttributeValue::Ordering(e),
AttributeValue::UnitRef(ref offset) => {
if let Some(unit_id) = die_ref_map.get(offset) {
write::AttributeValue::ThisUnitEntryRef(*unit_id)
} else {
pending_die_refs.push((current_scope_id, attr.name(), *offset));
continue;
}
}
// AttributeValue::DebugInfoRef(_) => {
// continue;
// }
_ => panic!(), //write::AttributeValue::StringRef(out_strings.add("_")),
};
let current_scope = out_unit.get_mut(current_scope_id);
current_scope.set(attr.name(), attr_value);
}
Ok(())
}
pub(crate) fn clone_attr_string<R>(
attr_value: &AttributeValue<R>,
form: gimli::DwForm,
debug_str: &DebugStr<R>,
out_strings: &mut write::StringTable,
) -> Result<write::LineString, gimli::Error>
where
R: Reader,
{
let content = match attr_value {
AttributeValue::DebugStrRef(str_offset) => {
debug_str.get_str(*str_offset)?.to_slice()?.to_vec()
}
AttributeValue::String(b) => b.to_slice()?.to_vec(),
_ => panic!("Unexpected attribute value"),
};
Ok(match form {
gimli::DW_FORM_strp => {
let id = out_strings.add(content);
write::LineString::StringRef(id)
}
gimli::DW_FORM_string => write::LineString::String(content),
_ => panic!("DW_FORM_line_strp or other not supported"),
})
}

View File

@@ -0,0 +1,491 @@
use crate::{HashMap, HashSet};
use alloc::vec::Vec;
use cranelift_codegen::ir::{StackSlots, ValueLabel, ValueLoc};
use cranelift_codegen::isa::RegUnit;
use cranelift_codegen::ValueLabelsRanges;
use cranelift_entity::EntityRef;
use cranelift_wasm::{get_vmctx_value_label, DefinedFuncIndex};
use failure::Error;
use gimli::write;
use gimli::{self, Expression, Operation, Reader, ReaderOffset, Register, X86_64};
use super::address_transform::AddressTransform;
#[derive(Debug)]
pub struct FunctionFrameInfo<'a> {
pub value_ranges: &'a ValueLabelsRanges,
pub memory_offset: i64,
pub stack_slots: &'a StackSlots,
}
#[derive(Debug)]
enum CompiledExpressionPart {
Code(Vec<u8>),
Local(ValueLabel),
Deref,
}
#[derive(Debug)]
pub struct CompiledExpression {
parts: Vec<CompiledExpressionPart>,
need_deref: bool,
}
impl Clone for CompiledExpressionPart {
fn clone(&self) -> Self {
match self {
CompiledExpressionPart::Code(c) => CompiledExpressionPart::Code(c.clone()),
CompiledExpressionPart::Local(i) => CompiledExpressionPart::Local(*i),
CompiledExpressionPart::Deref => CompiledExpressionPart::Deref,
}
}
}
impl CompiledExpression {
pub fn vmctx() -> CompiledExpression {
CompiledExpression::from_label(get_vmctx_value_label())
}
pub fn from_label(label: ValueLabel) -> CompiledExpression {
CompiledExpression {
parts: vec![
CompiledExpressionPart::Local(label),
CompiledExpressionPart::Code(vec![gimli::constants::DW_OP_stack_value.0 as u8]),
],
need_deref: false,
}
}
}
fn map_reg(reg: RegUnit) -> Register {
static mut REG_X86_MAP: Option<HashMap<RegUnit, Register>> = None;
// FIXME lazy initialization?
unsafe {
if REG_X86_MAP.is_none() {
REG_X86_MAP = Some(HashMap::new());
}
if let Some(val) = REG_X86_MAP.as_mut().unwrap().get(&reg) {
return *val;
}
let result = match reg {
0 => X86_64::RAX,
1 => X86_64::RCX,
2 => X86_64::RDX,
3 => X86_64::RBX,
4 => X86_64::RSP,
5 => X86_64::RBP,
6 => X86_64::RSI,
7 => X86_64::RDI,
8 => X86_64::R8,
9 => X86_64::R9,
10 => X86_64::R10,
11 => X86_64::R11,
12 => X86_64::R12,
13 => X86_64::R13,
14 => X86_64::R14,
15 => X86_64::R15,
16 => X86_64::XMM0,
17 => X86_64::XMM1,
18 => X86_64::XMM2,
19 => X86_64::XMM3,
20 => X86_64::XMM4,
21 => X86_64::XMM5,
22 => X86_64::XMM6,
23 => X86_64::XMM7,
_ => panic!("{}", reg),
};
REG_X86_MAP.as_mut().unwrap().insert(reg, result);
result
}
}
fn translate_loc(loc: ValueLoc, frame_info: Option<&FunctionFrameInfo>) -> Option<Vec<u8>> {
match loc {
ValueLoc::Reg(reg) => {
let machine_reg = map_reg(reg).0 as u8;
assert!(machine_reg < 32); // FIXME
Some(vec![gimli::constants::DW_OP_reg0.0 + machine_reg])
}
ValueLoc::Stack(ss) => {
if let Some(frame_info) = frame_info {
if let Some(ss_offset) = frame_info.stack_slots[ss].offset {
use gimli::write::Writer;
let endian = gimli::RunTimeEndian::Little;
let mut writer = write::EndianVec::new(endian);
writer
.write_u8(gimli::constants::DW_OP_breg0.0 + X86_64::RBP.0 as u8)
.expect("bp wr");
writer.write_sleb128(ss_offset as i64 + 16).expect("ss wr");
writer
.write_u8(gimli::constants::DW_OP_deref.0 as u8)
.expect("bp wr");
let buf = writer.into_vec();
return Some(buf);
}
}
None
}
_ => None,
}
}
fn append_memory_deref(
buf: &mut Vec<u8>,
frame_info: &FunctionFrameInfo,
vmctx_loc: ValueLoc,
endian: gimli::RunTimeEndian,
) -> write::Result<bool> {
use gimli::write::Writer;
let mut writer = write::EndianVec::new(endian);
match vmctx_loc {
ValueLoc::Reg(vmctx_reg) => {
let reg = map_reg(vmctx_reg);
writer.write_u8(gimli::constants::DW_OP_breg0.0 + reg.0 as u8)?;
writer.write_sleb128(frame_info.memory_offset)?;
}
ValueLoc::Stack(ss) => {
if let Some(ss_offset) = frame_info.stack_slots[ss].offset {
writer.write_u8(gimli::constants::DW_OP_breg0.0 + X86_64::RBP.0 as u8)?;
writer.write_sleb128(ss_offset as i64 + 16)?;
writer.write_u8(gimli::constants::DW_OP_deref.0 as u8)?;
writer.write_u8(gimli::constants::DW_OP_consts.0 as u8)?;
writer.write_sleb128(frame_info.memory_offset)?;
writer.write_u8(gimli::constants::DW_OP_plus.0 as u8)?;
} else {
return Ok(false);
}
}
_ => {
return Ok(false);
}
}
writer.write_u8(gimli::constants::DW_OP_deref.0 as u8)?;
writer.write_u8(gimli::constants::DW_OP_swap.0 as u8)?;
writer.write_u8(gimli::constants::DW_OP_stack_value.0 as u8)?;
writer.write_u8(gimli::constants::DW_OP_constu.0 as u8)?;
writer.write_uleb128(0xffff_ffff)?;
writer.write_u8(gimli::constants::DW_OP_and.0 as u8)?;
writer.write_u8(gimli::constants::DW_OP_plus.0 as u8)?;
buf.extend_from_slice(writer.slice());
Ok(true)
}
impl CompiledExpression {
pub fn is_simple(&self) -> bool {
if let [CompiledExpressionPart::Code(_)] = self.parts.as_slice() {
true
} else {
self.parts.is_empty()
}
}
pub fn build(&self) -> Option<write::Expression> {
if let [CompiledExpressionPart::Code(code)] = self.parts.as_slice() {
return Some(write::Expression(code.to_vec()));
}
// locals found, not supported
None
}
pub fn build_with_locals(
&self,
scope: &[(u64, u64)], // wasm ranges
addr_tr: &AddressTransform,
frame_info: Option<&FunctionFrameInfo>,
endian: gimli::RunTimeEndian,
) -> alloc::vec::Vec<(write::Address, u64, write::Expression)> {
if scope.is_empty() {
return vec![];
}
if let [CompiledExpressionPart::Code(code)] = self.parts.as_slice() {
let mut result_scope = Vec::new();
for s in scope {
for (addr, len) in addr_tr.translate_ranges(s.0, s.1) {
result_scope.push((addr, len, write::Expression(code.to_vec())));
}
}
return result_scope;
}
let vmctx_label = get_vmctx_value_label();
// Some locals are present, preparing and divided ranges based on the scope
// and frame_info data.
let mut ranges_builder = ValueLabelRangesBuilder::new(scope, addr_tr, frame_info);
for p in &self.parts {
match p {
CompiledExpressionPart::Code(_) => (),
CompiledExpressionPart::Local(label) => ranges_builder.process_label(*label),
CompiledExpressionPart::Deref => ranges_builder.process_label(vmctx_label),
}
}
if self.need_deref {
ranges_builder.process_label(vmctx_label);
}
ranges_builder.remove_incomplete_ranges();
let ranges = ranges_builder.ranges;
let mut result = Vec::new();
'range: for CachedValueLabelRange {
func_index,
start,
end,
label_location,
} in ranges
{
// build expression
let mut code_buf = Vec::new();
for part in &self.parts {
match part {
CompiledExpressionPart::Code(c) => code_buf.extend_from_slice(c.as_slice()),
CompiledExpressionPart::Local(label) => {
let loc = *label_location.get(&label).expect("loc");
if let Some(expr) = translate_loc(loc, frame_info) {
code_buf.extend_from_slice(&expr)
} else {
continue 'range;
}
}
CompiledExpressionPart::Deref => {
if let (Some(vmctx_loc), Some(frame_info)) =
(label_location.get(&vmctx_label), frame_info)
{
if !append_memory_deref(&mut code_buf, frame_info, *vmctx_loc, endian)
.expect("append_memory_deref")
{
continue 'range;
}
} else {
continue 'range;
};
}
}
}
if self.need_deref {
if let (Some(vmctx_loc), Some(frame_info)) =
(label_location.get(&vmctx_label), frame_info)
{
if !append_memory_deref(&mut code_buf, frame_info, *vmctx_loc, endian)
.expect("append_memory_deref")
{
continue 'range;
}
} else {
continue 'range;
};
}
result.push((
write::Address::Symbol {
symbol: func_index.index(),
addend: start as i64,
},
(end - start) as u64,
write::Expression(code_buf),
));
}
result
}
}
pub fn compile_expression<R>(
expr: &Expression<R>,
encoding: gimli::Encoding,
frame_base: Option<&CompiledExpression>,
) -> Result<Option<CompiledExpression>, Error>
where
R: Reader,
{
let mut parts = Vec::new();
let mut need_deref = false;
if let Some(frame_base) = frame_base {
parts.extend_from_slice(&frame_base.parts);
need_deref = frame_base.need_deref;
}
let base_len = parts.len();
let mut pc = expr.0.clone();
let mut code_chunk = Vec::new();
let buf = expr.0.to_slice()?;
while !pc.is_empty() {
let next = buf[pc.offset_from(&expr.0).into_u64() as usize];
need_deref = true;
if next == 0xED {
// WebAssembly DWARF extension
pc.read_u8()?;
let ty = pc.read_uleb128()?;
assert_eq!(ty, 0);
let index = pc.read_sleb128()?;
pc.read_u8()?; // consume 159
if code_chunk.len() > 0 {
parts.push(CompiledExpressionPart::Code(code_chunk));
code_chunk = Vec::new();
}
let label = ValueLabel::from_u32(index as u32);
parts.push(CompiledExpressionPart::Local(label));
} else {
let pos = pc.offset_from(&expr.0).into_u64() as usize;
let op = Operation::parse(&mut pc, &expr.0, encoding)?;
match op {
Operation::Literal { .. } | Operation::PlusConstant { .. } => (),
Operation::StackValue => {
need_deref = false;
}
Operation::Deref { .. } => {
if code_chunk.len() > 0 {
parts.push(CompiledExpressionPart::Code(code_chunk));
code_chunk = Vec::new();
}
parts.push(CompiledExpressionPart::Deref);
}
_ => {
return Ok(None);
}
}
let chunk = &buf[pos..pc.offset_from(&expr.0).into_u64() as usize];
code_chunk.extend_from_slice(chunk);
}
}
if code_chunk.len() > 0 {
parts.push(CompiledExpressionPart::Code(code_chunk));
}
if base_len > 0 && base_len + 1 < parts.len() {
// see if we can glue two code chunks
if let [CompiledExpressionPart::Code(cc1), CompiledExpressionPart::Code(cc2)] =
&parts[base_len..base_len + 1]
{
let mut combined = cc1.clone();
combined.extend_from_slice(cc2);
parts[base_len] = CompiledExpressionPart::Code(combined);
parts.remove(base_len + 1);
}
}
Ok(Some(CompiledExpression { parts, need_deref }))
}
#[derive(Debug, Clone)]
struct CachedValueLabelRange {
func_index: DefinedFuncIndex,
start: usize,
end: usize,
label_location: HashMap<ValueLabel, ValueLoc>,
}
struct ValueLabelRangesBuilder<'a, 'b> {
ranges: Vec<CachedValueLabelRange>,
addr_tr: &'a AddressTransform,
frame_info: Option<&'a FunctionFrameInfo<'b>>,
processed_labels: HashSet<ValueLabel>,
}
impl<'a, 'b> ValueLabelRangesBuilder<'a, 'b> {
fn new(
scope: &[(u64, u64)], // wasm ranges
addr_tr: &'a AddressTransform,
frame_info: Option<&'a FunctionFrameInfo<'b>>,
) -> Self {
let mut ranges = Vec::new();
for s in scope {
if let Some((func_index, tr)) = addr_tr.translate_ranges_raw(s.0, s.1) {
for (start, end) in tr {
ranges.push(CachedValueLabelRange {
func_index,
start,
end,
label_location: HashMap::new(),
})
}
}
}
ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start));
ValueLabelRangesBuilder {
ranges,
addr_tr,
frame_info,
processed_labels: HashSet::new(),
}
}
fn process_label(&mut self, label: ValueLabel) {
if self.processed_labels.contains(&label) {
return;
}
self.processed_labels.insert(label);
let value_ranges = if let Some(frame_info) = self.frame_info {
&frame_info.value_ranges
} else {
return;
};
let ranges = &mut self.ranges;
if let Some(local_ranges) = value_ranges.get(&label) {
for local_range in local_ranges {
let wasm_start = local_range.start;
let wasm_end = local_range.end;
let loc = local_range.loc;
// Find all native ranges for the value label ranges.
for (addr, len) in self
.addr_tr
.translate_ranges(wasm_start as u64, wasm_end as u64)
{
let (range_start, range_end) = self.addr_tr.convert_to_code_range(addr, len);
if range_start == range_end {
continue;
}
assert!(range_start < range_end);
// Find acceptable scope of ranges to intersect with.
let i = match ranges.binary_search_by(|s| s.start.cmp(&range_start)) {
Ok(i) => i,
Err(i) => {
if i > 0 && range_start < ranges[i - 1].end {
i - 1
} else {
i
}
}
};
let j = match ranges.binary_search_by(|s| s.start.cmp(&range_end)) {
Ok(i) | Err(i) => i,
};
// Starting for the end, intersect (range_start..range_end) with
// self.ranges array.
for i in (i..j).rev() {
if range_end <= ranges[i].start || ranges[i].end <= range_start {
continue;
}
if range_end < ranges[i].end {
// Cutting some of the range from the end.
let mut tail = ranges[i].clone();
ranges[i].end = range_end;
tail.start = range_end;
ranges.insert(i + 1, tail);
}
assert!(ranges[i].end <= range_end);
if range_start <= ranges[i].start {
ranges[i].label_location.insert(label, loc);
continue;
}
// Cutting some of the range from the start.
let mut tail = ranges[i].clone();
ranges[i].end = range_start;
tail.start = range_start;
tail.label_location.insert(label, loc);
ranges.insert(i + 1, tail);
}
}
}
}
}
fn remove_incomplete_ranges(&mut self) {
// Ranges with not-enough labels are discarded.
let processed_labels_len = self.processed_labels.len();
self.ranges
.retain(|r| r.label_location.len() == processed_labels_len);
}
}

View File

@@ -0,0 +1,234 @@
use alloc::collections::BTreeMap;
use alloc::vec::Vec;
use core::iter::FromIterator;
use cranelift_entity::EntityRef;
use failure::Error;
use gimli;
use gimli::{DebugLine, DebugLineOffset, DebugStr, DebuggingInformationEntry, LineEncoding, Unit};
use gimli::write;
use super::address_transform::AddressTransform;
use super::attr::clone_attr_string;
use super::{Reader, TransformError};
#[derive(Debug)]
enum SavedLineProgramRow {
Normal {
address: u64,
op_index: u64,
file_index: u64,
line: u64,
column: u64,
discriminator: u64,
is_stmt: bool,
basic_block: bool,
prologue_end: bool,
epilogue_begin: bool,
isa: u64,
},
EndOfSequence(u64),
}
#[derive(Debug, Eq, PartialEq)]
enum ReadLineProgramState {
SequenceEnded,
ReadSequence,
IgnoreSequence,
}
pub(crate) fn clone_line_program<R>(
unit: &Unit<R, R::Offset>,
root: &DebuggingInformationEntry<R>,
addr_tr: &AddressTransform,
out_encoding: gimli::Encoding,
debug_str: &DebugStr<R>,
debug_line: &DebugLine<R>,
out_strings: &mut write::StringTable,
) -> Result<(write::LineProgram, DebugLineOffset, Vec<write::FileId>), Error>
where
R: Reader,
{
let offset = match root.attr_value(gimli::DW_AT_stmt_list)? {
Some(gimli::AttributeValue::DebugLineRef(offset)) => offset,
_ => {
return Err(TransformError("Debug line offset is not found").into());
}
};
let comp_dir = root.attr_value(gimli::DW_AT_comp_dir)?;
let comp_name = root.attr_value(gimli::DW_AT_name)?;
let out_comp_dir = clone_attr_string(
comp_dir.as_ref().expect("comp_dir"),
gimli::DW_FORM_strp,
debug_str,
out_strings,
)?;
let out_comp_name = clone_attr_string(
comp_name.as_ref().expect("comp_name"),
gimli::DW_FORM_strp,
debug_str,
out_strings,
)?;
let program = debug_line.program(
offset,
unit.header.address_size(),
comp_dir.and_then(|val| val.string_value(&debug_str)),
comp_name.and_then(|val| val.string_value(&debug_str)),
);
if let Ok(program) = program {
let header = program.header();
assert!(header.version() <= 4, "not supported 5");
let line_encoding = LineEncoding {
minimum_instruction_length: header.minimum_instruction_length(),
maximum_operations_per_instruction: header.maximum_operations_per_instruction(),
default_is_stmt: header.default_is_stmt(),
line_base: header.line_base(),
line_range: header.line_range(),
};
let mut out_program = write::LineProgram::new(
out_encoding,
line_encoding,
out_comp_dir,
out_comp_name,
None,
);
let mut dirs = Vec::new();
dirs.push(out_program.default_directory());
for dir_attr in header.include_directories() {
let dir_id = out_program.add_directory(clone_attr_string(
dir_attr,
gimli::DW_FORM_string,
debug_str,
out_strings,
)?);
dirs.push(dir_id);
}
let mut files = Vec::new();
for file_entry in header.file_names() {
let dir_id = dirs[file_entry.directory_index() as usize];
let file_id = out_program.add_file(
clone_attr_string(
&file_entry.path_name(),
gimli::DW_FORM_string,
debug_str,
out_strings,
)?,
dir_id,
None,
);
files.push(file_id);
}
let mut rows = program.rows();
let mut saved_rows = BTreeMap::new();
let mut state = ReadLineProgramState::SequenceEnded;
while let Some((_header, row)) = rows.next_row()? {
if state == ReadLineProgramState::IgnoreSequence {
if row.end_sequence() {
state = ReadLineProgramState::SequenceEnded;
}
continue;
}
let saved_row = if row.end_sequence() {
state = ReadLineProgramState::SequenceEnded;
SavedLineProgramRow::EndOfSequence(row.address())
} else {
if state == ReadLineProgramState::SequenceEnded {
// Discard sequences for non-existent code.
if row.address() == 0 {
state = ReadLineProgramState::IgnoreSequence;
continue;
}
state = ReadLineProgramState::ReadSequence;
}
SavedLineProgramRow::Normal {
address: row.address(),
op_index: row.op_index(),
file_index: row.file_index(),
line: row.line().unwrap_or(0),
column: match row.column() {
gimli::ColumnType::LeftEdge => 0,
gimli::ColumnType::Column(val) => val,
},
discriminator: row.discriminator(),
is_stmt: row.is_stmt(),
basic_block: row.basic_block(),
prologue_end: row.prologue_end(),
epilogue_begin: row.epilogue_begin(),
isa: row.isa(),
}
};
saved_rows.insert(row.address(), saved_row);
}
let saved_rows = Vec::from_iter(saved_rows.into_iter());
for (i, map) in addr_tr.map() {
if map.len == 0 {
continue; // no code generated
}
let symbol = i.index();
let base_addr = map.offset;
out_program.begin_sequence(Some(write::Address::Symbol { symbol, addend: 0 }));
// TODO track and place function declaration line here
let mut last_address = None;
for addr_map in map.addresses.iter() {
let saved_row = match saved_rows.binary_search_by_key(&addr_map.wasm, |i| i.0) {
Ok(i) => Some(&saved_rows[i].1),
Err(i) => {
if i > 0 {
Some(&saved_rows[i - 1].1)
} else {
None
}
}
};
if let Some(SavedLineProgramRow::Normal {
address,
op_index,
file_index,
line,
column,
discriminator,
is_stmt,
basic_block,
prologue_end,
epilogue_begin,
isa,
}) = saved_row
{
// Ignore duplicates
if Some(*address) != last_address {
let address_offset = if last_address.is_none() {
// Extend first entry to the function declaration
// TODO use the function declaration line instead
0
} else {
(addr_map.generated - base_addr) as u64
};
out_program.row().address_offset = address_offset;
out_program.row().op_index = *op_index;
out_program.row().file = files[(file_index - 1) as usize];
out_program.row().line = *line;
out_program.row().column = *column;
out_program.row().discriminator = *discriminator;
out_program.row().is_statement = *is_stmt;
out_program.row().basic_block = *basic_block;
out_program.row().prologue_end = *prologue_end;
out_program.row().epilogue_begin = *epilogue_begin;
out_program.row().isa = *isa;
out_program.generate_row();
last_address = Some(*address);
}
}
}
let end_addr = (map.offset + map.len - 1) as u64;
out_program.end_sequence(end_addr);
}
Ok((out_program, offset, files))
} else {
Err(TransformError("Valid line program not found").into())
}
}

View File

@@ -0,0 +1,119 @@
use crate::gc::build_dependencies;
use crate::DebugInfoData;
use crate::HashSet;
use cranelift_codegen::isa::TargetFrontendConfig;
use failure::Error;
use simulate::generate_simulated_dwarf;
use thiserror::Error;
use wasmtime_environ::{ModuleAddressMap, ModuleVmctxInfo, ValueLabelsRanges};
use gimli;
use gimli::{
DebugAddr, DebugAddrBase, DebugLine, DebugStr, LocationLists, RangeLists, UnitSectionOffset,
};
use gimli::write;
pub use address_transform::AddressTransform;
use unit::clone_unit;
mod address_transform;
mod attr;
mod expression;
mod line_program;
mod range_info_builder;
mod simulate;
mod unit;
mod utils;
pub(crate) trait Reader: gimli::Reader<Offset = usize> {}
impl<'input, Endian> Reader for gimli::EndianSlice<'input, Endian> where Endian: gimli::Endianity {}
#[derive(Error, Debug)]
#[error("Debug info transform error: {0}")]
pub struct TransformError(&'static str);
pub(crate) struct DebugInputContext<'a, R>
where
R: Reader,
{
debug_str: &'a DebugStr<R>,
debug_line: &'a DebugLine<R>,
debug_addr: &'a DebugAddr<R>,
debug_addr_base: DebugAddrBase<R::Offset>,
rnglists: &'a RangeLists<R>,
loclists: &'a LocationLists<R>,
reachable: &'a HashSet<UnitSectionOffset>,
}
pub fn transform_dwarf(
target_config: &TargetFrontendConfig,
di: &DebugInfoData,
at: &ModuleAddressMap,
vmctx_info: &ModuleVmctxInfo,
ranges: &ValueLabelsRanges,
) -> Result<write::Dwarf, Error> {
let addr_tr = AddressTransform::new(at, &di.wasm_file);
let reachable = build_dependencies(&di.dwarf, &addr_tr)?.get_reachable();
let context = DebugInputContext {
debug_str: &di.dwarf.debug_str,
debug_line: &di.dwarf.debug_line,
debug_addr: &di.dwarf.debug_addr,
debug_addr_base: DebugAddrBase(0),
rnglists: &di.dwarf.ranges,
loclists: &di.dwarf.locations,
reachable: &reachable,
};
let out_encoding = gimli::Encoding {
format: gimli::Format::Dwarf32,
// TODO: this should be configurable
// macOS doesn't seem to support DWARF > 3
version: 3,
address_size: target_config.pointer_bytes(),
};
let mut out_strings = write::StringTable::default();
let mut out_units = write::UnitTable::default();
let out_line_strings = write::LineStringTable::default();
let mut translated = HashSet::new();
let mut iter = di.dwarf.debug_info.units();
while let Some(unit) = iter.next().unwrap_or(None) {
let unit = di.dwarf.unit(unit)?;
clone_unit(
unit,
&context,
&addr_tr,
&ranges,
out_encoding,
&vmctx_info,
&mut out_units,
&mut out_strings,
&mut translated,
)?;
}
generate_simulated_dwarf(
&addr_tr,
di,
&vmctx_info,
&ranges,
&translated,
out_encoding,
&mut out_units,
&mut out_strings,
)?;
Ok(write::Dwarf {
units: out_units,
line_programs: vec![],
line_strings: out_line_strings,
strings: out_strings,
})
}

View File

@@ -0,0 +1,226 @@
use alloc::vec::Vec;
use cranelift_entity::EntityRef;
use cranelift_wasm::DefinedFuncIndex;
use failure::Error;
use gimli;
use gimli::{AttributeValue, DebuggingInformationEntry, RangeListsOffset};
use gimli::write;
use super::address_transform::AddressTransform;
use super::DebugInputContext;
use super::Reader;
pub(crate) enum RangeInfoBuilder {
Undefined,
Position(u64),
Ranges(Vec<(u64, u64)>),
Function(DefinedFuncIndex),
}
impl RangeInfoBuilder {
pub(crate) fn from<R>(
entry: &DebuggingInformationEntry<R>,
context: &DebugInputContext<R>,
unit_encoding: gimli::Encoding,
cu_low_pc: u64,
) -> Result<Self, Error>
where
R: Reader,
{
if let Some(AttributeValue::RangeListsRef(r)) = entry.attr_value(gimli::DW_AT_ranges)? {
return RangeInfoBuilder::from_ranges_ref(r, context, unit_encoding, cu_low_pc);
};
let low_pc =
if let Some(AttributeValue::Addr(addr)) = entry.attr_value(gimli::DW_AT_low_pc)? {
addr
} else {
return Ok(RangeInfoBuilder::Undefined);
};
Ok(
if let Some(AttributeValue::Udata(u)) = entry.attr_value(gimli::DW_AT_high_pc)? {
RangeInfoBuilder::Ranges(vec![(low_pc, low_pc + u)])
} else {
RangeInfoBuilder::Position(low_pc)
},
)
}
pub(crate) fn from_ranges_ref<R>(
ranges: RangeListsOffset,
context: &DebugInputContext<R>,
unit_encoding: gimli::Encoding,
cu_low_pc: u64,
) -> Result<Self, Error>
where
R: Reader,
{
let mut ranges = context.rnglists.ranges(
ranges,
unit_encoding,
cu_low_pc,
&context.debug_addr,
context.debug_addr_base,
)?;
let mut result = Vec::new();
while let Some(range) = ranges.next()? {
if range.begin >= range.end {
// ignore empty ranges
}
result.push((range.begin, range.end));
}
Ok(if result.len() > 0 {
RangeInfoBuilder::Ranges(result)
} else {
RangeInfoBuilder::Undefined
})
}
pub(crate) fn from_subprogram_die<R>(
entry: &DebuggingInformationEntry<R>,
context: &DebugInputContext<R>,
unit_encoding: gimli::Encoding,
addr_tr: &AddressTransform,
cu_low_pc: u64,
) -> Result<Self, Error>
where
R: Reader,
{
let addr =
if let Some(AttributeValue::Addr(addr)) = entry.attr_value(gimli::DW_AT_low_pc)? {
addr
} else if let Some(AttributeValue::RangeListsRef(r)) =
entry.attr_value(gimli::DW_AT_ranges)?
{
let mut ranges = context.rnglists.ranges(
r,
unit_encoding,
cu_low_pc,
&context.debug_addr,
context.debug_addr_base,
)?;
if let Some(range) = ranges.next()? {
range.begin
} else {
return Ok(RangeInfoBuilder::Undefined);
}
} else {
return Ok(RangeInfoBuilder::Undefined);
};
let index = addr_tr.find_func_index(addr);
if index.is_none() {
return Ok(RangeInfoBuilder::Undefined);
}
Ok(RangeInfoBuilder::Function(index.unwrap()))
}
pub(crate) fn build(
&self,
addr_tr: &AddressTransform,
out_unit: &mut write::Unit,
current_scope_id: write::UnitEntryId,
) {
match self {
RangeInfoBuilder::Undefined => (),
RangeInfoBuilder::Position(pc) => {
let addr = addr_tr
.translate(*pc)
.unwrap_or(write::Address::Constant(0));
let current_scope = out_unit.get_mut(current_scope_id);
current_scope.set(gimli::DW_AT_low_pc, write::AttributeValue::Address(addr));
}
RangeInfoBuilder::Ranges(ranges) => {
let mut result = Vec::new();
for (begin, end) in ranges {
for tr in addr_tr.translate_ranges(*begin, *end) {
if tr.1 == 0 {
// Ignore empty range
continue;
}
result.push(tr);
}
}
if result.len() != 1 {
let range_list = result
.iter()
.map(|tr| write::Range::StartLength {
begin: tr.0,
length: tr.1,
})
.collect::<Vec<_>>();
let range_list_id = out_unit.ranges.add(write::RangeList(range_list));
let current_scope = out_unit.get_mut(current_scope_id);
current_scope.set(
gimli::DW_AT_ranges,
write::AttributeValue::RangeListRef(range_list_id),
);
} else {
let current_scope = out_unit.get_mut(current_scope_id);
current_scope.set(
gimli::DW_AT_low_pc,
write::AttributeValue::Address(result[0].0),
);
current_scope.set(
gimli::DW_AT_high_pc,
write::AttributeValue::Udata(result[0].1),
);
}
}
RangeInfoBuilder::Function(index) => {
let range = addr_tr.func_range(*index);
let symbol = index.index();
let addr = write::Address::Symbol {
symbol,
addend: range.0 as i64,
};
let len = (range.1 - range.0) as u64;
let current_scope = out_unit.get_mut(current_scope_id);
current_scope.set(gimli::DW_AT_low_pc, write::AttributeValue::Address(addr));
current_scope.set(gimli::DW_AT_high_pc, write::AttributeValue::Udata(len));
}
}
}
pub(crate) fn get_ranges(&self, addr_tr: &AddressTransform) -> Vec<(u64, u64)> {
match self {
RangeInfoBuilder::Undefined | RangeInfoBuilder::Position(_) => vec![],
RangeInfoBuilder::Ranges(ranges) => ranges.clone(),
RangeInfoBuilder::Function(index) => {
let range = addr_tr.func_source_range(*index);
vec![(range.0, range.1)]
}
}
}
pub(crate) fn build_ranges(
&self,
addr_tr: &AddressTransform,
out_range_lists: &mut write::RangeListTable,
) -> write::RangeListId {
if let RangeInfoBuilder::Ranges(ranges) = self {
let mut range_list = Vec::new();
for (begin, end) in ranges {
assert!(begin < end);
for tr in addr_tr.translate_ranges(*begin, *end) {
if tr.1 == 0 {
// Ignore empty range
continue;
}
range_list.push(write::Range::StartLength {
begin: tr.0,
length: tr.1,
});
}
}
out_range_lists.add(write::RangeList(range_list))
} else {
unreachable!();
}
}
}

View File

@@ -0,0 +1,372 @@
use crate::read_debuginfo::WasmFileInfo;
pub use crate::read_debuginfo::{DebugInfoData, FunctionMetadata, WasmType};
use crate::{HashMap, HashSet};
use alloc::string::String;
use alloc::vec::Vec;
use cranelift_entity::EntityRef;
use cranelift_wasm::get_vmctx_value_label;
use failure::Error;
use std::path::PathBuf;
use wasmtime_environ::{ModuleVmctxInfo, ValueLabelsRanges};
use gimli::write;
use gimli::{self, LineEncoding};
use super::expression::{CompiledExpression, FunctionFrameInfo};
use super::utils::{add_internal_types, append_vmctx_info, get_function_frame_info};
use super::AddressTransform;
const PRODUCER_NAME: &str = "wasmtime";
fn generate_line_info(
addr_tr: &AddressTransform,
translated: &HashSet<u32>,
out_encoding: gimli::Encoding,
w: &WasmFileInfo,
comp_dir_id: write::StringId,
name_id: write::StringId,
name: &str,
) -> Result<write::LineProgram, Error> {
let out_comp_dir = write::LineString::StringRef(comp_dir_id);
let out_comp_name = write::LineString::StringRef(name_id);
let line_encoding = LineEncoding::default();
let mut out_program = write::LineProgram::new(
out_encoding,
line_encoding,
out_comp_dir,
out_comp_name,
None,
);
let file_index = out_program.add_file(
write::LineString::String(name.as_bytes().to_vec()),
out_program.default_directory(),
None,
);
for (i, map) in addr_tr.map() {
let symbol = i.index();
if translated.contains(&(symbol as u32)) {
continue;
}
let base_addr = map.offset;
out_program.begin_sequence(Some(write::Address::Symbol { symbol, addend: 0 }));
for addr_map in map.addresses.iter() {
let address_offset = (addr_map.generated - base_addr) as u64;
out_program.row().address_offset = address_offset;
out_program.row().op_index = 0;
out_program.row().file = file_index;
let wasm_offset = w.code_section_offset + addr_map.wasm as u64;
out_program.row().line = wasm_offset;
out_program.row().column = 0;
out_program.row().discriminator = 1;
out_program.row().is_statement = true;
out_program.row().basic_block = false;
out_program.row().prologue_end = false;
out_program.row().epilogue_begin = false;
out_program.row().isa = 0;
out_program.generate_row();
}
let end_addr = (map.offset + map.len - 1) as u64;
out_program.end_sequence(end_addr);
}
Ok(out_program)
}
fn autogenerate_dwarf_wasm_path(di: &DebugInfoData) -> PathBuf {
let module_name = di
.name_section
.as_ref()
.and_then(|ns| ns.module_name.to_owned())
.unwrap_or_else(|| unsafe {
static mut GEN_ID: u32 = 0;
GEN_ID += 1;
format!("<gen-{}>", GEN_ID)
});
let path = format!("/<wasm-module>/{}.wasm", module_name);
PathBuf::from(path)
}
struct WasmTypesDieRefs {
vmctx: write::UnitEntryId,
i32: write::UnitEntryId,
i64: write::UnitEntryId,
f32: write::UnitEntryId,
f64: write::UnitEntryId,
}
fn add_wasm_types(
unit: &mut write::Unit,
root_id: write::UnitEntryId,
out_strings: &mut write::StringTable,
vmctx_info: &ModuleVmctxInfo,
) -> WasmTypesDieRefs {
let (_wp_die_id, vmctx_die_id) = add_internal_types(unit, root_id, out_strings, vmctx_info);
macro_rules! def_type {
($id:literal, $size:literal, $enc:path) => {{
let die_id = unit.add(root_id, gimli::DW_TAG_base_type);
let die = unit.get_mut(die_id);
die.set(
gimli::DW_AT_name,
write::AttributeValue::StringRef(out_strings.add($id)),
);
die.set(gimli::DW_AT_byte_size, write::AttributeValue::Data1($size));
die.set(gimli::DW_AT_encoding, write::AttributeValue::Encoding($enc));
die_id
}};
}
let i32_die_id = def_type!("i32", 4, gimli::DW_ATE_signed);
let i64_die_id = def_type!("i64", 8, gimli::DW_ATE_signed);
let f32_die_id = def_type!("f32", 4, gimli::DW_ATE_float);
let f64_die_id = def_type!("f64", 8, gimli::DW_ATE_float);
WasmTypesDieRefs {
vmctx: vmctx_die_id,
i32: i32_die_id,
i64: i64_die_id,
f32: f32_die_id,
f64: f64_die_id,
}
}
fn resolve_var_type(
index: usize,
wasm_types: &WasmTypesDieRefs,
func_meta: &FunctionMetadata,
) -> Option<(write::UnitEntryId, bool)> {
let (ty, is_param) = if index < func_meta.params.len() {
(func_meta.params[index], true)
} else {
let mut i = (index - func_meta.params.len()) as u32;
let mut j = 0;
while j < func_meta.locals.len() && i >= func_meta.locals[j].0 {
i -= func_meta.locals[j].0;
j += 1;
}
if j >= func_meta.locals.len() {
// Ignore the var index out of bound.
return None;
}
(func_meta.locals[j].1, false)
};
let type_die_id = match ty {
WasmType::I32 => wasm_types.i32,
WasmType::I64 => wasm_types.i64,
WasmType::F32 => wasm_types.f32,
WasmType::F64 => wasm_types.f64,
_ => {
// Ignore unsupported types.
return None;
}
};
Some((type_die_id, is_param))
}
fn generate_vars(
unit: &mut write::Unit,
die_id: write::UnitEntryId,
addr_tr: &AddressTransform,
frame_info: &FunctionFrameInfo,
scope_ranges: &[(u64, u64)],
wasm_types: &WasmTypesDieRefs,
func_meta: &FunctionMetadata,
locals_names: Option<&HashMap<u32, String>>,
out_strings: &mut write::StringTable,
) {
let vmctx_label = get_vmctx_value_label();
for label in frame_info.value_ranges.keys() {
if label.index() == vmctx_label.index() {
append_vmctx_info(
unit,
die_id,
wasm_types.vmctx,
addr_tr,
Some(frame_info),
scope_ranges,
out_strings,
)
.expect("append_vmctx_info success");
} else {
let var_index = label.index();
let (type_die_id, is_param) =
if let Some(result) = resolve_var_type(var_index, wasm_types, func_meta) {
result
} else {
// Skipping if type of local cannot be detected.
continue;
};
let loc_list_id = {
let endian = gimli::RunTimeEndian::Little;
let expr = CompiledExpression::from_label(*label);
let mut locs = Vec::new();
for (begin, length, data) in
expr.build_with_locals(scope_ranges, addr_tr, Some(frame_info), endian)
{
locs.push(write::Location::StartLength {
begin,
length,
data,
});
}
unit.locations.add(write::LocationList(locs))
};
let var_id = unit.add(
die_id,
if is_param {
gimli::DW_TAG_formal_parameter
} else {
gimli::DW_TAG_variable
},
);
let var = unit.get_mut(var_id);
let name_id = match locals_names.and_then(|m| m.get(&(var_index as u32))) {
Some(n) => out_strings.add(n.to_owned()),
None => out_strings.add(format!("var{}", var_index)),
};
var.set(gimli::DW_AT_name, write::AttributeValue::StringRef(name_id));
var.set(
gimli::DW_AT_type,
write::AttributeValue::ThisUnitEntryRef(type_die_id),
);
var.set(
gimli::DW_AT_location,
write::AttributeValue::LocationListRef(loc_list_id),
);
}
}
}
pub fn generate_simulated_dwarf(
addr_tr: &AddressTransform,
di: &DebugInfoData,
vmctx_info: &ModuleVmctxInfo,
ranges: &ValueLabelsRanges,
translated: &HashSet<u32>,
out_encoding: gimli::Encoding,
out_units: &mut write::UnitTable,
out_strings: &mut write::StringTable,
) -> Result<(), Error> {
let path = di
.wasm_file
.path
.to_owned()
.unwrap_or_else(|| autogenerate_dwarf_wasm_path(di));
let (func_names, locals_names) = if let Some(ref name_section) = di.name_section {
(
Some(&name_section.func_names),
Some(&name_section.locals_names),
)
} else {
(None, None)
};
let (unit, root_id, name_id) = {
let comp_dir_id = out_strings.add(path.parent().expect("path dir").to_str().unwrap());
let name = path.file_name().expect("path name").to_str().unwrap();
let name_id = out_strings.add(name);
let out_program = generate_line_info(
addr_tr,
translated,
out_encoding,
&di.wasm_file,
comp_dir_id,
name_id,
name,
)?;
let unit_id = out_units.add(write::Unit::new(out_encoding, out_program));
let unit = out_units.get_mut(unit_id);
let root_id = unit.root();
let root = unit.get_mut(root_id);
let id = out_strings.add(PRODUCER_NAME);
root.set(gimli::DW_AT_producer, write::AttributeValue::StringRef(id));
root.set(gimli::DW_AT_name, write::AttributeValue::StringRef(name_id));
root.set(
gimli::DW_AT_stmt_list,
write::AttributeValue::LineProgramRef,
);
root.set(
gimli::DW_AT_comp_dir,
write::AttributeValue::StringRef(comp_dir_id),
);
(unit, root_id, name_id)
};
let wasm_types = add_wasm_types(unit, root_id, out_strings, vmctx_info);
for (i, map) in addr_tr.map().iter() {
let index = i.index();
if translated.contains(&(index as u32)) {
continue;
}
let start = map.offset as u64;
let end = start + map.len as u64;
let die_id = unit.add(root_id, gimli::DW_TAG_subprogram);
let die = unit.get_mut(die_id);
die.set(
gimli::DW_AT_low_pc,
write::AttributeValue::Address(write::Address::Symbol {
symbol: index,
addend: start as i64,
}),
);
die.set(
gimli::DW_AT_high_pc,
write::AttributeValue::Udata((end - start) as u64),
);
let id = match func_names.and_then(|m| m.get(&(index as u32))) {
Some(n) => out_strings.add(n.to_owned()),
None => out_strings.add(format!("wasm-function[{}]", index)),
};
die.set(gimli::DW_AT_name, write::AttributeValue::StringRef(id));
die.set(
gimli::DW_AT_decl_file,
write::AttributeValue::StringRef(name_id),
);
let f = addr_tr.map().get(i).unwrap();
let f_start = f.addresses[0].wasm;
let wasm_offset = di.wasm_file.code_section_offset + f_start as u64;
die.set(
gimli::DW_AT_decl_file,
write::AttributeValue::Udata(wasm_offset),
);
if let Some(frame_info) = get_function_frame_info(vmctx_info, i, ranges) {
let source_range = addr_tr.func_source_range(i);
generate_vars(
unit,
die_id,
addr_tr,
&frame_info,
&[(source_range.0, source_range.1)],
&wasm_types,
&di.wasm_file.funcs[index],
locals_names.and_then(|m| m.get(&(index as u32))),
out_strings,
);
}
}
Ok(())
}

View File

@@ -0,0 +1,375 @@
use crate::{HashMap, HashSet};
use alloc::string::String;
use alloc::vec::Vec;
use cranelift_entity::EntityRef;
use failure::Error;
use wasmtime_environ::{ModuleVmctxInfo, ValueLabelsRanges};
use gimli;
use gimli::{AttributeValue, DebuggingInformationEntry, Unit, UnitOffset};
use gimli::write;
use super::address_transform::AddressTransform;
use super::attr::{clone_die_attributes, FileAttributeContext};
use super::expression::compile_expression;
use super::line_program::clone_line_program;
use super::range_info_builder::RangeInfoBuilder;
use super::utils::{add_internal_types, append_vmctx_info, get_function_frame_info};
use super::{DebugInputContext, Reader, TransformError};
pub(crate) type PendingDieRef = (write::UnitEntryId, gimli::DwAt, UnitOffset);
struct InheritedAttr<T> {
stack: Vec<(usize, T)>,
}
impl<T> InheritedAttr<T> {
fn new() -> Self {
InheritedAttr { stack: Vec::new() }
}
fn update(&mut self, depth: usize) {
while !self.stack.is_empty() && self.stack.last().unwrap().0 >= depth {
self.stack.pop();
}
}
fn push(&mut self, depth: usize, value: T) {
self.stack.push((depth, value));
}
fn top(&self) -> Option<&T> {
self.stack.last().map(|entry| &entry.1)
}
fn is_empty(&self) -> bool {
self.stack.is_empty()
}
}
fn get_base_type_name<R>(
type_entry: &DebuggingInformationEntry<R>,
unit: &Unit<R, R::Offset>,
context: &DebugInputContext<R>,
) -> Result<String, Error>
where
R: Reader,
{
// FIXME remove recursion.
match type_entry.attr_value(gimli::DW_AT_type)? {
Some(AttributeValue::UnitRef(ref offset)) => {
let mut entries = unit.entries_at_offset(*offset)?;
entries.next_entry()?;
if let Some(die) = entries.current() {
if let Some(AttributeValue::DebugStrRef(str_offset)) =
die.attr_value(gimli::DW_AT_name)?
{
return Ok(String::from(
context.debug_str.get_str(str_offset)?.to_string()?,
));
}
match die.tag() {
gimli::DW_TAG_const_type => {
return Ok(format!("const {}", get_base_type_name(die, unit, context)?));
}
gimli::DW_TAG_pointer_type => {
return Ok(format!("{}*", get_base_type_name(die, unit, context)?));
}
gimli::DW_TAG_reference_type => {
return Ok(format!("{}&", get_base_type_name(die, unit, context)?));
}
gimli::DW_TAG_array_type => {
return Ok(format!("{}[]", get_base_type_name(die, unit, context)?));
}
_ => (),
}
}
}
_ => (),
};
Ok(String::from("??"))
}
fn replace_pointer_type<R>(
parent_id: write::UnitEntryId,
comp_unit: &mut write::Unit,
wp_die_id: write::UnitEntryId,
entry: &DebuggingInformationEntry<R>,
unit: &Unit<R, R::Offset>,
context: &DebugInputContext<R>,
out_strings: &mut write::StringTable,
pending_die_refs: &mut Vec<(write::UnitEntryId, gimli::DwAt, UnitOffset)>,
) -> Result<write::UnitEntryId, Error>
where
R: Reader,
{
let die_id = comp_unit.add(parent_id, gimli::DW_TAG_structure_type);
let die = comp_unit.get_mut(die_id);
let name = format!(
"WebAssemblyPtrWrapper<{}>",
get_base_type_name(entry, unit, context)?
);
die.set(
gimli::DW_AT_name,
write::AttributeValue::StringRef(out_strings.add(name.as_str())),
);
die.set(gimli::DW_AT_byte_size, write::AttributeValue::Data1(4));
let p_die_id = comp_unit.add(die_id, gimli::DW_TAG_template_type_parameter);
let p_die = comp_unit.get_mut(p_die_id);
p_die.set(
gimli::DW_AT_name,
write::AttributeValue::StringRef(out_strings.add("T")),
);
p_die.set(
gimli::DW_AT_type,
write::AttributeValue::ThisUnitEntryRef(wp_die_id),
);
match entry.attr_value(gimli::DW_AT_type)? {
Some(AttributeValue::UnitRef(ref offset)) => {
pending_die_refs.push((p_die_id, gimli::DW_AT_type, *offset))
}
_ => (),
}
let m_die_id = comp_unit.add(die_id, gimli::DW_TAG_member);
let m_die = comp_unit.get_mut(m_die_id);
m_die.set(
gimli::DW_AT_name,
write::AttributeValue::StringRef(out_strings.add("__ptr")),
);
m_die.set(
gimli::DW_AT_type,
write::AttributeValue::ThisUnitEntryRef(wp_die_id),
);
m_die.set(
gimli::DW_AT_data_member_location,
write::AttributeValue::Data1(0),
);
Ok(die_id)
}
pub(crate) fn clone_unit<'a, R>(
unit: Unit<R, R::Offset>,
context: &DebugInputContext<R>,
addr_tr: &'a AddressTransform,
value_ranges: &'a ValueLabelsRanges,
out_encoding: gimli::Encoding,
module_info: &ModuleVmctxInfo,
out_units: &mut write::UnitTable,
out_strings: &mut write::StringTable,
translated: &mut HashSet<u32>,
) -> Result<(), Error>
where
R: Reader,
{
let mut die_ref_map = HashMap::new();
let mut pending_die_refs = Vec::new();
let mut stack = Vec::new();
// Iterate over all of this compilation unit's entries.
let mut entries = unit.entries();
let (mut comp_unit, file_map, cu_low_pc, wp_die_id, vmctx_die_id) =
if let Some((depth_delta, entry)) = entries.next_dfs()? {
assert!(depth_delta == 0);
let (out_line_program, debug_line_offset, file_map) = clone_line_program(
&unit,
entry,
addr_tr,
out_encoding,
context.debug_str,
context.debug_line,
out_strings,
)?;
if entry.tag() == gimli::DW_TAG_compile_unit {
let unit_id = out_units.add(write::Unit::new(out_encoding, out_line_program));
let comp_unit = out_units.get_mut(unit_id);
let root_id = comp_unit.root();
die_ref_map.insert(entry.offset(), root_id);
let cu_low_pc = if let Some(AttributeValue::Addr(addr)) =
entry.attr_value(gimli::DW_AT_low_pc)?
{
addr
} else {
// FIXME? return Err(TransformError("No low_pc for unit header").into());
0
};
clone_die_attributes(
entry,
context,
addr_tr,
None,
unit.encoding(),
comp_unit,
root_id,
None,
None,
cu_low_pc,
out_strings,
&die_ref_map,
&mut pending_die_refs,
FileAttributeContext::Root(Some(debug_line_offset)),
)?;
let (wp_die_id, vmctx_die_id) =
add_internal_types(comp_unit, root_id, out_strings, module_info);
stack.push(root_id);
(comp_unit, file_map, cu_low_pc, wp_die_id, vmctx_die_id)
} else {
return Err(TransformError("Unexpected unit header").into());
}
} else {
return Ok(()); // empty
};
let mut skip_at_depth = None;
let mut current_frame_base = InheritedAttr::new();
let mut current_value_range = InheritedAttr::new();
let mut current_scope_ranges = InheritedAttr::new();
while let Some((depth_delta, entry)) = entries.next_dfs()? {
let depth_delta = if let Some((depth, cached)) = skip_at_depth {
let new_depth = depth + depth_delta;
if new_depth > 0 {
skip_at_depth = Some((new_depth, cached));
continue;
}
skip_at_depth = None;
new_depth + cached
} else {
depth_delta
};
if !context
.reachable
.contains(&entry.offset().to_unit_section_offset(&unit))
{
// entry is not reachable: discarding all its info.
skip_at_depth = Some((0, depth_delta));
continue;
}
let new_stack_len = stack.len().wrapping_add(depth_delta as usize);
current_frame_base.update(new_stack_len);
current_scope_ranges.update(new_stack_len);
current_value_range.update(new_stack_len);
let range_builder = if entry.tag() == gimli::DW_TAG_subprogram {
let range_builder = RangeInfoBuilder::from_subprogram_die(
entry,
context,
unit.encoding(),
addr_tr,
cu_low_pc,
)?;
if let RangeInfoBuilder::Function(func_index) = range_builder {
if let Some(frame_info) =
get_function_frame_info(module_info, func_index, value_ranges)
{
current_value_range.push(new_stack_len, frame_info);
}
translated.insert(func_index.index() as u32);
current_scope_ranges.push(new_stack_len, range_builder.get_ranges(addr_tr));
Some(range_builder)
} else {
// FIXME current_scope_ranges.push()
None
}
} else {
let high_pc = entry.attr_value(gimli::DW_AT_high_pc)?;
let ranges = entry.attr_value(gimli::DW_AT_ranges)?;
if high_pc.is_some() || ranges.is_some() {
let range_builder =
RangeInfoBuilder::from(entry, context, unit.encoding(), cu_low_pc)?;
current_scope_ranges.push(new_stack_len, range_builder.get_ranges(addr_tr));
Some(range_builder)
} else {
None
}
};
if depth_delta <= 0 {
for _ in depth_delta..1 {
stack.pop();
}
} else {
assert!(depth_delta == 1);
}
if let Some(AttributeValue::Exprloc(expr)) = entry.attr_value(gimli::DW_AT_frame_base)? {
if let Some(expr) = compile_expression(&expr, unit.encoding(), None)? {
current_frame_base.push(new_stack_len, expr);
}
}
let parent = stack.last().unwrap();
if entry.tag() == gimli::DW_TAG_pointer_type {
// Wrap pointer types.
// TODO reference types?
let die_id = replace_pointer_type(
*parent,
comp_unit,
wp_die_id,
entry,
&unit,
context,
out_strings,
&mut pending_die_refs,
)?;
stack.push(die_id);
assert!(stack.len() == new_stack_len);
die_ref_map.insert(entry.offset(), die_id);
continue;
}
let die_id = comp_unit.add(*parent, entry.tag());
stack.push(die_id);
assert!(stack.len() == new_stack_len);
die_ref_map.insert(entry.offset(), die_id);
clone_die_attributes(
entry,
context,
addr_tr,
current_value_range.top(),
unit.encoding(),
&mut comp_unit,
die_id,
range_builder,
current_scope_ranges.top(),
cu_low_pc,
out_strings,
&die_ref_map,
&mut pending_die_refs,
FileAttributeContext::Children(&file_map, current_frame_base.top()),
)?;
if entry.tag() == gimli::DW_TAG_subprogram && !current_scope_ranges.is_empty() {
append_vmctx_info(
comp_unit,
die_id,
vmctx_die_id,
addr_tr,
current_value_range.top(),
current_scope_ranges.top().expect("range"),
out_strings,
)?;
}
}
for (die_id, attr_name, offset) in pending_die_refs {
let die = comp_unit.get_mut(die_id);
if let Some(unit_id) = die_ref_map.get(&offset) {
die.set(attr_name, write::AttributeValue::ThisUnitEntryRef(*unit_id));
} else {
// TODO check why loosing DIEs
}
}
Ok(())
}

View File

@@ -0,0 +1,155 @@
use alloc::vec::Vec;
use cranelift_wasm::DefinedFuncIndex;
use failure::Error;
use wasmtime_environ::{ModuleVmctxInfo, ValueLabelsRanges};
use gimli;
use gimli::write;
use super::address_transform::AddressTransform;
use super::expression::{CompiledExpression, FunctionFrameInfo};
pub(crate) fn add_internal_types(
comp_unit: &mut write::Unit,
root_id: write::UnitEntryId,
out_strings: &mut write::StringTable,
module_info: &ModuleVmctxInfo,
) -> (write::UnitEntryId, write::UnitEntryId) {
let wp_die_id = comp_unit.add(root_id, gimli::DW_TAG_base_type);
let wp_die = comp_unit.get_mut(wp_die_id);
wp_die.set(
gimli::DW_AT_name,
write::AttributeValue::StringRef(out_strings.add("WebAssemblyPtr")),
);
wp_die.set(gimli::DW_AT_byte_size, write::AttributeValue::Data1(4));
wp_die.set(
gimli::DW_AT_encoding,
write::AttributeValue::Encoding(gimli::DW_ATE_unsigned),
);
let memory_byte_die_id = comp_unit.add(root_id, gimli::DW_TAG_base_type);
let memory_byte_die = comp_unit.get_mut(memory_byte_die_id);
memory_byte_die.set(
gimli::DW_AT_name,
write::AttributeValue::StringRef(out_strings.add("u8")),
);
memory_byte_die.set(
gimli::DW_AT_encoding,
write::AttributeValue::Encoding(gimli::DW_ATE_unsigned),
);
memory_byte_die.set(gimli::DW_AT_byte_size, write::AttributeValue::Data1(1));
let memory_bytes_die_id = comp_unit.add(root_id, gimli::DW_TAG_pointer_type);
let memory_bytes_die = comp_unit.get_mut(memory_bytes_die_id);
memory_bytes_die.set(
gimli::DW_AT_name,
write::AttributeValue::StringRef(out_strings.add("u8*")),
);
memory_bytes_die.set(
gimli::DW_AT_type,
write::AttributeValue::ThisUnitEntryRef(memory_byte_die_id),
);
let memory_offset = module_info.memory_offset;
let vmctx_die_id = comp_unit.add(root_id, gimli::DW_TAG_structure_type);
let vmctx_die = comp_unit.get_mut(vmctx_die_id);
vmctx_die.set(
gimli::DW_AT_name,
write::AttributeValue::StringRef(out_strings.add("WasmtimeVMContext")),
);
vmctx_die.set(
gimli::DW_AT_byte_size,
write::AttributeValue::Data4(memory_offset as u32 + 8),
);
let m_die_id = comp_unit.add(vmctx_die_id, gimli::DW_TAG_member);
let m_die = comp_unit.get_mut(m_die_id);
m_die.set(
gimli::DW_AT_name,
write::AttributeValue::StringRef(out_strings.add("memory")),
);
m_die.set(
gimli::DW_AT_type,
write::AttributeValue::ThisUnitEntryRef(memory_bytes_die_id),
);
m_die.set(
gimli::DW_AT_data_member_location,
write::AttributeValue::Udata(memory_offset as u64),
);
let vmctx_ptr_die_id = comp_unit.add(root_id, gimli::DW_TAG_pointer_type);
let vmctx_ptr_die = comp_unit.get_mut(vmctx_ptr_die_id);
vmctx_ptr_die.set(
gimli::DW_AT_name,
write::AttributeValue::StringRef(out_strings.add("WasmtimeVMContext*")),
);
vmctx_ptr_die.set(
gimli::DW_AT_type,
write::AttributeValue::ThisUnitEntryRef(vmctx_die_id),
);
(wp_die_id, vmctx_ptr_die_id)
}
pub(crate) fn append_vmctx_info(
comp_unit: &mut write::Unit,
parent_id: write::UnitEntryId,
vmctx_die_id: write::UnitEntryId,
addr_tr: &AddressTransform,
frame_info: Option<&FunctionFrameInfo>,
scope_ranges: &[(u64, u64)],
out_strings: &mut write::StringTable,
) -> Result<(), Error> {
let loc = {
let endian = gimli::RunTimeEndian::Little;
let expr = CompiledExpression::vmctx();
let mut locs = Vec::new();
for (begin, length, data) in
expr.build_with_locals(scope_ranges, addr_tr, frame_info, endian)
{
locs.push(write::Location::StartLength {
begin,
length,
data,
});
}
let list_id = comp_unit.locations.add(write::LocationList(locs));
write::AttributeValue::LocationListRef(list_id)
};
let var_die_id = comp_unit.add(parent_id, gimli::DW_TAG_variable);
let var_die = comp_unit.get_mut(var_die_id);
var_die.set(
gimli::DW_AT_name,
write::AttributeValue::StringRef(out_strings.add("__vmctx")),
);
var_die.set(
gimli::DW_AT_type,
write::AttributeValue::ThisUnitEntryRef(vmctx_die_id),
);
var_die.set(gimli::DW_AT_location, loc);
Ok(())
}
pub(crate) fn get_function_frame_info<'a, 'b, 'c>(
module_info: &'b ModuleVmctxInfo,
func_index: DefinedFuncIndex,
value_ranges: &'c ValueLabelsRanges,
) -> Option<FunctionFrameInfo<'a>>
where
'b: 'a,
'c: 'a,
{
if let Some(value_ranges) = value_ranges.get(func_index) {
let frame_info = FunctionFrameInfo {
value_ranges,
memory_offset: module_info.memory_offset,
stack_slots: &module_info.stack_slots[func_index],
};
Some(frame_info)
} else {
None
}
}