Don't re-parse wasm for debuginfo (#2085)
* Don't re-parse wasm for debuginfo This commit updates debuginfo parsing to happen during the main translation of the original wasm module. This avoid re-parsing the wasm module twice (at least the section-level headers). Additionally this ties debuginfo directly to a `ModuleTranslation` which makes it easier to process debuginfo for nested modules in the upcoming module linking proposal. The changes here are summarized by taking the `read_debuginfo` function and merging it with the main module translation that happens which is driven by cranelift. Some new hooks were added to the module environment trait to support this, but most of it was integrating with existing hooks. * Fix tests in debug crate
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -2466,6 +2466,7 @@ dependencies = [
|
|||||||
"errno",
|
"errno",
|
||||||
"file-per-thread-logger",
|
"file-per-thread-logger",
|
||||||
"filetime",
|
"filetime",
|
||||||
|
"gimli 0.21.0",
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"libc",
|
"libc",
|
||||||
|
|||||||
@@ -743,13 +743,11 @@ impl<'data> ModuleEnvironment<'data> for DummyEnvironment {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn declare_module_name(&mut self, name: &'data str) -> WasmResult<()> {
|
fn declare_module_name(&mut self, name: &'data str) {
|
||||||
self.module_name = Some(String::from(name));
|
self.module_name = Some(String::from(name));
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn declare_func_name(&mut self, func_index: FuncIndex, name: &'data str) -> WasmResult<()> {
|
fn declare_func_name(&mut self, func_index: FuncIndex, name: &'data str) {
|
||||||
self.function_names[func_index] = String::from(name);
|
self.function_names[func_index] = String::from(name);
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,6 +66,20 @@ impl TryFrom<wasmparser::Type> for WasmType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<WasmType> for wasmparser::Type {
|
||||||
|
fn from(ty: WasmType) -> wasmparser::Type {
|
||||||
|
match ty {
|
||||||
|
WasmType::I32 => wasmparser::Type::I32,
|
||||||
|
WasmType::I64 => wasmparser::Type::I64,
|
||||||
|
WasmType::F32 => wasmparser::Type::F32,
|
||||||
|
WasmType::F64 => wasmparser::Type::F64,
|
||||||
|
WasmType::V128 => wasmparser::Type::V128,
|
||||||
|
WasmType::FuncRef => wasmparser::Type::FuncRef,
|
||||||
|
WasmType::ExternRef => wasmparser::Type::ExternRef,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// WebAssembly function type -- equivalent of `wasmparser`'s FuncType.
|
/// WebAssembly function type -- equivalent of `wasmparser`'s FuncType.
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||||
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
|
||||||
@@ -743,10 +757,13 @@ pub trait ModuleEnvironment<'data>: TargetEnvironment {
|
|||||||
/// Declare a passive data segment.
|
/// Declare a passive data segment.
|
||||||
fn declare_passive_data(&mut self, data_index: DataIndex, data: &'data [u8]) -> WasmResult<()>;
|
fn declare_passive_data(&mut self, data_index: DataIndex, data: &'data [u8]) -> WasmResult<()>;
|
||||||
|
|
||||||
|
/// Indicates how many functions the code section reports and the byte
|
||||||
|
/// offset of where the code sections starts.
|
||||||
|
fn reserve_function_bodies(&mut self, bodies: u32, code_section_offset: u64) {
|
||||||
|
drop((bodies, code_section_offset));
|
||||||
|
}
|
||||||
|
|
||||||
/// Provides the contents of a function body.
|
/// Provides the contents of a function body.
|
||||||
///
|
|
||||||
/// Note there's no `reserve_function_bodies` function because the number of
|
|
||||||
/// functions is already provided by `reserve_func_types`.
|
|
||||||
fn define_function_body(
|
fn define_function_body(
|
||||||
&mut self,
|
&mut self,
|
||||||
module_translation_state: &ModuleTranslationState,
|
module_translation_state: &ModuleTranslationState,
|
||||||
@@ -773,16 +790,19 @@ pub trait ModuleEnvironment<'data>: TargetEnvironment {
|
|||||||
///
|
///
|
||||||
/// By default this does nothing, but implementations can use this to read
|
/// By default this does nothing, but implementations can use this to read
|
||||||
/// the module name subsection of the custom name section if desired.
|
/// the module name subsection of the custom name section if desired.
|
||||||
fn declare_module_name(&mut self, _name: &'data str) -> WasmResult<()> {
|
fn declare_module_name(&mut self, _name: &'data str) {}
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Declares the name of a function to the environment.
|
/// Declares the name of a function to the environment.
|
||||||
///
|
///
|
||||||
/// By default this does nothing, but implementations can use this to read
|
/// By default this does nothing, but implementations can use this to read
|
||||||
/// the function name subsection of the custom name section if desired.
|
/// the function name subsection of the custom name section if desired.
|
||||||
fn declare_func_name(&mut self, _func_index: FuncIndex, _name: &'data str) -> WasmResult<()> {
|
fn declare_func_name(&mut self, _func_index: FuncIndex, _name: &'data str) {}
|
||||||
Ok(())
|
|
||||||
|
/// Declares the name of a function's local to the environment.
|
||||||
|
///
|
||||||
|
/// By default this does nothing, but implementations can use this to read
|
||||||
|
/// the local name subsection of the custom name section if desired.
|
||||||
|
fn declare_local_name(&mut self, _func_index: FuncIndex, _local_index: u32, _name: &'data str) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Indicates that a custom section has been found in the wasm file
|
/// Indicates that a custom section has been found in the wasm file
|
||||||
|
|||||||
@@ -59,7 +59,10 @@ pub fn translate_module<'data>(
|
|||||||
parse_element_section(elements, environ)?;
|
parse_element_section(elements, environ)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Payload::CodeSectionStart { .. } => {}
|
Payload::CodeSectionStart { count, range, .. } => {
|
||||||
|
environ.reserve_function_bodies(count, range.start as u64);
|
||||||
|
}
|
||||||
|
|
||||||
Payload::CodeSectionEntry(code) => {
|
Payload::CodeSectionEntry(code) => {
|
||||||
let mut code = code.get_binary_reader();
|
let mut code = code.get_binary_reader();
|
||||||
let size = code.bytes_remaining();
|
let size = code.bytes_remaining();
|
||||||
@@ -91,7 +94,14 @@ pub fn translate_module<'data>(
|
|||||||
name: "name",
|
name: "name",
|
||||||
data,
|
data,
|
||||||
data_offset,
|
data_offset,
|
||||||
} => parse_name_section(NameSectionReader::new(data, data_offset)?, environ)?,
|
} => {
|
||||||
|
let result = NameSectionReader::new(data, data_offset)
|
||||||
|
.map_err(|e| e.into())
|
||||||
|
.and_then(|s| parse_name_section(s, environ));
|
||||||
|
if let Err(e) = result {
|
||||||
|
log::warn!("failed to parse name section {:?}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Payload::CustomSection { name, data, .. } => environ.custom_section(name, data)?,
|
Payload::CustomSection { name, data, .. } => environ.custom_section(name, data)?,
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ use crate::translation_utils::{
|
|||||||
tabletype_to_type, type_to_type, DataIndex, ElemIndex, FuncIndex, Global, GlobalIndex,
|
tabletype_to_type, type_to_type, DataIndex, ElemIndex, FuncIndex, Global, GlobalIndex,
|
||||||
GlobalInit, Memory, MemoryIndex, SignatureIndex, Table, TableElementType, TableIndex,
|
GlobalInit, Memory, MemoryIndex, SignatureIndex, Table, TableElementType, TableIndex,
|
||||||
};
|
};
|
||||||
use crate::{wasm_unsupported, HashMap};
|
use crate::wasm_unsupported;
|
||||||
use core::convert::TryFrom;
|
use core::convert::TryFrom;
|
||||||
use core::convert::TryInto;
|
use core::convert::TryInto;
|
||||||
use cranelift_codegen::ir::immediates::V128Imm;
|
use cranelift_codegen::ir::immediates::V128Imm;
|
||||||
@@ -26,8 +26,8 @@ use wasmparser::{
|
|||||||
self, Data, DataKind, DataSectionReader, Element, ElementItem, ElementItems, ElementKind,
|
self, Data, DataKind, DataSectionReader, Element, ElementItem, ElementItems, ElementKind,
|
||||||
ElementSectionReader, Export, ExportSectionReader, ExternalKind, FunctionSectionReader,
|
ElementSectionReader, Export, ExportSectionReader, ExternalKind, FunctionSectionReader,
|
||||||
GlobalSectionReader, GlobalType, ImportSectionEntryType, ImportSectionReader,
|
GlobalSectionReader, GlobalType, ImportSectionEntryType, ImportSectionReader,
|
||||||
MemorySectionReader, MemoryType, NameSectionReader, Naming, NamingReader, Operator,
|
MemorySectionReader, MemoryType, NameSectionReader, Naming, Operator, TableSectionReader, Type,
|
||||||
TableSectionReader, Type, TypeDef, TypeSectionReader,
|
TypeDef, TypeSectionReader,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Parses the Type section of the wasm module.
|
/// Parses the Type section of the wasm module.
|
||||||
@@ -404,53 +404,40 @@ pub fn parse_data_section<'data>(
|
|||||||
|
|
||||||
/// Parses the Name section of the wasm module.
|
/// Parses the Name section of the wasm module.
|
||||||
pub fn parse_name_section<'data>(
|
pub fn parse_name_section<'data>(
|
||||||
mut names: NameSectionReader<'data>,
|
names: NameSectionReader<'data>,
|
||||||
environ: &mut dyn ModuleEnvironment<'data>,
|
environ: &mut dyn ModuleEnvironment<'data>,
|
||||||
) -> WasmResult<()> {
|
) -> WasmResult<()> {
|
||||||
while let Ok(subsection) = names.read() {
|
for subsection in names {
|
||||||
match subsection {
|
match subsection? {
|
||||||
wasmparser::Name::Function(function_subsection) => {
|
wasmparser::Name::Function(f) => {
|
||||||
if let Some(function_names) = function_subsection
|
let mut names = f.get_map()?;
|
||||||
.get_map()
|
for _ in 0..names.get_count() {
|
||||||
.ok()
|
let Naming { index, name } = names.read()?;
|
||||||
.and_then(parse_function_name_subsection)
|
// We reserve `u32::MAX` for our own use in cranelift-entity.
|
||||||
{
|
if index != u32::max_value() {
|
||||||
for (index, name) in function_names {
|
environ.declare_func_name(FuncIndex::from_u32(index), name);
|
||||||
environ.declare_func_name(index, name)?;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
wasmparser::Name::Module(module) => {
|
wasmparser::Name::Module(module) => {
|
||||||
if let Ok(name) = module.get_name() {
|
let name = module.get_name()?;
|
||||||
environ.declare_module_name(name)?;
|
environ.declare_module_name(name);
|
||||||
|
}
|
||||||
|
wasmparser::Name::Local(l) => {
|
||||||
|
let mut reader = l.get_function_local_reader()?;
|
||||||
|
for _ in 0..reader.get_count() {
|
||||||
|
let f = reader.read()?;
|
||||||
|
if f.func_index == u32::max_value() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let mut map = f.get_map()?;
|
||||||
|
for _ in 0..map.get_count() {
|
||||||
|
let Naming { index, name } = map.read()?;
|
||||||
|
environ.declare_local_name(FuncIndex::from_u32(f.func_index), index, name)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
wasmparser::Name::Local(_) => {}
|
}
|
||||||
};
|
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_function_name_subsection(
|
|
||||||
mut naming_reader: NamingReader<'_>,
|
|
||||||
) -> Option<HashMap<FuncIndex, &str>> {
|
|
||||||
let mut function_names = HashMap::new();
|
|
||||||
for _ in 0..naming_reader.get_count() {
|
|
||||||
let Naming { index, name } = naming_reader.read().ok()?;
|
|
||||||
if index == std::u32::MAX {
|
|
||||||
// We reserve `u32::MAX` for our own use in cranelift-entity.
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
if function_names
|
|
||||||
.insert(FuncIndex::from_u32(index), name)
|
|
||||||
.is_some()
|
|
||||||
{
|
|
||||||
// If the function index has been previously seen, then we
|
|
||||||
// break out of the loop and early return `None`, because these
|
|
||||||
// should be unique.
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some(function_names)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -6,11 +6,9 @@ use anyhow::{bail, ensure, Error};
|
|||||||
use object::{RelocationEncoding, RelocationKind};
|
use object::{RelocationEncoding, RelocationKind};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
pub use crate::read_debuginfo::{read_debuginfo, DebugInfoData, WasmFileInfo};
|
|
||||||
pub use crate::write_debuginfo::{emit_dwarf, DwarfSection, DwarfSectionRelocTarget};
|
pub use crate::write_debuginfo::{emit_dwarf, DwarfSection, DwarfSectionRelocTarget};
|
||||||
|
|
||||||
mod gc;
|
mod gc;
|
||||||
mod read_debuginfo;
|
|
||||||
mod transform;
|
mod transform;
|
||||||
mod write_debuginfo;
|
mod write_debuginfo;
|
||||||
|
|
||||||
|
|||||||
@@ -1,244 +0,0 @@
|
|||||||
use anyhow::{bail, Result};
|
|
||||||
use gimli::{
|
|
||||||
DebugAbbrev, DebugAddr, DebugInfo, DebugLine, DebugLineStr, DebugLoc, DebugLocLists,
|
|
||||||
DebugRanges, DebugRngLists, DebugStr, DebugStrOffsets, DebugTypes, EndianSlice, LittleEndian,
|
|
||||||
LocationLists, RangeLists,
|
|
||||||
};
|
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::path::PathBuf;
|
|
||||||
use wasmparser::{self, NameSectionReader, Parser, Payload, TypeDef};
|
|
||||||
|
|
||||||
trait Reader: gimli::Reader<Offset = usize, Endian = LittleEndian> {}
|
|
||||||
|
|
||||||
impl<'input> Reader for gimli::EndianSlice<'input, LittleEndian> {}
|
|
||||||
|
|
||||||
pub use wasmparser::Type as WasmType;
|
|
||||||
|
|
||||||
pub type Dwarf<'input> = gimli::Dwarf<gimli::EndianSlice<'input, LittleEndian>>;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct FunctionMetadata {
|
|
||||||
pub params: Box<[WasmType]>,
|
|
||||||
pub locals: Box<[(u32, WasmType)]>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct WasmFileInfo {
|
|
||||||
pub path: Option<PathBuf>,
|
|
||||||
pub code_section_offset: u64,
|
|
||||||
pub imported_func_count: u32,
|
|
||||||
pub funcs: Box<[FunctionMetadata]>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct NameSection {
|
|
||||||
pub module_name: Option<String>,
|
|
||||||
pub func_names: HashMap<u32, String>,
|
|
||||||
pub locals_names: HashMap<u32, HashMap<u32, String>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct DebugInfoData<'a> {
|
|
||||||
pub dwarf: Dwarf<'a>,
|
|
||||||
pub name_section: Option<NameSection>,
|
|
||||||
pub wasm_file: WasmFileInfo,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn convert_sections<'a>(sections: HashMap<&str, &'a [u8]>) -> Result<Dwarf<'a>> {
|
|
||||||
const EMPTY_SECTION: &[u8] = &[];
|
|
||||||
|
|
||||||
let endian = LittleEndian;
|
|
||||||
let debug_str = DebugStr::new(sections.get(".debug_str").unwrap_or(&EMPTY_SECTION), endian);
|
|
||||||
let debug_abbrev = DebugAbbrev::new(
|
|
||||||
sections.get(".debug_abbrev").unwrap_or(&EMPTY_SECTION),
|
|
||||||
endian,
|
|
||||||
);
|
|
||||||
let debug_info = DebugInfo::new(
|
|
||||||
sections.get(".debug_info").unwrap_or(&EMPTY_SECTION),
|
|
||||||
endian,
|
|
||||||
);
|
|
||||||
let debug_line = DebugLine::new(
|
|
||||||
sections.get(".debug_line").unwrap_or(&EMPTY_SECTION),
|
|
||||||
endian,
|
|
||||||
);
|
|
||||||
let debug_addr = DebugAddr::from(EndianSlice::new(
|
|
||||||
sections.get(".debug_addr").unwrap_or(&EMPTY_SECTION),
|
|
||||||
endian,
|
|
||||||
));
|
|
||||||
|
|
||||||
let debug_line_str = DebugLineStr::from(EndianSlice::new(
|
|
||||||
sections.get(".debug_line_str").unwrap_or(&EMPTY_SECTION),
|
|
||||||
endian,
|
|
||||||
));
|
|
||||||
let debug_str_sup = DebugStr::from(EndianSlice::new(EMPTY_SECTION, endian));
|
|
||||||
|
|
||||||
let debug_ranges = match sections.get(".debug_ranges") {
|
|
||||||
Some(section) => DebugRanges::new(section, endian),
|
|
||||||
None => DebugRanges::new(EMPTY_SECTION, endian),
|
|
||||||
};
|
|
||||||
let debug_rnglists = match sections.get(".debug_rnglists") {
|
|
||||||
Some(section) => DebugRngLists::new(section, endian),
|
|
||||||
None => DebugRngLists::new(EMPTY_SECTION, endian),
|
|
||||||
};
|
|
||||||
let ranges = RangeLists::new(debug_ranges, debug_rnglists);
|
|
||||||
|
|
||||||
let debug_loc = match sections.get(".debug_loc") {
|
|
||||||
Some(section) => DebugLoc::new(section, endian),
|
|
||||||
None => DebugLoc::new(EMPTY_SECTION, endian),
|
|
||||||
};
|
|
||||||
let debug_loclists = match sections.get(".debug_loclists") {
|
|
||||||
Some(section) => DebugLocLists::new(section, endian),
|
|
||||||
None => DebugLocLists::new(EMPTY_SECTION, endian),
|
|
||||||
};
|
|
||||||
let locations = LocationLists::new(debug_loc, debug_loclists);
|
|
||||||
|
|
||||||
let debug_str_offsets = DebugStrOffsets::from(EndianSlice::new(
|
|
||||||
sections.get(".debug_str_offsets").unwrap_or(&EMPTY_SECTION),
|
|
||||||
endian,
|
|
||||||
));
|
|
||||||
|
|
||||||
if sections.contains_key(".debug_types") {
|
|
||||||
bail!("Unexpected .debug_types");
|
|
||||||
}
|
|
||||||
|
|
||||||
let debug_types = DebugTypes::from(EndianSlice::new(EMPTY_SECTION, endian));
|
|
||||||
|
|
||||||
Ok(Dwarf {
|
|
||||||
debug_abbrev,
|
|
||||||
debug_addr,
|
|
||||||
debug_info,
|
|
||||||
debug_line,
|
|
||||||
debug_line_str,
|
|
||||||
debug_str,
|
|
||||||
debug_str_offsets,
|
|
||||||
debug_str_sup,
|
|
||||||
debug_types,
|
|
||||||
locations,
|
|
||||||
ranges,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_name_section(reader: wasmparser::NameSectionReader) -> wasmparser::Result<NameSection> {
|
|
||||||
let mut module_name = None;
|
|
||||||
let mut func_names = HashMap::new();
|
|
||||||
let mut locals_names = HashMap::new();
|
|
||||||
for i in reader.into_iter() {
|
|
||||||
match i? {
|
|
||||||
wasmparser::Name::Module(m) => {
|
|
||||||
module_name = Some(String::from(m.get_name()?));
|
|
||||||
}
|
|
||||||
wasmparser::Name::Function(f) => {
|
|
||||||
let mut reader = f.get_map()?;
|
|
||||||
while let Ok(naming) = reader.read() {
|
|
||||||
func_names.insert(naming.index, String::from(naming.name));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
wasmparser::Name::Local(l) => {
|
|
||||||
let mut reader = l.get_function_local_reader()?;
|
|
||||||
while let Ok(f) = reader.read() {
|
|
||||||
let mut names = HashMap::new();
|
|
||||||
let mut reader = f.get_map()?;
|
|
||||||
while let Ok(naming) = reader.read() {
|
|
||||||
names.insert(naming.index, String::from(naming.name));
|
|
||||||
}
|
|
||||||
locals_names.insert(f.func_index, names);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let result = NameSection {
|
|
||||||
module_name,
|
|
||||||
func_names,
|
|
||||||
locals_names,
|
|
||||||
};
|
|
||||||
Ok(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn read_debuginfo(data: &[u8]) -> Result<DebugInfoData> {
|
|
||||||
let mut sections = HashMap::new();
|
|
||||||
let mut name_section = None;
|
|
||||||
let mut code_section_offset = 0;
|
|
||||||
let mut imported_func_count = 0;
|
|
||||||
|
|
||||||
let mut signatures_params: Vec<Box<[WasmType]>> = Vec::new();
|
|
||||||
let mut func_params_refs: Vec<usize> = Vec::new();
|
|
||||||
let mut func_locals: Vec<Box<[(u32, WasmType)]>> = Vec::new();
|
|
||||||
|
|
||||||
for payload in Parser::new(0).parse_all(data) {
|
|
||||||
match payload? {
|
|
||||||
Payload::CustomSection {
|
|
||||||
name,
|
|
||||||
data,
|
|
||||||
data_offset,
|
|
||||||
} => {
|
|
||||||
if name.starts_with(".debug_") {
|
|
||||||
sections.insert(name, data);
|
|
||||||
} else if name == "name" {
|
|
||||||
if let Ok(reader) = NameSectionReader::new(data, data_offset) {
|
|
||||||
if let Ok(section) = read_name_section(reader) {
|
|
||||||
name_section = Some(section);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Payload::TypeSection(s) => {
|
|
||||||
signatures_params = s
|
|
||||||
.into_iter()
|
|
||||||
.map(|ft| {
|
|
||||||
if let Ok(TypeDef::Func(ft)) = ft {
|
|
||||||
Ok(ft.params)
|
|
||||||
} else {
|
|
||||||
unimplemented!("module linking not implemented yet")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect::<Result<Vec<_>>>()?;
|
|
||||||
}
|
|
||||||
Payload::ImportSection(s) => {
|
|
||||||
for i in s {
|
|
||||||
if let wasmparser::ImportSectionEntryType::Function(_) = i?.ty {
|
|
||||||
imported_func_count += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Payload::FunctionSection(s) => {
|
|
||||||
func_params_refs = s
|
|
||||||
.into_iter()
|
|
||||||
.map(|index| Ok(index? as usize))
|
|
||||||
.collect::<Result<Vec<_>>>()?;
|
|
||||||
}
|
|
||||||
Payload::CodeSectionStart { range, .. } => {
|
|
||||||
code_section_offset = range.start as u64;
|
|
||||||
}
|
|
||||||
Payload::CodeSectionEntry(body) => {
|
|
||||||
let locals = body.get_locals_reader()?;
|
|
||||||
let locals = locals
|
|
||||||
.into_iter()
|
|
||||||
.collect::<Result<Vec<_>, _>>()?
|
|
||||||
.into_boxed_slice();
|
|
||||||
func_locals.push(locals);
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let func_meta = func_params_refs
|
|
||||||
.into_iter()
|
|
||||||
.zip(func_locals.into_iter())
|
|
||||||
.map(|(params_index, locals)| FunctionMetadata {
|
|
||||||
params: signatures_params[params_index].clone(),
|
|
||||||
locals,
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
let dwarf = convert_sections(sections)?;
|
|
||||||
Ok(DebugInfoData {
|
|
||||||
dwarf,
|
|
||||||
name_section,
|
|
||||||
wasm_file: WasmFileInfo {
|
|
||||||
path: None,
|
|
||||||
code_section_offset,
|
|
||||||
imported_func_count,
|
|
||||||
funcs: func_meta.into_boxed_slice(),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
use crate::WasmFileInfo;
|
|
||||||
use gimli::write;
|
use gimli::write;
|
||||||
use more_asserts::assert_le;
|
use more_asserts::assert_le;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
@@ -6,6 +5,7 @@ use std::iter::FromIterator;
|
|||||||
use wasmtime_environ::entity::{EntityRef, PrimaryMap};
|
use wasmtime_environ::entity::{EntityRef, PrimaryMap};
|
||||||
use wasmtime_environ::ir::SourceLoc;
|
use wasmtime_environ::ir::SourceLoc;
|
||||||
use wasmtime_environ::wasm::DefinedFuncIndex;
|
use wasmtime_environ::wasm::DefinedFuncIndex;
|
||||||
|
use wasmtime_environ::WasmFileInfo;
|
||||||
use wasmtime_environ::{FunctionAddressMap, ModuleAddressMap};
|
use wasmtime_environ::{FunctionAddressMap, ModuleAddressMap};
|
||||||
|
|
||||||
pub type GeneratedAddress = usize;
|
pub type GeneratedAddress = usize;
|
||||||
@@ -602,11 +602,11 @@ impl AddressTransform {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{build_function_lookup, get_wasm_code_offset, AddressTransform};
|
use super::{build_function_lookup, get_wasm_code_offset, AddressTransform};
|
||||||
use crate::read_debuginfo::WasmFileInfo;
|
|
||||||
use gimli::write::Address;
|
use gimli::write::Address;
|
||||||
use std::iter::FromIterator;
|
use std::iter::FromIterator;
|
||||||
use wasmtime_environ::entity::PrimaryMap;
|
use wasmtime_environ::entity::PrimaryMap;
|
||||||
use wasmtime_environ::ir::SourceLoc;
|
use wasmtime_environ::ir::SourceLoc;
|
||||||
|
use wasmtime_environ::WasmFileInfo;
|
||||||
use wasmtime_environ::{FunctionAddressMap, InstructionAddressMap, ModuleAddressMap};
|
use wasmtime_environ::{FunctionAddressMap, InstructionAddressMap, ModuleAddressMap};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -724,7 +724,7 @@ mod tests {
|
|||||||
path: None,
|
path: None,
|
||||||
code_section_offset: 1,
|
code_section_offset: 1,
|
||||||
imported_func_count: 0,
|
imported_func_count: 0,
|
||||||
funcs: Box::new([]),
|
funcs: Vec::new(),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -683,9 +683,9 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn create_mock_address_transform() -> AddressTransform {
|
fn create_mock_address_transform() -> AddressTransform {
|
||||||
use crate::read_debuginfo::WasmFileInfo;
|
|
||||||
use wasmtime_environ::entity::PrimaryMap;
|
use wasmtime_environ::entity::PrimaryMap;
|
||||||
use wasmtime_environ::ir::SourceLoc;
|
use wasmtime_environ::ir::SourceLoc;
|
||||||
|
use wasmtime_environ::WasmFileInfo;
|
||||||
use wasmtime_environ::{FunctionAddressMap, InstructionAddressMap};
|
use wasmtime_environ::{FunctionAddressMap, InstructionAddressMap};
|
||||||
let mut module_map = PrimaryMap::new();
|
let mut module_map = PrimaryMap::new();
|
||||||
let code_section_offset: u32 = 100;
|
let code_section_offset: u32 = 100;
|
||||||
@@ -709,7 +709,7 @@ mod tests {
|
|||||||
});
|
});
|
||||||
let fi = WasmFileInfo {
|
let fi = WasmFileInfo {
|
||||||
code_section_offset: code_section_offset.into(),
|
code_section_offset: code_section_offset.into(),
|
||||||
funcs: Box::new([]),
|
funcs: Vec::new(),
|
||||||
imported_func_count: 0,
|
imported_func_count: 0,
|
||||||
path: None,
|
path: None,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ use self::refs::DebugInfoRefsMap;
|
|||||||
use self::simulate::generate_simulated_dwarf;
|
use self::simulate::generate_simulated_dwarf;
|
||||||
use self::unit::clone_unit;
|
use self::unit::clone_unit;
|
||||||
use crate::gc::build_dependencies;
|
use crate::gc::build_dependencies;
|
||||||
use crate::DebugInfoData;
|
|
||||||
use anyhow::Error;
|
use anyhow::Error;
|
||||||
use gimli::{
|
use gimli::{
|
||||||
write, DebugAddr, DebugLine, DebugLineStr, DebugStr, DebugStrOffsets, LocationLists,
|
write, DebugAddr, DebugLine, DebugLineStr, DebugStr, DebugStrOffsets, LocationLists,
|
||||||
@@ -11,6 +10,7 @@ use gimli::{
|
|||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use wasmtime_environ::isa::TargetIsa;
|
use wasmtime_environ::isa::TargetIsa;
|
||||||
|
use wasmtime_environ::DebugInfoData;
|
||||||
use wasmtime_environ::{ModuleAddressMap, ModuleVmctxInfo, ValueLabelsRanges};
|
use wasmtime_environ::{ModuleAddressMap, ModuleVmctxInfo, ValueLabelsRanges};
|
||||||
|
|
||||||
pub use address_transform::AddressTransform;
|
pub use address_transform::AddressTransform;
|
||||||
|
|||||||
@@ -1,18 +1,19 @@
|
|||||||
use super::expression::{CompiledExpression, FunctionFrameInfo};
|
use super::expression::{CompiledExpression, FunctionFrameInfo};
|
||||||
use super::utils::{add_internal_types, append_vmctx_info, get_function_frame_info};
|
use super::utils::{add_internal_types, append_vmctx_info, get_function_frame_info};
|
||||||
use super::AddressTransform;
|
use super::AddressTransform;
|
||||||
use crate::read_debuginfo::WasmFileInfo;
|
|
||||||
use anyhow::{Context, Error};
|
use anyhow::{Context, Error};
|
||||||
use gimli::write;
|
use gimli::write;
|
||||||
use gimli::{self, LineEncoding};
|
use gimli::{self, LineEncoding};
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
|
||||||
|
use wasmparser::Type as WasmType;
|
||||||
use wasmtime_environ::entity::EntityRef;
|
use wasmtime_environ::entity::EntityRef;
|
||||||
use wasmtime_environ::wasm::{get_vmctx_value_label, DefinedFuncIndex};
|
|
||||||
use wasmtime_environ::{ModuleVmctxInfo, ValueLabelsRanges};
|
|
||||||
|
|
||||||
pub use crate::read_debuginfo::{DebugInfoData, FunctionMetadata, WasmType};
|
|
||||||
use wasmtime_environ::isa::TargetIsa;
|
use wasmtime_environ::isa::TargetIsa;
|
||||||
|
use wasmtime_environ::wasm::{get_vmctx_value_label, DefinedFuncIndex};
|
||||||
|
use wasmtime_environ::WasmFileInfo;
|
||||||
|
use wasmtime_environ::{DebugInfoData, FunctionMetadata};
|
||||||
|
use wasmtime_environ::{ModuleVmctxInfo, ValueLabelsRanges};
|
||||||
|
|
||||||
const PRODUCER_NAME: &str = "wasmtime";
|
const PRODUCER_NAME: &str = "wasmtime";
|
||||||
|
|
||||||
@@ -87,7 +88,7 @@ fn generate_line_info(
|
|||||||
Ok(out_program)
|
Ok(out_program)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_invalid_chars_in_name(s: String) -> Option<String> {
|
fn check_invalid_chars_in_name(s: &str) -> Option<&str> {
|
||||||
if s.contains('\x00') {
|
if s.contains('\x00') {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
@@ -96,16 +97,13 @@ fn check_invalid_chars_in_name(s: String) -> Option<String> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn autogenerate_dwarf_wasm_path(di: &DebugInfoData) -> PathBuf {
|
fn autogenerate_dwarf_wasm_path(di: &DebugInfoData) -> PathBuf {
|
||||||
|
static NEXT_ID: AtomicUsize = AtomicUsize::new(0);
|
||||||
let module_name = di
|
let module_name = di
|
||||||
.name_section
|
.name_section
|
||||||
.as_ref()
|
.module_name
|
||||||
.and_then(|ns| ns.module_name.to_owned())
|
|
||||||
.and_then(check_invalid_chars_in_name)
|
.and_then(check_invalid_chars_in_name)
|
||||||
.unwrap_or_else(|| unsafe {
|
.map(|s| s.to_string())
|
||||||
static mut GEN_ID: u32 = 0;
|
.unwrap_or_else(|| format!("<gen-{}>", NEXT_ID.fetch_add(1, SeqCst)));
|
||||||
GEN_ID += 1;
|
|
||||||
format!("<gen-{}>", GEN_ID)
|
|
||||||
});
|
|
||||||
let path = format!("/<wasm-module>/{}.wasm", module_name);
|
let path = format!("/<wasm-module>/{}.wasm", module_name);
|
||||||
PathBuf::from(path)
|
PathBuf::from(path)
|
||||||
}
|
}
|
||||||
@@ -195,7 +193,7 @@ fn generate_vars(
|
|||||||
scope_ranges: &[(u64, u64)],
|
scope_ranges: &[(u64, u64)],
|
||||||
wasm_types: &WasmTypesDieRefs,
|
wasm_types: &WasmTypesDieRefs,
|
||||||
func_meta: &FunctionMetadata,
|
func_meta: &FunctionMetadata,
|
||||||
locals_names: Option<&HashMap<u32, String>>,
|
locals_names: Option<&HashMap<u32, &str>>,
|
||||||
out_strings: &mut write::StringTable,
|
out_strings: &mut write::StringTable,
|
||||||
isa: &dyn TargetIsa,
|
isa: &dyn TargetIsa,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
@@ -253,7 +251,7 @@ fn generate_vars(
|
|||||||
|
|
||||||
let name_id = match locals_names
|
let name_id = match locals_names
|
||||||
.and_then(|m| m.get(&(var_index as u32)))
|
.and_then(|m| m.get(&(var_index as u32)))
|
||||||
.and_then(|s| check_invalid_chars_in_name(s.to_owned()))
|
.and_then(|s| check_invalid_chars_in_name(s))
|
||||||
{
|
{
|
||||||
Some(n) => out_strings.add(assert_dwarf_str!(n)),
|
Some(n) => out_strings.add(assert_dwarf_str!(n)),
|
||||||
None => out_strings.add(format!("var{}", var_index)),
|
None => out_strings.add(format!("var{}", var_index)),
|
||||||
@@ -297,14 +295,8 @@ pub fn generate_simulated_dwarf(
|
|||||||
.and_then(check_invalid_chars_in_path)
|
.and_then(check_invalid_chars_in_path)
|
||||||
.unwrap_or_else(|| autogenerate_dwarf_wasm_path(di));
|
.unwrap_or_else(|| autogenerate_dwarf_wasm_path(di));
|
||||||
|
|
||||||
let (func_names, locals_names) = if let Some(ref name_section) = di.name_section {
|
let func_names = &di.name_section.func_names;
|
||||||
(
|
let locals_names = &di.name_section.locals_names;
|
||||||
Some(&name_section.func_names),
|
|
||||||
Some(&name_section.locals_names),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
(None, None)
|
|
||||||
};
|
|
||||||
let imported_func_count = di.wasm_file.imported_func_count;
|
let imported_func_count = di.wasm_file.imported_func_count;
|
||||||
|
|
||||||
let (unit, root_id, name_id) = {
|
let (unit, root_id, name_id) = {
|
||||||
@@ -376,8 +368,8 @@ pub fn generate_simulated_dwarf(
|
|||||||
|
|
||||||
let func_index = imported_func_count + (index as u32);
|
let func_index = imported_func_count + (index as u32);
|
||||||
let id = match func_names
|
let id = match func_names
|
||||||
.and_then(|m| m.get(&func_index))
|
.get(&func_index)
|
||||||
.and_then(|s| check_invalid_chars_in_name(s.to_owned()))
|
.and_then(|s| check_invalid_chars_in_name(s))
|
||||||
{
|
{
|
||||||
Some(n) => out_strings.add(assert_dwarf_str!(n)),
|
Some(n) => out_strings.add(assert_dwarf_str!(n)),
|
||||||
None => out_strings.add(format!("wasm-function[{}]", func_index)),
|
None => out_strings.add(format!("wasm-function[{}]", func_index)),
|
||||||
@@ -407,7 +399,7 @@ pub fn generate_simulated_dwarf(
|
|||||||
&[(source_range.0, source_range.1)],
|
&[(source_range.0, source_range.1)],
|
||||||
&wasm_types,
|
&wasm_types,
|
||||||
&di.wasm_file.funcs[index],
|
&di.wasm_file.funcs[index],
|
||||||
locals_names.and_then(|m| m.get(&(index as u32))),
|
locals_names.get(&(index as u32)),
|
||||||
out_strings,
|
out_strings,
|
||||||
isa,
|
isa,
|
||||||
)?;
|
)?;
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
pub use crate::read_debuginfo::{read_debuginfo, DebugInfoData, WasmFileInfo};
|
|
||||||
pub use crate::transform::transform_dwarf;
|
pub use crate::transform::transform_dwarf;
|
||||||
use gimli::write::{Address, Dwarf, EndianVec, FrameTable, Result, Sections, Writer};
|
use gimli::write::{Address, Dwarf, EndianVec, FrameTable, Result, Sections, Writer};
|
||||||
use gimli::{RunTimeEndian, SectionId};
|
use gimli::{RunTimeEndian, SectionId};
|
||||||
use wasmtime_environ::entity::{EntityRef, PrimaryMap};
|
use wasmtime_environ::entity::{EntityRef, PrimaryMap};
|
||||||
use wasmtime_environ::isa::{unwind::UnwindInfo, TargetIsa};
|
use wasmtime_environ::isa::{unwind::UnwindInfo, TargetIsa};
|
||||||
use wasmtime_environ::wasm::DefinedFuncIndex;
|
use wasmtime_environ::wasm::DefinedFuncIndex;
|
||||||
|
use wasmtime_environ::DebugInfoData;
|
||||||
use wasmtime_environ::{ModuleAddressMap, ModuleVmctxInfo, ValueLabelsRanges};
|
use wasmtime_environ::{ModuleAddressMap, ModuleVmctxInfo, ValueLabelsRanges};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ toml = "0.5.5"
|
|||||||
file-per-thread-logger = "0.1.1"
|
file-per-thread-logger = "0.1.1"
|
||||||
more-asserts = "0.2.1"
|
more-asserts = "0.2.1"
|
||||||
cfg-if = "0.1.9"
|
cfg-if = "0.1.9"
|
||||||
|
gimli = "0.21"
|
||||||
|
|
||||||
[target.'cfg(target_os = "windows")'.dependencies]
|
[target.'cfg(target_os = "windows")'.dependencies]
|
||||||
winapi = "0.3.7"
|
winapi = "0.3.7"
|
||||||
|
|||||||
@@ -57,10 +57,7 @@ pub use crate::lightbeam::Lightbeam;
|
|||||||
pub use crate::module::{
|
pub use crate::module::{
|
||||||
EntityIndex, MemoryPlan, MemoryStyle, Module, ModuleLocal, TableElements, TablePlan, TableStyle,
|
EntityIndex, MemoryPlan, MemoryStyle, Module, ModuleLocal, TableElements, TablePlan, TableStyle,
|
||||||
};
|
};
|
||||||
pub use crate::module_environ::{
|
pub use crate::module_environ::*;
|
||||||
translate_signature, DataInitializer, DataInitializerLocation, FunctionBodyData,
|
|
||||||
ModuleEnvironment, ModuleTranslation,
|
|
||||||
};
|
|
||||||
pub use crate::tunables::Tunables;
|
pub use crate::tunables::Tunables;
|
||||||
pub use crate::vmoffsets::{TargetSharedSignatureIndex, VMOffsets, INTERRUPTED};
|
pub use crate::vmoffsets::{TargetSharedSignatureIndex, VMOffsets, INTERRUPTED};
|
||||||
|
|
||||||
|
|||||||
@@ -10,17 +10,17 @@ use cranelift_wasm::{
|
|||||||
TargetEnvironment, WasmError, WasmFuncType, WasmResult,
|
TargetEnvironment, WasmError, WasmFuncType, WasmResult,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
use std::path::PathBuf;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use wasmparser::Type as WasmType;
|
||||||
|
|
||||||
/// Contains function data: byte code and its offset in the module.
|
/// Object containing the standalone environment information.
|
||||||
#[derive(Hash)]
|
pub struct ModuleEnvironment<'data> {
|
||||||
pub struct FunctionBodyData<'a> {
|
/// The result to be filled in.
|
||||||
/// Body byte code.
|
result: ModuleTranslation<'data>,
|
||||||
pub data: &'a [u8],
|
code_index: u32,
|
||||||
|
|
||||||
/// Body offset in the module file.
|
|
||||||
pub module_offset: usize,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The result of translating via `ModuleEnvironment`. Function bodies are not
|
/// The result of translating via `ModuleEnvironment`. Function bodies are not
|
||||||
@@ -44,12 +44,60 @@ pub struct ModuleTranslation<'data> {
|
|||||||
|
|
||||||
/// The decoded Wasm types for the module.
|
/// The decoded Wasm types for the module.
|
||||||
pub module_translation: Option<ModuleTranslationState>,
|
pub module_translation: Option<ModuleTranslationState>,
|
||||||
|
|
||||||
|
/// DWARF debug information, if enabled, parsed from the module.
|
||||||
|
pub debuginfo: Option<DebugInfoData<'data>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Object containing the standalone environment information.
|
/// Contains function data: byte code and its offset in the module.
|
||||||
pub struct ModuleEnvironment<'data> {
|
#[derive(Hash)]
|
||||||
/// The result to be filled in.
|
pub struct FunctionBodyData<'a> {
|
||||||
result: ModuleTranslation<'data>,
|
/// Body byte code.
|
||||||
|
pub data: &'a [u8],
|
||||||
|
|
||||||
|
/// Body offset in the module file.
|
||||||
|
pub module_offset: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
pub struct DebugInfoData<'a> {
|
||||||
|
pub dwarf: Dwarf<'a>,
|
||||||
|
pub name_section: NameSection<'a>,
|
||||||
|
pub wasm_file: WasmFileInfo,
|
||||||
|
debug_loc: gimli::DebugLoc<Reader<'a>>,
|
||||||
|
debug_loclists: gimli::DebugLocLists<Reader<'a>>,
|
||||||
|
debug_ranges: gimli::DebugRanges<Reader<'a>>,
|
||||||
|
debug_rnglists: gimli::DebugRngLists<Reader<'a>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
pub type Dwarf<'input> = gimli::Dwarf<Reader<'input>>;
|
||||||
|
|
||||||
|
type Reader<'input> = gimli::EndianSlice<'input, gimli::LittleEndian>;
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
pub struct NameSection<'a> {
|
||||||
|
pub module_name: Option<&'a str>,
|
||||||
|
pub func_names: HashMap<u32, &'a str>,
|
||||||
|
pub locals_names: HashMap<u32, HashMap<u32, &'a str>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
pub struct WasmFileInfo {
|
||||||
|
pub path: Option<PathBuf>,
|
||||||
|
pub code_section_offset: u64,
|
||||||
|
pub imported_func_count: u32,
|
||||||
|
pub funcs: Vec<FunctionMetadata>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
pub struct FunctionMetadata {
|
||||||
|
pub params: Box<[WasmType]>,
|
||||||
|
pub locals: Box<[(u32, WasmType)]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'data> ModuleEnvironment<'data> {
|
impl<'data> ModuleEnvironment<'data> {
|
||||||
@@ -63,7 +111,13 @@ impl<'data> ModuleEnvironment<'data> {
|
|||||||
data_initializers: Vec::new(),
|
data_initializers: Vec::new(),
|
||||||
tunables: tunables.clone(),
|
tunables: tunables.clone(),
|
||||||
module_translation: None,
|
module_translation: None,
|
||||||
|
debuginfo: if tunables.debug_info {
|
||||||
|
Some(DebugInfoData::default())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
code_index: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,6 +141,42 @@ impl<'data> ModuleEnvironment<'data> {
|
|||||||
.insert(String::from(name), export);
|
.insert(String::from(name), export);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn register_dwarf_section(&mut self, name: &str, data: &'data [u8]) {
|
||||||
|
let info = match &mut self.result.debuginfo {
|
||||||
|
Some(info) => info,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
if !name.starts_with(".debug_") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let dwarf = &mut info.dwarf;
|
||||||
|
let endian = gimli::LittleEndian;
|
||||||
|
let slice = gimli::EndianSlice::new(data, endian);
|
||||||
|
|
||||||
|
match name {
|
||||||
|
".debug_str" => dwarf.debug_str = gimli::DebugStr::new(data, endian),
|
||||||
|
".debug_abbrev" => dwarf.debug_abbrev = gimli::DebugAbbrev::new(data, endian),
|
||||||
|
".debug_info" => dwarf.debug_info = gimli::DebugInfo::new(data, endian),
|
||||||
|
".debug_line" => dwarf.debug_line = gimli::DebugLine::new(data, endian),
|
||||||
|
".debug_addr" => dwarf.debug_addr = gimli::DebugAddr::from(slice),
|
||||||
|
".debug_line_str" => dwarf.debug_line_str = gimli::DebugLineStr::from(slice),
|
||||||
|
".debug_str_sup" => dwarf.debug_str_sup = gimli::DebugStr::from(slice),
|
||||||
|
".debug_ranges" => info.debug_ranges = gimli::DebugRanges::new(data, endian),
|
||||||
|
".debug_rnglists" => info.debug_rnglists = gimli::DebugRngLists::new(data, endian),
|
||||||
|
".debug_loc" => info.debug_loc = gimli::DebugLoc::from(slice),
|
||||||
|
".debug_loclists" => info.debug_loclists = gimli::DebugLocLists::from(slice),
|
||||||
|
".debug_str_offsets" => dwarf.debug_str_offsets = gimli::DebugStrOffsets::from(slice),
|
||||||
|
".debug_types" => dwarf.debug_types = gimli::DebugTypes::from(slice),
|
||||||
|
other => {
|
||||||
|
log::warn!("unknown debug section `{}`", other);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dwarf.ranges = gimli::RangeLists::new(info.debug_ranges, info.debug_rnglists);
|
||||||
|
dwarf.locations = gimli::LocationLists::new(info.debug_loc, info.debug_loclists);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'data> TargetEnvironment for ModuleEnvironment<'data> {
|
impl<'data> TargetEnvironment for ModuleEnvironment<'data> {
|
||||||
@@ -144,6 +234,9 @@ impl<'data> cranelift_wasm::ModuleEnvironment<'data> for ModuleEnvironment<'data
|
|||||||
EntityIndex::Function(func_index),
|
EntityIndex::Function(func_index),
|
||||||
));
|
));
|
||||||
self.result.module.local.num_imported_funcs += 1;
|
self.result.module.local.num_imported_funcs += 1;
|
||||||
|
if let Some(info) = &mut self.result.debuginfo {
|
||||||
|
info.wasm_file.imported_func_count += 1;
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -346,6 +439,12 @@ impl<'data> cranelift_wasm::ModuleEnvironment<'data> for ModuleEnvironment<'data
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn reserve_function_bodies(&mut self, _count: u32, offset: u64) {
|
||||||
|
if let Some(info) = &mut self.result.debuginfo {
|
||||||
|
info.wasm_file.code_section_offset = offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn define_function_body(
|
fn define_function_body(
|
||||||
&mut self,
|
&mut self,
|
||||||
_module_translation: &ModuleTranslationState,
|
_module_translation: &ModuleTranslationState,
|
||||||
@@ -356,6 +455,22 @@ impl<'data> cranelift_wasm::ModuleEnvironment<'data> for ModuleEnvironment<'data
|
|||||||
data: body_bytes,
|
data: body_bytes,
|
||||||
module_offset: body_offset,
|
module_offset: body_offset,
|
||||||
});
|
});
|
||||||
|
if let Some(info) = &mut self.result.debuginfo {
|
||||||
|
let func_index = self.code_index + self.result.module.local.num_imported_funcs as u32;
|
||||||
|
let func_index = FuncIndex::from_u32(func_index);
|
||||||
|
let sig_index = self.result.module.local.functions[func_index];
|
||||||
|
let sig = &self.result.module.local.signatures[sig_index];
|
||||||
|
let mut locals = Vec::new();
|
||||||
|
let body = wasmparser::FunctionBody::new(body_offset, body_bytes);
|
||||||
|
for pair in body.get_locals_reader()? {
|
||||||
|
locals.push(pair?);
|
||||||
|
}
|
||||||
|
info.wasm_file.funcs.push(FunctionMetadata {
|
||||||
|
locals: locals.into_boxed_slice(),
|
||||||
|
params: sig.0.params.iter().cloned().map(|i| i.into()).collect(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
self.code_index += 1;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -402,20 +517,38 @@ impl<'data> cranelift_wasm::ModuleEnvironment<'data> for ModuleEnvironment<'data
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn declare_module_name(&mut self, name: &'data str) -> WasmResult<()> {
|
fn declare_module_name(&mut self, name: &'data str) {
|
||||||
self.result.module.name = Some(name.to_string());
|
self.result.module.name = Some(name.to_string());
|
||||||
Ok(())
|
if let Some(info) = &mut self.result.debuginfo {
|
||||||
|
info.name_section.module_name = Some(name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn declare_func_name(&mut self, func_index: FuncIndex, name: &'data str) -> WasmResult<()> {
|
fn declare_func_name(&mut self, func_index: FuncIndex, name: &'data str) {
|
||||||
self.result
|
self.result
|
||||||
.module
|
.module
|
||||||
.func_names
|
.func_names
|
||||||
.insert(func_index, name.to_string());
|
.insert(func_index, name.to_string());
|
||||||
Ok(())
|
if let Some(info) = &mut self.result.debuginfo {
|
||||||
|
info.name_section
|
||||||
|
.func_names
|
||||||
|
.insert(func_index.as_u32(), name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn custom_section(&mut self, name: &'data str, _data: &'data [u8]) -> WasmResult<()> {
|
fn declare_local_name(&mut self, func_index: FuncIndex, local: u32, name: &'data str) {
|
||||||
|
if let Some(info) = &mut self.result.debuginfo {
|
||||||
|
info.name_section
|
||||||
|
.locals_names
|
||||||
|
.entry(func_index.as_u32())
|
||||||
|
.or_insert(HashMap::new())
|
||||||
|
.insert(local, name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn custom_section(&mut self, name: &'data str, data: &'data [u8]) -> WasmResult<()> {
|
||||||
|
self.register_dwarf_section(name, data);
|
||||||
|
|
||||||
match name {
|
match name {
|
||||||
"webidl-bindings" | "wasm-interface-types" => Err(WasmError::Unsupported(
|
"webidl-bindings" | "wasm-interface-types" => Err(WasmError::Unsupported(
|
||||||
"\
|
"\
|
||||||
@@ -431,6 +564,7 @@ and for re-adding support for interface types you can see this issue:
|
|||||||
"
|
"
|
||||||
.to_owned(),
|
.to_owned(),
|
||||||
)),
|
)),
|
||||||
|
|
||||||
// skip other sections
|
// skip other sections
|
||||||
_ => Ok(()),
|
_ => Ok(()),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,12 +5,12 @@ use crate::object::{build_object, ObjectUnwindInfo};
|
|||||||
use cranelift_codegen::ir;
|
use cranelift_codegen::ir;
|
||||||
use object::write::Object;
|
use object::write::Object;
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
use wasmtime_debug::{emit_dwarf, DebugInfoData, DwarfSection};
|
use wasmtime_debug::{emit_dwarf, DwarfSection};
|
||||||
use wasmtime_environ::entity::{EntityRef, PrimaryMap};
|
use wasmtime_environ::entity::{EntityRef, PrimaryMap};
|
||||||
use wasmtime_environ::isa::{unwind::UnwindInfo, TargetFrontendConfig, TargetIsa};
|
use wasmtime_environ::isa::{unwind::UnwindInfo, TargetFrontendConfig, TargetIsa};
|
||||||
use wasmtime_environ::wasm::{DefinedFuncIndex, DefinedMemoryIndex, MemoryIndex};
|
use wasmtime_environ::wasm::{DefinedFuncIndex, DefinedMemoryIndex, MemoryIndex};
|
||||||
use wasmtime_environ::{
|
use wasmtime_environ::{
|
||||||
Compiler as _C, Module, ModuleAddressMap, ModuleMemoryOffset, ModuleTranslation,
|
Compiler as _C, DebugInfoData, Module, ModuleAddressMap, ModuleMemoryOffset, ModuleTranslation,
|
||||||
ModuleVmctxInfo, StackMaps, Traps, Tunables, VMOffsets, ValueLabelsRanges,
|
ModuleVmctxInfo, StackMaps, Traps, Tunables, VMOffsets, ValueLabelsRanges,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -61,7 +61,7 @@ fn _assert_compiler_send_sync() {
|
|||||||
fn transform_dwarf_data(
|
fn transform_dwarf_data(
|
||||||
isa: &dyn TargetIsa,
|
isa: &dyn TargetIsa,
|
||||||
module: &Module,
|
module: &Module,
|
||||||
debug_data: DebugInfoData,
|
debug_data: &DebugInfoData,
|
||||||
address_transform: &ModuleAddressMap,
|
address_transform: &ModuleAddressMap,
|
||||||
value_ranges: &ValueLabelsRanges,
|
value_ranges: &ValueLabelsRanges,
|
||||||
stack_slots: PrimaryMap<DefinedFuncIndex, ir::StackSlots>,
|
stack_slots: PrimaryMap<DefinedFuncIndex, ir::StackSlots>,
|
||||||
@@ -86,7 +86,7 @@ fn transform_dwarf_data(
|
|||||||
};
|
};
|
||||||
emit_dwarf(
|
emit_dwarf(
|
||||||
isa,
|
isa,
|
||||||
&debug_data,
|
debug_data,
|
||||||
&address_transform,
|
&address_transform,
|
||||||
&module_vmctx_info,
|
&module_vmctx_info,
|
||||||
&value_ranges,
|
&value_ranges,
|
||||||
@@ -129,7 +129,6 @@ impl Compiler {
|
|||||||
pub(crate) fn compile<'data>(
|
pub(crate) fn compile<'data>(
|
||||||
&self,
|
&self,
|
||||||
translation: &ModuleTranslation,
|
translation: &ModuleTranslation,
|
||||||
debug_data: Option<DebugInfoData>,
|
|
||||||
) -> Result<Compilation, SetupError> {
|
) -> Result<Compilation, SetupError> {
|
||||||
let (
|
let (
|
||||||
compilation,
|
compilation,
|
||||||
@@ -152,12 +151,12 @@ impl Compiler {
|
|||||||
}
|
}
|
||||||
.map_err(SetupError::Compile)?;
|
.map_err(SetupError::Compile)?;
|
||||||
|
|
||||||
let dwarf_sections = if debug_data.is_some() && !compilation.is_empty() {
|
let dwarf_sections = if translation.debuginfo.is_some() && !compilation.is_empty() {
|
||||||
let unwind_info = compilation.unwind_info();
|
let unwind_info = compilation.unwind_info();
|
||||||
transform_dwarf_data(
|
transform_dwarf_data(
|
||||||
&*self.isa,
|
&*self.isa,
|
||||||
&translation.module,
|
&translation.module,
|
||||||
debug_data.unwrap(),
|
translation.debuginfo.as_ref().unwrap(),
|
||||||
&address_transform,
|
&address_transform,
|
||||||
&value_ranges,
|
&value_ranges,
|
||||||
stack_slots,
|
stack_slots,
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ use std::any::Any;
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use wasmtime_debug::{create_gdbjit_image, read_debuginfo};
|
use wasmtime_debug::create_gdbjit_image;
|
||||||
use wasmtime_environ::entity::{BoxedSlice, PrimaryMap};
|
use wasmtime_environ::entity::{BoxedSlice, PrimaryMap};
|
||||||
use wasmtime_environ::isa::TargetIsa;
|
use wasmtime_environ::isa::TargetIsa;
|
||||||
use wasmtime_environ::wasm::{DefinedFuncIndex, SignatureIndex};
|
use wasmtime_environ::wasm::{DefinedFuncIndex, SignatureIndex};
|
||||||
@@ -89,21 +89,13 @@ impl CompilationArtifacts {
|
|||||||
.translate(data)
|
.translate(data)
|
||||||
.map_err(|error| SetupError::Compile(CompileError::Wasm(error)))?;
|
.map_err(|error| SetupError::Compile(CompileError::Wasm(error)))?;
|
||||||
|
|
||||||
let debug_info = compiler.tunables().debug_info;
|
|
||||||
|
|
||||||
let mut debug_data = None;
|
|
||||||
if debug_info {
|
|
||||||
// TODO Do we want to ignore invalid DWARF data?
|
|
||||||
debug_data = Some(read_debuginfo(&data)?);
|
|
||||||
}
|
|
||||||
|
|
||||||
let Compilation {
|
let Compilation {
|
||||||
obj,
|
obj,
|
||||||
unwind_info,
|
unwind_info,
|
||||||
traps,
|
traps,
|
||||||
stack_maps,
|
stack_maps,
|
||||||
address_transform,
|
address_transform,
|
||||||
} = compiler.compile(&translation, debug_data)?;
|
} = compiler.compile(&translation)?;
|
||||||
|
|
||||||
let ModuleTranslation {
|
let ModuleTranslation {
|
||||||
module,
|
module,
|
||||||
@@ -131,7 +123,7 @@ impl CompilationArtifacts {
|
|||||||
traps,
|
traps,
|
||||||
stack_maps,
|
stack_maps,
|
||||||
address_transform,
|
address_transform,
|
||||||
debug_info,
|
debug_info: compiler.tunables().debug_info,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use anyhow::{anyhow, bail, Context as _, Result};
|
|||||||
use object::write::Object;
|
use object::write::Object;
|
||||||
use target_lexicon::Triple;
|
use target_lexicon::Triple;
|
||||||
use wasmtime::Strategy;
|
use wasmtime::Strategy;
|
||||||
use wasmtime_debug::{emit_dwarf, read_debuginfo};
|
use wasmtime_debug::emit_dwarf;
|
||||||
#[cfg(feature = "lightbeam")]
|
#[cfg(feature = "lightbeam")]
|
||||||
use wasmtime_environ::Lightbeam;
|
use wasmtime_environ::Lightbeam;
|
||||||
use wasmtime_environ::{
|
use wasmtime_environ::{
|
||||||
@@ -103,8 +103,7 @@ pub fn compile_to_obj(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let dwarf_sections = if debug_info {
|
let dwarf_sections = if let Some(debug_data) = &translation.debuginfo {
|
||||||
let debug_data = read_debuginfo(wasm).context("failed to emit DWARF")?;
|
|
||||||
emit_dwarf(
|
emit_dwarf(
|
||||||
&*isa,
|
&*isa,
|
||||||
&debug_data,
|
&debug_data,
|
||||||
|
|||||||
Reference in New Issue
Block a user