diff --git a/wasmtime-debug/Cargo.toml b/wasmtime-debug/Cargo.toml index 65933474698c..a5521c8e94c3 100644 --- a/wasmtime-debug/Cargo.toml +++ b/wasmtime-debug/Cargo.toml @@ -12,7 +12,7 @@ readme = "README.md" edition = "2018" [dependencies] -gimli = "0.17.0" +gimli = "0.19.0" wasmparser = { version = "0.32.1" } cranelift-codegen = "0.33.0" cranelift-entity = "0.33.0" diff --git a/wasmtime-debug/src/address_transform.rs b/wasmtime-debug/src/address_transform.rs index 595e76f5dfee..6f5599d585ce 100644 --- a/wasmtime-debug/src/address_transform.rs +++ b/wasmtime-debug/src/address_transform.rs @@ -3,9 +3,9 @@ use cranelift_entity::{EntityRef, PrimaryMap}; use cranelift_wasm::DefinedFuncIndex; use gimli::write; use std::collections::BTreeMap; -use std::ops::Bound::{Included, Unbounded}; +use std::iter::FromIterator; use std::vec::Vec; -use wasmtime_environ::AddressTransforms; +use wasmtime_environ::ModuleAddressMap; pub type GeneratedAddress = usize; pub type WasmAddress = u64; @@ -26,13 +26,16 @@ pub struct FunctionMap { #[derive(Debug)] pub struct AddressTransform { - lookup: BTreeMap, + lookup: Vec<( + WasmAddress, + (SymbolIndex, GeneratedAddress, GeneratedAddress), + )>, map: PrimaryMap, func_ranges: Vec<(usize, usize)>, } impl AddressTransform { - pub fn new(at: &AddressTransforms, wasm_file: &WasmFileInfo) -> Self { + pub fn new(at: &ModuleAddressMap, wasm_file: &WasmFileInfo) -> Self { let code_section_offset = wasm_file.code_section_offset; let function_offsets = &wasm_file.function_offsets_and_sizes; let mut lookup = BTreeMap::new(); @@ -50,7 +53,7 @@ impl AddressTransform { (index, ft.body_offset, ft.body_offset), ); let mut fn_map = Vec::new(); - for t in &ft.locations { + for t in &ft.instructions { if t.srcloc.is_default() { // TODO extend some range if possible continue; @@ -76,6 +79,9 @@ impl AddressTransform { addresses: fn_map.into_boxed_slice(), }); } + + let lookup = Vec::from_iter(lookup.into_iter()); + AddressTransform { lookup, map, @@ -83,14 +89,27 @@ impl AddressTransform { } } + pub fn can_translate_address(&self, addr: u64) -> bool { + self.translate(addr).is_some() + } + pub fn translate(&self, addr: u64) -> Option { if addr == 0 { // It's normally 0 for debug info without the linked code. return None; } - let search = self.lookup.range((Unbounded, Included(addr))); - if let Some((_, value)) = search.last() { - return Some(write::Address::Relative { + let found = match self.lookup.binary_search_by(|entry| entry.0.cmp(&addr)) { + Ok(i) => Some(&self.lookup[i].1), + Err(i) => { + if i > 0 { + Some(&self.lookup[i - 1].1) + } else { + None + } + } + }; + if let Some(value) = found { + return Some(write::Address::Symbol { symbol: value.0, addend: value.1 as i64, }); @@ -106,11 +125,11 @@ impl AddressTransform { return None; } if let ( - Some(write::Address::Relative { + Some(write::Address::Symbol { symbol: s1, addend: a, }), - Some(write::Address::Relative { + Some(write::Address::Symbol { symbol: s2, addend: b, }), diff --git a/wasmtime-debug/src/gc.rs b/wasmtime-debug/src/gc.rs new file mode 100644 index 000000000000..16a54d1a63a2 --- /dev/null +++ b/wasmtime-debug/src/gc.rs @@ -0,0 +1,232 @@ +use crate::address_transform::AddressTransform; +use gimli::constants; +use gimli::read; +use gimli::{Reader, UnitSectionOffset}; +use std::collections::{HashMap, HashSet}; +use std::vec::Vec; + +#[derive(Debug)] +pub struct Dependencies { + edges: HashMap>, + roots: HashSet, +} + +impl Dependencies { + fn new() -> Dependencies { + Dependencies { + edges: HashMap::new(), + roots: HashSet::new(), + } + } + + fn add_edge(&mut self, a: UnitSectionOffset, b: UnitSectionOffset) { + use std::collections::hash_map::Entry; + match self.edges.entry(a) { + Entry::Occupied(mut o) => { + o.get_mut().insert(b); + } + Entry::Vacant(v) => { + let mut set = HashSet::new(); + set.insert(b); + v.insert(set); + } + } + } + + fn add_root(&mut self, root: UnitSectionOffset) { + self.roots.insert(root); + } + + pub fn get_reachable(&self) -> HashSet { + let mut reachable = self.roots.clone(); + let mut queue = Vec::new(); + for i in self.roots.iter() { + if let Some(deps) = self.edges.get(i) { + for j in deps { + if reachable.contains(j) { + continue; + } + reachable.insert(*j); + queue.push(*j); + } + } + } + while let Some(i) = queue.pop() { + if let Some(deps) = self.edges.get(&i) { + for j in deps { + if reachable.contains(j) { + continue; + } + reachable.insert(*j); + queue.push(*j); + } + } + } + reachable + } +} + +pub fn build_dependencies>( + dwarf: &read::Dwarf, + at: &AddressTransform, +) -> read::Result { + let mut deps = Dependencies::new(); + let mut units = dwarf.units(); + while let Some(unit) = units.next()? { + build_unit_dependencies(unit, dwarf, at, &mut deps)?; + } + Ok(deps) +} + +fn build_unit_dependencies>( + header: read::CompilationUnitHeader, + dwarf: &read::Dwarf, + at: &AddressTransform, + deps: &mut Dependencies, +) -> read::Result<()> { + let unit = dwarf.unit(header)?; + let mut tree = unit.entries_tree(None)?; + let root = tree.root()?; + build_die_dependencies(root, dwarf, &unit, at, deps)?; + Ok(()) +} + +fn has_die_back_edge>(die: &read::DebuggingInformationEntry) -> bool { + match die.tag() { + constants::DW_TAG_variable + | constants::DW_TAG_constant + | constants::DW_TAG_inlined_subroutine + | constants::DW_TAG_lexical_block + | constants::DW_TAG_label + | constants::DW_TAG_with_stmt + | constants::DW_TAG_try_block + | constants::DW_TAG_catch_block + | constants::DW_TAG_template_type_parameter + | constants::DW_TAG_member + | constants::DW_TAG_formal_parameter => true, + _ => false, + } +} + +fn has_valid_code_range>( + die: &read::DebuggingInformationEntry, + dwarf: &read::Dwarf, + unit: &read::Unit, + at: &AddressTransform, +) -> read::Result { + match die.tag() { + constants::DW_TAG_subprogram => { + if let Some(ranges_attr) = die.attr_value(constants::DW_AT_ranges)? { + let offset = match ranges_attr { + read::AttributeValue::RangeListsRef(val) => val, + read::AttributeValue::DebugRngListsIndex(index) => { + dwarf.ranges_offset(unit, index)? + } + _ => return Ok(false), + }; + let mut has_valid_base = if let Some(read::AttributeValue::Addr(low_pc)) = + die.attr_value(constants::DW_AT_low_pc)? + { + Some(at.can_translate_address(low_pc)) + } else { + None + }; + let mut it = dwarf.ranges.raw_ranges(offset, unit.encoding())?; + while let Some(range) = it.next()? { + // If at least one of the range addresses can be converted, + // declaring code range as valid. + match range { + read::RawRngListEntry::AddressOrOffsetPair { .. } + if has_valid_base.is_some() => + { + if has_valid_base.unwrap() { + return Ok(true); + } + } + read::RawRngListEntry::StartEnd { begin, .. } + | read::RawRngListEntry::StartLength { begin, .. } + | read::RawRngListEntry::AddressOrOffsetPair { begin, .. } => { + if at.can_translate_address(begin) { + return Ok(true); + } + } + read::RawRngListEntry::StartxEndx { begin, .. } + | read::RawRngListEntry::StartxLength { begin, .. } => { + let addr = dwarf.address(unit, begin)?; + if at.can_translate_address(addr) { + return Ok(true); + } + } + read::RawRngListEntry::BaseAddress { addr } => { + has_valid_base = Some(at.can_translate_address(addr)); + } + read::RawRngListEntry::BaseAddressx { addr } => { + let addr = dwarf.address(unit, addr)?; + has_valid_base = Some(at.can_translate_address(addr)); + } + read::RawRngListEntry::OffsetPair { .. } => (), + } + } + return Ok(false); + } else if let Some(low_pc) = die.attr_value(constants::DW_AT_low_pc)? { + if let read::AttributeValue::Addr(a) = low_pc { + return Ok(at.can_translate_address(a)); + } + } + } + _ => (), + } + Ok(false) +} + +fn build_die_dependencies>( + die: read::EntriesTreeNode, + dwarf: &read::Dwarf, + unit: &read::Unit, + at: &AddressTransform, + deps: &mut Dependencies, +) -> read::Result<()> { + let entry = die.entry(); + let offset = entry.offset().to_unit_section_offset(unit); + let mut attrs = entry.attrs(); + while let Some(attr) = attrs.next()? { + build_attr_dependencies(&attr, offset, dwarf, unit, at, deps)?; + } + + let mut children = die.children(); + while let Some(child) = children.next()? { + let child_entry = child.entry(); + let child_offset = child_entry.offset().to_unit_section_offset(unit); + deps.add_edge(child_offset, offset); + if has_die_back_edge(child_entry) { + deps.add_edge(offset, child_offset); + } + if has_valid_code_range(child_entry, dwarf, unit, at)? { + deps.add_root(child_offset); + } + build_die_dependencies(child, dwarf, unit, at, deps)?; + } + Ok(()) +} + +fn build_attr_dependencies>( + attr: &read::Attribute, + offset: UnitSectionOffset, + _dwarf: &read::Dwarf, + unit: &read::Unit, + _at: &AddressTransform, + deps: &mut Dependencies, +) -> read::Result<()> { + match attr.value() { + read::AttributeValue::UnitRef(val) => { + let ref_offset = val.to_unit_section_offset(unit); + deps.add_edge(offset, ref_offset); + } + read::AttributeValue::DebugInfoRef(val) => { + let ref_offset = UnitSectionOffset::DebugInfoOffset(val); + deps.add_edge(offset, ref_offset); + } + _ => (), + } + Ok(()) +} diff --git a/wasmtime-debug/src/lib.rs b/wasmtime-debug/src/lib.rs index 0b2ee5fa4491..cc6e3e6b49ae 100644 --- a/wasmtime-debug/src/lib.rs +++ b/wasmtime-debug/src/lib.rs @@ -3,14 +3,14 @@ use cranelift_codegen::isa::TargetFrontendConfig; use faerie::{Artifact, Decl}; use failure::Error; use target_lexicon::{BinaryFormat, Triple}; +use wasmtime_environ::ModuleAddressMap; -pub use crate::read_debuginfo::{read_debuginfo, DebugInfoData}; -pub use crate::transform::transform_dwarf; +pub use crate::read_debuginfo::{read_debuginfo, DebugInfoData, WasmFileInfo}; +pub use crate::transform::{transform_dwarf, ModuleVmctxInfo, ValueLabelsRanges}; pub use crate::write_debuginfo::{emit_dwarf, ResolvedSymbol, SymbolResolver}; -use wasmtime_environ::AddressTransforms; - mod address_transform; +mod gc; mod read_debuginfo; mod transform; mod write_debuginfo; @@ -30,11 +30,11 @@ pub fn emit_debugsections( obj: &mut Artifact, target_config: &TargetFrontendConfig, debuginfo_data: &DebugInfoData, - at: &AddressTransforms, + at: &ModuleAddressMap, ) -> Result<(), Error> { - let dwarf = transform_dwarf(target_config, debuginfo_data, at)?; let resolver = FunctionRelocResolver {}; - emit_dwarf(obj, dwarf, &resolver); + let dwarf = transform_dwarf(target_config, debuginfo_data, at)?; + emit_dwarf(obj, dwarf, &resolver)?; Ok(()) } @@ -53,7 +53,7 @@ pub fn emit_debugsections_image( triple: Triple, target_config: &TargetFrontendConfig, debuginfo_data: &DebugInfoData, - at: &AddressTransforms, + at: &ModuleAddressMap, funcs: &Vec<(*const u8, usize)>, ) -> Result, Error> { let ref func_offsets = funcs @@ -61,8 +61,8 @@ pub fn emit_debugsections_image( .map(|(ptr, _)| *ptr as u64) .collect::>(); let mut obj = Artifact::new(triple, String::from("module")); - let dwarf = transform_dwarf(target_config, debuginfo_data, at)?; let resolver = ImageRelocResolver { func_offsets }; + let dwarf = transform_dwarf(target_config, debuginfo_data, at)?; // Assuming all functions in the same code block, looking min/max of its range. assert!(funcs.len() > 0); @@ -76,7 +76,7 @@ pub fn emit_debugsections_image( let body = unsafe { ::std::slice::from_raw_parts(segment_body.0, segment_body.1) }; obj.declare_with("all", Decl::function(), body.to_vec())?; - emit_dwarf(&mut obj, dwarf, &resolver); + emit_dwarf(&mut obj, dwarf, &resolver)?; // LLDB is too "magical" about mach-o, generating elf let mut bytes = obj.emit_as(BinaryFormat::Elf)?; diff --git a/wasmtime-debug/src/transform.rs b/wasmtime-debug/src/transform.rs index 038be623b526..9af0daca0872 100644 --- a/wasmtime-debug/src/transform.rs +++ b/wasmtime-debug/src/transform.rs @@ -1,17 +1,21 @@ use crate::address_transform::AddressTransform; +use crate::gc::build_dependencies; pub use crate::read_debuginfo::DebugInfoData; +use cranelift_codegen::ir; use cranelift_codegen::isa::TargetFrontendConfig; -use cranelift_entity::EntityRef; +use cranelift_entity::{EntityRef, PrimaryMap}; +use cranelift_wasm::DefinedFuncIndex; use failure::Error; -use std::collections::{BTreeMap, HashMap}; -use std::ops::Bound::{Included, Unbounded}; +use std::collections::{BTreeMap, HashMap, HashSet}; +use std::iter::FromIterator; +use wasmtime_environ::ModuleAddressMap; use gimli; use gimli::{ - AttributeValue, CompilationUnitHeader, DebugAbbrev, DebugAddr, DebugAddrBase, DebugLine, - DebugLineOffset, DebugStr, DebuggingInformationEntry, LineEncoding, LocationLists, RangeLists, - UnitOffset, + AttributeValue, DebugAddr, DebugAddrBase, DebugLine, DebugLineOffset, DebugStr, + DebuggingInformationEntry, LineEncoding, LocationLists, RangeLists, Unit, UnitOffset, + UnitSectionOffset, }; use gimli::write; @@ -24,25 +28,26 @@ impl<'input, Endian> Reader for gimli::EndianSlice<'input, Endian> where Endian: #[fail(display = "Debug info transform error: {}", _0)] pub struct TransformError(&'static str); -pub struct TransformedDwarf { - pub encoding: gimli::Encoding, - pub strings: write::StringTable, - pub units: write::UnitTable, - pub line_strings: write::LineStringTable, - pub range_lists: write::RangeListTable, +/// Module `vmctx` related info. +pub struct ModuleVmctxInfo { + pub memory_offset: i64, + pub stack_slots: PrimaryMap, } +/// Value ranges for functions. +pub type ValueLabelsRanges = PrimaryMap; + struct DebugInputContext<'a, R> where R: Reader, { - debug_abbrev: &'a DebugAbbrev, debug_str: &'a DebugStr, debug_line: &'a DebugLine, debug_addr: &'a DebugAddr, debug_addr_base: DebugAddrBase, rnglists: &'a RangeLists, loclists: &'a LocationLists, + reachable: HashSet, } type PendingDieRef = (write::UnitEntryId, gimli::DwAt, UnitOffset); @@ -84,7 +89,7 @@ where write::AttributeValue::Udata(subprogram_range.unwrap().1) } AttributeValue::Addr(u) => { - let addr = addr_tr.translate(u).unwrap_or(write::Address::Absolute(0)); + let addr = addr_tr.translate(u).unwrap_or(write::Address::Constant(0)); if attr.name() == gimli::DW_AT_low_pc { low_pc = Some((u, addr)); } @@ -243,7 +248,7 @@ enum ReadLineProgramState { } fn clone_line_program( - unit: &CompilationUnitHeader, + unit: &Unit, root: &DebuggingInformationEntry, addr_tr: &AddressTransform, out_encoding: &gimli::Encoding, @@ -277,7 +282,7 @@ where let program = debug_line.program( offset, - unit.address_size(), + unit.header.address_size(), comp_dir.and_then(|val| val.string_value(&debug_str)), comp_name.and_then(|val| val.string_value(&debug_str)), ); @@ -367,21 +372,26 @@ where saved_rows.insert(row.address(), saved_row); } + let saved_rows = Vec::from_iter(saved_rows.into_iter()); + for (i, map) in addr_tr.map() { let symbol = i.index(); let base_addr = map.offset; - out_program.begin_sequence(Some(write::Address::Relative { symbol, addend: 0 })); + 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 mut saved_row = saved_rows.get(&addr_map.wasm); - if saved_row.is_none() { - // No direct match -- repeat search with range. - saved_row = saved_rows - .range((Unbounded, Included(addr_map.wasm))) - .last() - .map(|p| p.1); - } + let saved_row = + match saved_rows.binary_search_by(|entry| entry.0.cmp(&addr_map.wasm)) { + 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, @@ -440,9 +450,9 @@ where let low_pc = entry.attr_value(gimli::DW_AT_low_pc)?; if let Some(AttributeValue::Addr(addr)) = low_pc { let transformed = addr_tr.translate(addr); - if let Some(write::Address::Relative { symbol, .. }) = transformed { + if let Some(write::Address::Symbol { symbol, .. }) = transformed { let range = addr_tr.func_range(symbol); - let addr = write::Address::Relative { + let addr = write::Address::Symbol { symbol, addend: range.0 as i64, }; @@ -454,7 +464,7 @@ where } fn clone_unit<'a, R>( - unit: &CompilationUnitHeader, + unit: Unit, context: &DebugInputContext, addr_tr: &'a AddressTransform, out_encoding: &gimli::Encoding, @@ -464,18 +474,16 @@ fn clone_unit<'a, R>( where R: Reader, { - let abbrevs = unit.abbreviations(context.debug_abbrev)?; - 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(&abbrevs); + let mut entries = unit.entries(); let (comp_unit, file_map) = 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, + &unit, entry, addr_tr, out_encoding, @@ -526,14 +534,17 @@ where } 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 range = if entry.tag() == gimli::DW_TAG_subprogram { - let range = get_subprogram_range(entry, addr_tr)?; - if range.is_none() { - // Subprogram was not compiled: discarding all its info. - skip_at_depth = Some((0, depth_delta)); - continue; - } - range + get_subprogram_range(entry, addr_tr)? } else { None }; @@ -580,16 +591,19 @@ where pub fn transform_dwarf( target_config: &TargetFrontendConfig, di: &DebugInfoData, - at: &wasmtime_environ::AddressTransforms, -) -> Result { + at: &ModuleAddressMap, +) -> Result { + let addr_tr = AddressTransform::new(at, &di.wasm_file); + let reachable = build_dependencies(&di.dwarf, &addr_tr)?.get_reachable(); + let context = DebugInputContext { - debug_abbrev: &di.dwarf.debug_abbrev, 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, }; let out_encoding = gimli::Encoding { @@ -600,16 +614,14 @@ pub fn transform_dwarf( address_size: target_config.pointer_bytes(), }; - let addr_tr = AddressTransform::new(at, &di.wasm_file); - let mut out_strings = write::StringTable::default(); let mut out_units = write::UnitTable::default(); - let out_range_lists = write::RangeListTable::default(); let out_line_strings = write::LineStringTable::default(); let mut iter = di.dwarf.debug_info.units(); - while let Some(ref unit) = iter.next().unwrap_or(None) { + while let Some(unit) = iter.next().unwrap_or(None) { + let unit = di.dwarf.unit(unit)?; clone_unit( unit, &context, @@ -620,22 +632,10 @@ pub fn transform_dwarf( )?; } - // let unit_range_list = write::RangeList(Vec::new()); - // let unit_range_list_id = out_range_lists.add(unit_range_list.clone()); - // let unit = dwarf.units.get_mut(self.unit_id); - // let root = unit.root(); - // let root = unit.get_mut(root); - // root.set( - // gimli::DW_AT_ranges, - // AttributeValue::RangeListRef(unit_range_list_id), - // ); - - //println!("{:?} \n====\n {:?}", di, at); - Ok(TransformedDwarf { - encoding: out_encoding, - strings: out_strings, + Ok(write::Dwarf { units: out_units, + line_programs: vec![], line_strings: out_line_strings, - range_lists: out_range_lists, + strings: out_strings, }) } diff --git a/wasmtime-debug/src/write_debuginfo.rs b/wasmtime-debug/src/write_debuginfo.rs index b4d787899565..6920472c3bb8 100644 --- a/wasmtime-debug/src/write_debuginfo.rs +++ b/wasmtime-debug/src/write_debuginfo.rs @@ -1,14 +1,11 @@ -use crate::transform::TransformedDwarf; - -use gimli::write::{ - Address, DebugAbbrev, DebugInfo, DebugLine, DebugLineStr, DebugRanges, DebugRngLists, DebugStr, - EndianVec, Result, SectionId, Sections, Writer, -}; -use gimli::RunTimeEndian; +use gimli::write::{Address, Dwarf, EndianVec, Result, Sections, Writer}; +use gimli::{RunTimeEndian, SectionId}; use faerie::artifact::{Decl, SectionKind}; use faerie::*; +use std::result; +#[derive(Clone)] struct DebugReloc { offset: u32, size: u8, @@ -16,38 +13,6 @@ struct DebugReloc { addend: i64, } -macro_rules! decl_section { - ($artifact:ident . $section:ident = $name:expr) => { - $artifact - .declare_with( - SectionId::$section.name(), - Decl::section(SectionKind::Debug), - $name.0.writer.into_vec(), - ) - .unwrap(); - }; -} - -macro_rules! sect_relocs { - ($artifact:ident . $section:ident = $name:expr) => { - for reloc in $name.0.relocs { - $artifact - .link_with( - faerie::Link { - from: SectionId::$section.name(), - to: &reloc.name, - at: u64::from(reloc.offset), - }, - faerie::Reloc::Debug { - size: reloc.size, - addend: reloc.addend as i32, - }, - ) - .expect("faerie relocation error"); - } - }; -} - pub enum ResolvedSymbol { PhysicalAddress(u64), Reloc { name: String, addend: i64 }, @@ -59,75 +24,48 @@ pub trait SymbolResolver { pub fn emit_dwarf( artifact: &mut Artifact, - mut dwarf: TransformedDwarf, + mut dwarf: Dwarf, symbol_resolver: &SymbolResolver, -) { +) -> result::Result<(), failure::Error> { let endian = RunTimeEndian::Little; - let debug_abbrev = DebugAbbrev::from(WriterRelocate::new(endian, symbol_resolver)); - let debug_info = DebugInfo::from(WriterRelocate::new(endian, symbol_resolver)); - let debug_str = DebugStr::from(WriterRelocate::new(endian, symbol_resolver)); - let debug_line = DebugLine::from(WriterRelocate::new(endian, symbol_resolver)); - let debug_ranges = DebugRanges::from(WriterRelocate::new(endian, symbol_resolver)); - let debug_rnglists = DebugRngLists::from(WriterRelocate::new(endian, symbol_resolver)); - let debug_line_str = DebugLineStr::from(WriterRelocate::new(endian, symbol_resolver)); - - let mut sections = Sections { - debug_abbrev, - debug_info, - debug_line, - debug_line_str, - debug_ranges, - debug_rnglists, - debug_str, - }; - - let debug_str_offsets = dwarf.strings.write(&mut sections.debug_str).unwrap(); - let debug_line_str_offsets = dwarf - .line_strings - .write(&mut sections.debug_line_str) - .unwrap(); - dwarf - .units - .write(&mut sections, &debug_line_str_offsets, &debug_str_offsets) - .unwrap(); - - decl_section!(artifact.DebugAbbrev = sections.debug_abbrev); - decl_section!(artifact.DebugInfo = sections.debug_info); - decl_section!(artifact.DebugStr = sections.debug_str); - decl_section!(artifact.DebugLine = sections.debug_line); - - let debug_ranges_not_empty = !sections.debug_ranges.0.writer.slice().is_empty(); - if debug_ranges_not_empty { - decl_section!(artifact.DebugRanges = sections.debug_ranges); - } - let debug_rnglists_not_empty = !sections.debug_rnglists.0.writer.slice().is_empty(); - if debug_rnglists_not_empty { - decl_section!(artifact.DebugRngLists = sections.debug_rnglists); - } - - sect_relocs!(artifact.DebugAbbrev = sections.debug_abbrev); - sect_relocs!(artifact.DebugInfo = sections.debug_info); - sect_relocs!(artifact.DebugStr = sections.debug_str); - sect_relocs!(artifact.DebugLine = sections.debug_line); - - if debug_ranges_not_empty { - sect_relocs!(artifact.DebugRanges = sections.debug_ranges); - } - - if debug_rnglists_not_empty { - sect_relocs!(artifact.DebugRngLists = sections.debug_rnglists); - } + let mut sections = Sections::new(WriterRelocate::new(endian, symbol_resolver)); + dwarf.write(&mut sections)?; + sections.for_each_mut(|id, s| -> result::Result<(), failure::Error> { + artifact.declare_with( + id.name(), + Decl::section(SectionKind::Debug), + s.writer.take(), + ) + })?; + sections.for_each_mut(|id, s| -> result::Result<(), failure::Error> { + for reloc in &s.relocs { + artifact.link_with( + faerie::Link { + from: id.name(), + to: &reloc.name, + at: u64::from(reloc.offset), + }, + faerie::Reloc::Debug { + size: reloc.size, + addend: reloc.addend as i32, + }, + )?; + } + Ok(()) + })?; + Ok(()) } -struct WriterRelocate<'a> { +#[derive(Clone)] +pub struct WriterRelocate<'a> { relocs: Vec, writer: EndianVec, symbol_resolver: &'a SymbolResolver, } impl<'a> WriterRelocate<'a> { - fn new(endian: RunTimeEndian, symbol_resolver: &'a SymbolResolver) -> Self { + pub fn new(endian: RunTimeEndian, symbol_resolver: &'a SymbolResolver) -> Self { WriterRelocate { relocs: Vec::new(), writer: EndianVec::new(endian), @@ -157,10 +95,10 @@ impl<'a> Writer for WriterRelocate<'a> { fn write_address(&mut self, address: Address, size: u8) -> Result<()> { match address { - Address::Absolute(val) => self.write_word(val, size), - Address::Relative { symbol, addend } => { + Address::Constant(val) => self.write_udata(val, size), + Address::Symbol { symbol, addend } => { match self.symbol_resolver.resolve_symbol(symbol, addend as i64) { - ResolvedSymbol::PhysicalAddress(addr) => self.write_word(addr, size), + ResolvedSymbol::PhysicalAddress(addr) => self.write_udata(addr, size), ResolvedSymbol::Reloc { name, addend } => { let offset = self.len() as u64; self.relocs.push(DebugReloc { @@ -169,7 +107,7 @@ impl<'a> Writer for WriterRelocate<'a> { name, addend, }); - self.write_word(addend as u64, size) + self.write_udata(addend as u64, size) } } } @@ -185,7 +123,7 @@ impl<'a> Writer for WriterRelocate<'a> { name, addend: val as i64, }); - self.write_word(val as u64, size) + self.write_udata(val as u64, size) } fn write_offset_at( @@ -202,6 +140,6 @@ impl<'a> Writer for WriterRelocate<'a> { name, addend: val as i64, }); - self.write_word_at(offset, val as u64, size) + self.write_udata_at(offset, val as u64, size) } } diff --git a/wasmtime-environ/src/address_map.rs b/wasmtime-environ/src/address_map.rs new file mode 100644 index 000000000000..7ce2c28a2665 --- /dev/null +++ b/wasmtime-environ/src/address_map.rs @@ -0,0 +1,37 @@ +//! Data structures to provide transformation of the source +// addresses of a WebAssembly module into the native code. + +use cranelift_codegen::ir; +use cranelift_entity::PrimaryMap; +use cranelift_wasm::DefinedFuncIndex; +use std::vec::Vec; + +/// Single source location to generated address mapping. +#[derive(Debug, Clone)] +pub struct InstructionAddressMap { + /// Original source location. + pub srcloc: ir::SourceLoc, + + /// Generated instructions offset. + pub code_offset: usize, + + /// Generated instructions length. + pub code_len: usize, +} + +/// Function and its instructions addresses mappings. +#[derive(Debug, Clone)] +pub struct FunctionAddressMap { + /// Instructions maps. + /// The array is sorted by the InstructionAddressMap::code_offset field. + pub instructions: Vec, + + /// Generated function body offset if applicable, otherwise 0. + pub body_offset: usize, + + /// Generated function body length. + pub body_len: usize, +} + +/// Module functions addresses mappings. +pub type ModuleAddressMap = PrimaryMap; diff --git a/wasmtime-environ/src/compilation.rs b/wasmtime-environ/src/compilation.rs index 1a130a518059..c699cbc98445 100644 --- a/wasmtime-environ/src/compilation.rs +++ b/wasmtime-environ/src/compilation.rs @@ -1,6 +1,7 @@ //! A `Compilation` contains the compiled function bodies for a WebAssembly //! module. +use crate::address_map::ModuleAddressMap; use crate::module; use crate::module_environ::FunctionBodyData; use cranelift_codegen::{binemit, ir, isa, CodegenError}; @@ -139,35 +140,6 @@ pub enum CompileError { Codegen(CodegenError), } -/// Single address point transform. -#[derive(Debug)] -pub struct InstructionAddressTransform { - /// Original source location. - pub srcloc: ir::SourceLoc, - - /// Generated instructions offset. - pub code_offset: usize, - - /// Generated instructions length. - pub code_len: usize, -} - -/// Function and its instructions transforms. -#[derive(Debug)] -pub struct FunctionAddressTransform { - /// Instructions transforms - pub locations: Vec, - - /// Generated function body offset if applicable, otherwise 0. - pub body_offset: usize, - - /// Generated function body length. - pub body_len: usize, -} - -/// Function AddressTransforms collection. -pub type AddressTransforms = PrimaryMap; - /// An implementation of a compiler from parsed WebAssembly module to native code. pub trait Compiler { /// Compile a parsed module with the given `TargetIsa`. @@ -176,5 +148,5 @@ pub trait Compiler { function_body_inputs: PrimaryMap>, isa: &dyn isa::TargetIsa, generate_debug_info: bool, - ) -> Result<(Compilation, Relocations, AddressTransforms), CompileError>; + ) -> Result<(Compilation, Relocations, ModuleAddressMap), CompileError>; } diff --git a/wasmtime-environ/src/cranelift.rs b/wasmtime-environ/src/cranelift.rs index 7c5e8e6190eb..9f12dbd0c255 100644 --- a/wasmtime-environ/src/cranelift.rs +++ b/wasmtime-environ/src/cranelift.rs @@ -1,8 +1,8 @@ //! Support for compiling with Cranelift. +use crate::address_map::{FunctionAddressMap, InstructionAddressMap, ModuleAddressMap}; use crate::compilation::{ - AddressTransforms, CodeAndJTOffsets, Compilation, CompileError, FunctionAddressTransform, - InstructionAddressTransform, Relocation, RelocationTarget, Relocations, + CodeAndJTOffsets, Compilation, CompileError, Relocation, RelocationTarget, Relocations, }; use crate::func_environ::{ get_func_name, get_imported_memory32_grow_name, get_imported_memory32_size_name, @@ -89,10 +89,7 @@ impl RelocSink { } } -fn get_address_transform( - context: &Context, - isa: &isa::TargetIsa, -) -> Vec { +fn get_address_transform(context: &Context, isa: &isa::TargetIsa) -> Vec { let mut result = Vec::new(); let func = &context.func; @@ -103,7 +100,7 @@ fn get_address_transform( for ebb in ebbs { for (offset, inst, size) in func.inst_offsets(ebb, &encinfo) { let srcloc = func.srclocs[inst]; - result.push(InstructionAddressTransform { + result.push(InstructionAddressMap { srcloc, code_offset: offset as usize, code_len: size as usize, @@ -125,7 +122,7 @@ impl crate::compilation::Compiler for Cranelift { function_body_inputs: PrimaryMap>, isa: &dyn isa::TargetIsa, generate_debug_info: bool, - ) -> Result<(Compilation, Relocations, AddressTransforms), CompileError> { + ) -> Result<(Compilation, Relocations, ModuleAddressMap), CompileError> { let mut functions = PrimaryMap::with_capacity(function_body_inputs.len()); let mut relocations = PrimaryMap::with_capacity(function_body_inputs.len()); let mut address_transforms = PrimaryMap::with_capacity(function_body_inputs.len()); @@ -162,8 +159,9 @@ impl crate::compilation::Compiler for Cranelift { let address_transform = if generate_debug_info { let body_len = code_buf.len(); let at = get_address_transform(&context, isa); - Some(FunctionAddressTransform { - locations: at, + + Some(FunctionAddressMap { + instructions: at, body_offset: 0, body_len, }) diff --git a/wasmtime-environ/src/lib.rs b/wasmtime-environ/src/lib.rs index 4c0d214fbf50..39dea60dcc04 100644 --- a/wasmtime-environ/src/lib.rs +++ b/wasmtime-environ/src/lib.rs @@ -37,6 +37,7 @@ extern crate std; #[macro_use] extern crate failure_derive; +mod address_map; mod compilation; mod func_environ; mod module; @@ -48,9 +49,9 @@ pub mod cranelift; #[cfg(feature = "lightbeam")] pub mod lightbeam; +pub use crate::address_map::{FunctionAddressMap, InstructionAddressMap, ModuleAddressMap}; pub use crate::compilation::{ - AddressTransforms, Compilation, CompileError, Compiler, InstructionAddressTransform, - Relocation, RelocationTarget, Relocations, + Compilation, CompileError, Compiler, Relocation, RelocationTarget, Relocations, }; pub use crate::cranelift::Cranelift; #[cfg(feature = "lightbeam")]