diff --git a/crates/cranelift/src/compiler.rs b/crates/cranelift/src/compiler.rs index 199c08f525..61c268ee6e 100644 --- a/crates/cranelift/src/compiler.rs +++ b/crates/cranelift/src/compiler.rs @@ -3,7 +3,7 @@ use crate::func_environ::{get_func_name, FuncEnvironment}; use crate::obj::ObjectBuilder; use crate::{ blank_sig, func_signature, indirect_signature, value_type, wasmtime_call_conv, - CompiledFunction, Relocation, RelocationTarget, + CompiledFunction, FunctionAddressMap, Relocation, RelocationTarget, }; use anyhow::{Context as _, Result}; use cranelift_codegen::ir::{self, ExternalName, InstBuilder, MemFlags}; @@ -26,7 +26,7 @@ use std::convert::TryFrom; use std::mem; use std::sync::Mutex; use wasmtime_environ::{ - CompileError, FilePos, FlagValue, FunctionAddressMap, FunctionBodyData, FunctionInfo, + AddressMapSection, CompileError, FilePos, FlagValue, FunctionBodyData, FunctionInfo, InstructionAddressMap, Module, ModuleTranslation, StackMapInformation, TrapCode, TrapInformation, Tunables, TypeTables, VMOffsets, }; @@ -209,10 +209,11 @@ impl wasmtime_environ::Compiler for Compiler { stack_slots: context.func.stack_slots, unwind_info, info: FunctionInfo { - address_map: address_transform, traps: trap_sink.traps, + start_srcloc: address_transform.start_srcloc, stack_maps: stack_map_sink.finish(), }, + address_map: address_transform, })) } @@ -247,9 +248,11 @@ impl wasmtime_environ::Compiler for Compiler { } let mut builder = ObjectBuilder::new(obj, &translation.module); + let mut addrs = AddressMapSection::default(); for (i, func) in funcs.iter() { - builder.func(i, func); + let range = builder.func(i, func); + addrs.push(range, &func.address_map.instructions); } for (i, func) in trampolines.iter() { builder.trampoline(*i, func); @@ -287,6 +290,8 @@ impl wasmtime_environ::Compiler for Compiler { } builder.finish(&*self.isa)?; + addrs.append_to(obj); + Ok(funcs.into_iter().map(|(_, f)| f.info).collect()) } @@ -527,6 +532,7 @@ impl Compiler { stack_slots: Default::default(), value_labels_ranges: Default::default(), info: Default::default(), + address_map: Default::default(), }) } } diff --git a/crates/cranelift/src/debug/transform/address_transform.rs b/crates/cranelift/src/debug/transform/address_transform.rs index aac8b103b3..45097586dd 100644 --- a/crates/cranelift/src/debug/transform/address_transform.rs +++ b/crates/cranelift/src/debug/transform/address_transform.rs @@ -1,11 +1,9 @@ -use crate::CompiledFunctions; +use crate::{CompiledFunctions, FunctionAddressMap}; use gimli::write; use more_asserts::assert_le; use std::collections::BTreeMap; use std::iter::FromIterator; -use wasmtime_environ::{ - DefinedFuncIndex, EntityRef, FilePos, FunctionAddressMap, PrimaryMap, WasmFileInfo, -}; +use wasmtime_environ::{DefinedFuncIndex, EntityRef, FilePos, PrimaryMap, WasmFileInfo}; pub type GeneratedAddress = usize; pub type WasmAddress = u64; @@ -199,7 +197,7 @@ fn build_function_addr_map( ) -> PrimaryMap { let mut map = PrimaryMap::new(); for (_, f) in funcs { - let ft = &f.info.address_map; + let ft = &f.address_map; let mut fn_map = Vec::new(); for t in ft.instructions.iter() { if t.srcloc.file_offset().is_none() { @@ -460,7 +458,7 @@ impl AddressTransform { let mut func = BTreeMap::new(); for (i, f) in funcs { - let ft = &f.info.address_map; + let ft = &f.address_map; let (fn_start, fn_end, lookup) = build_function_lookup(ft, code_section_offset); func.insert( @@ -611,14 +609,12 @@ impl AddressTransform { #[cfg(test)] mod tests { use super::{build_function_lookup, get_wasm_code_offset, AddressTransform}; - use crate::{CompiledFunction, CompiledFunctions}; + use crate::{CompiledFunction, CompiledFunctions, FunctionAddressMap}; use cranelift_entity::PrimaryMap; use gimli::write::Address; use std::iter::FromIterator; use std::mem; - use wasmtime_environ::{ - FilePos, FunctionAddressMap, FunctionInfo, InstructionAddressMap, WasmFileInfo, - }; + use wasmtime_environ::{FilePos, InstructionAddressMap, WasmFileInfo}; #[test] fn test_get_wasm_code_offset() { @@ -660,10 +656,7 @@ mod tests { fn create_simple_module(address_map: FunctionAddressMap) -> CompiledFunctions { PrimaryMap::from_iter(vec![CompiledFunction { - info: FunctionInfo { - address_map, - ..Default::default() - }, + address_map, ..Default::default() }]) } diff --git a/crates/cranelift/src/debug/transform/expression.rs b/crates/cranelift/src/debug/transform/expression.rs index 74965311f1..0ccfde8b03 100644 --- a/crates/cranelift/src/debug/transform/expression.rs +++ b/crates/cranelift/src/debug/transform/expression.rs @@ -849,7 +849,7 @@ mod tests { }; use crate::CompiledFunction; use gimli::{self, constants, Encoding, EndianSlice, Expression, RunTimeEndian}; - use wasmtime_environ::{FilePos, FunctionInfo}; + use wasmtime_environ::FilePos; macro_rules! dw_op { (DW_OP_WASM_location) => { @@ -1177,39 +1177,37 @@ mod tests { } fn create_mock_address_transform() -> AddressTransform { + use crate::FunctionAddressMap; use cranelift_entity::PrimaryMap; + use wasmtime_environ::InstructionAddressMap; use wasmtime_environ::WasmFileInfo; - use wasmtime_environ::{FunctionAddressMap, InstructionAddressMap}; let mut module_map = PrimaryMap::new(); let code_section_offset: u32 = 100; module_map.push(CompiledFunction { - info: FunctionInfo { - address_map: FunctionAddressMap { - instructions: vec![ - InstructionAddressMap { - srcloc: FilePos::new(code_section_offset + 12), - code_offset: 5, - }, - InstructionAddressMap { - srcloc: FilePos::default(), - code_offset: 8, - }, - InstructionAddressMap { - srcloc: FilePos::new(code_section_offset + 17), - code_offset: 15, - }, - InstructionAddressMap { - srcloc: FilePos::default(), - code_offset: 23, - }, - ] - .into(), - start_srcloc: FilePos::new(code_section_offset + 10), - end_srcloc: FilePos::new(code_section_offset + 20), - body_offset: 0, - body_len: 30, - }, - ..Default::default() + address_map: FunctionAddressMap { + instructions: vec![ + InstructionAddressMap { + srcloc: FilePos::new(code_section_offset + 12), + code_offset: 5, + }, + InstructionAddressMap { + srcloc: FilePos::default(), + code_offset: 8, + }, + InstructionAddressMap { + srcloc: FilePos::new(code_section_offset + 17), + code_offset: 15, + }, + InstructionAddressMap { + srcloc: FilePos::default(), + code_offset: 23, + }, + ] + .into(), + start_srcloc: FilePos::new(code_section_offset + 10), + end_srcloc: FilePos::new(code_section_offset + 20), + body_offset: 0, + body_len: 30, }, ..Default::default() }); diff --git a/crates/cranelift/src/lib.rs b/crates/cranelift/src/lib.rs index 8792268f67..f7bffe58d4 100644 --- a/crates/cranelift/src/lib.rs +++ b/crates/cranelift/src/lib.rs @@ -94,7 +94,7 @@ use cranelift_codegen::isa::{unwind::UnwindInfo, CallConv, TargetIsa}; use cranelift_entity::PrimaryMap; use cranelift_wasm::{DefinedFuncIndex, FuncIndex, WasmFuncType, WasmType}; use target_lexicon::CallingConvention; -use wasmtime_environ::{FunctionInfo, Module, TypeTables}; +use wasmtime_environ::{FilePos, FunctionInfo, InstructionAddressMap, Module, TypeTables}; pub use builder::builder; @@ -118,6 +118,10 @@ pub struct CompiledFunction { /// The unwind information. unwind_info: Option, + /// Information used to translate from binary offsets back to the original + /// location found in the wasm input. + address_map: FunctionAddressMap, + relocations: Vec, value_labels_ranges: cranelift_codegen::ValueLabelsRanges, stack_slots: ir::StackSlots, @@ -125,6 +129,32 @@ pub struct CompiledFunction { info: FunctionInfo, } +/// Function and its instructions addresses mappings. +#[derive(Debug, Clone, PartialEq, Eq, Default)] +struct FunctionAddressMap { + /// An array of data for the instructions in this function, indicating where + /// each instruction maps back to in the original function. + /// + /// This array is sorted least-to-greatest by the `code_offset` field. + /// Additionally the span of each `InstructionAddressMap` is implicitly the + /// gap between it and the next item in the array. + instructions: Box<[InstructionAddressMap]>, + + /// Function's initial offset in the source file, specified in bytes from + /// the front of the file. + start_srcloc: FilePos, + + /// Function's end offset in the source file, specified in bytes from + /// the front of the file. + end_srcloc: FilePos, + + /// Generated function body offset if applicable, otherwise 0. + body_offset: usize, + + /// Generated function body length. + body_len: u32, +} + /// A record of a relocation to perform. #[derive(Debug, Clone, PartialEq, Eq)] struct Relocation { diff --git a/crates/cranelift/src/obj.rs b/crates/cranelift/src/obj.rs index 4bd545aa8b..ac6d81dcfa 100644 --- a/crates/cranelift/src/obj.rs +++ b/crates/cranelift/src/obj.rs @@ -34,6 +34,7 @@ use object::{ }; use std::collections::HashMap; use std::convert::TryFrom; +use std::ops::Range; use wasmtime_environ::obj; use wasmtime_environ::{ DefinedFuncIndex, EntityRef, FuncIndex, Module, PrimaryMap, SignatureIndex, @@ -157,7 +158,11 @@ impl<'a> ObjectBuilder<'a> { } } - fn append_func(&mut self, name: Vec, func: &'a CompiledFunction) -> SymbolId { + /// Appends the `func` specified named `name` to this object. + /// + /// Returns the symbol associated with the function as well as the range + /// that the function resides within the text section. + fn append_func(&mut self, name: Vec, func: &'a CompiledFunction) -> (SymbolId, Range) { let off = self .obj .append_section_data(self.text_section, &func.body, 1); @@ -207,15 +212,22 @@ impl<'a> ObjectBuilder<'a> { if !func.relocations.is_empty() { self.pending_relocations.push((off, &func.relocations)); } - symbol_id + (symbol_id, off..off + func.body.len() as u64) } - pub fn func(&mut self, index: DefinedFuncIndex, func: &'a CompiledFunction) { + /// Pushes a new defined function from the a wasm module into this object, + /// returning the range that the compiled code will live at relative in the + /// text section of the final executable. + /// + /// Note that functions must be pushed in the order of their + /// `DefinedFuncIndex`. + pub fn func(&mut self, index: DefinedFuncIndex, func: &'a CompiledFunction) -> Range { assert_eq!(self.jump_tables.push(&func.jt_offsets), index); let index = self.module.func_index(index); let name = obj::func_symbol_name(index); - let symbol_id = self.append_func(name.into_bytes(), func); + let (symbol_id, range) = self.append_func(name.into_bytes(), func); assert_eq!(self.func_symbols.push(symbol_id), index); + range } pub fn trampoline(&mut self, sig: SignatureIndex, func: &'a CompiledFunction) { diff --git a/crates/environ/Cargo.toml b/crates/environ/Cargo.toml index 6e3075c4bc..7de3f63f16 100644 --- a/crates/environ/Cargo.toml +++ b/crates/environ/Cargo.toml @@ -22,7 +22,7 @@ log = { version = "0.4.8", default-features = false } more-asserts = "0.2.1" cfg-if = "1.0" gimli = { version = "0.25.0", default-features = false, features = ['read'] } -object = { version = "0.26.0", default-features = false, features = ['write_core', 'elf'] } +object = { version = "0.26.0", default-features = false, features = ['read_core', 'write_core', 'elf'] } target-lexicon = "0.12" [badges] diff --git a/crates/environ/src/address_map.rs b/crates/environ/src/address_map.rs index f169b132d8..af7b79278b 100644 --- a/crates/environ/src/address_map.rs +++ b/crates/environ/src/address_map.rs @@ -1,7 +1,11 @@ //! Data structures to provide transformation of the source // addresses of a WebAssembly module into the native code. +use object::write::{Object, StandardSegment}; +use object::{Bytes, LittleEndian, SectionKind, U32Bytes}; use serde::{Deserialize, Serialize}; +use std::convert::TryFrom; +use std::ops::Range; /// Single source location to generated address mapping. #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] @@ -15,32 +19,6 @@ pub struct InstructionAddressMap { pub code_offset: u32, } -/// Function and its instructions addresses mappings. -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default)] -pub struct FunctionAddressMap { - /// An array of data for the instructions in this function, indicating where - /// each instruction maps back to in the original function. - /// - /// This array is sorted least-to-greatest by the `code_offset` field. - /// Additionally the span of each `InstructionAddressMap` is implicitly the - /// gap between it and the next item in the array. - pub instructions: Box<[InstructionAddressMap]>, - - /// Function's initial offset in the source file, specified in bytes from - /// the front of the file. - pub start_srcloc: FilePos, - - /// Function's end offset in the source file, specified in bytes from - /// the front of the file. - pub end_srcloc: FilePos, - - /// Generated function body offset if applicable, otherwise 0. - pub body_offset: usize, - - /// Generated function body length. - pub body_len: u32, -} - /// A position within an original source file, /// /// This structure is used as a newtype wrapper around a 32-bit integer which @@ -74,3 +52,140 @@ impl Default for FilePos { FilePos(u32::MAX) } } + +/// Builder for the address map section of a wasmtime compilation image. +/// +/// This builder is used to conveniently built the `ELF_WASMTIME_ADDRMAP` +/// section by compilers, and provides utilities to directly insert the results +/// into an `Object`. +#[derive(Default)] +pub struct AddressMapSection { + offsets: Vec>, + positions: Vec>, + last_offset: u32, +} + +/// A custom Wasmtime-specific section of our compilation image which stores +/// mapping data from offsets in the image to offset in the original wasm +/// binary. +/// +/// This section has a custom binary encoding. Currently its encoding is: +/// +/// * The section starts with a 32-bit little-endian integer. This integer is +/// how many entries are in the following two arrays. +/// * Next is an array with the previous count number of 32-bit little-endian +/// integers. This array is a sorted list of relative offsets within the text +/// section. This is intended to be a lookup array to perform a binary search +/// on an offset within the text section on this array. +/// * Finally there is another array, with the same count as before, also of +/// 32-bit little-endian integers. These integers map 1:1 with the previous +/// array of offsets, and correspond to what the original offset was in the +/// wasm file. +/// +/// Decoding this section is intentionally simple, it only requires loading a +/// 32-bit little-endian integer plus some bounds checks. Reading this section +/// is done with the `lookup_file_pos` function below. Reading involves +/// performing a binary search on the first array using the index found for the +/// native code offset to index into the second array and find the wasm code +/// offset. +/// +/// At this time this section has an alignment of 1, which means all reads of it +/// are unaligned. Additionally at this time the 32-bit encodings chosen here +/// mean that >=4gb text sections are not supported. +pub const ELF_WASMTIME_ADDRMAP: &str = ".wasmtime.addrmap"; + +impl AddressMapSection { + /// Pushes a new set of instruction mapping information for a function added + /// in the exectuable. + /// + /// The `func` argument here is the range of the function, relative to the + /// start of the text section in the executable. The `instrs` provided are + /// the descriptors for instructions in the function and their various + /// mappings back to original source positions. + /// + /// This is required to be called for `func` values that are strictly + /// increasing in addresses (e.g. as the object is built). Additionally the + /// `instrs` map must be sorted based on code offset in the native text + /// section. + pub fn push(&mut self, func: Range, instrs: &[InstructionAddressMap]) { + // NB: for now this only supports <=4GB text sections in object files. + // Alternative schemes will need to be created for >32-bit offsets to + // avoid making this section overly large. + let func_start = u32::try_from(func.start).unwrap(); + let func_end = u32::try_from(func.end).unwrap(); + + self.offsets.reserve(instrs.len()); + self.positions.reserve(instrs.len()); + for map in instrs { + // Sanity-check to ensure that functions are pushed in-order, otherwise + // the `offsets` array won't be sorted which is our goal. + let pos = func_start + map.code_offset; + assert!(pos >= self.last_offset); + self.offsets.push(U32Bytes::new(LittleEndian, pos)); + self.positions + .push(U32Bytes::new(LittleEndian, map.srcloc.0)); + self.last_offset = pos; + } + self.last_offset = func_end; + } + + /// Finishes encoding this section into the `Object` provided. + pub fn append_to(self, obj: &mut Object) { + let section = obj.add_section( + obj.segment_name(StandardSegment::Data).to_vec(), + ELF_WASMTIME_ADDRMAP.as_bytes().to_vec(), + SectionKind::ReadOnlyData, + ); + + // NB: this matches the encoding expected by `lookup` below. + let amt = u32::try_from(self.offsets.len()).unwrap(); + obj.append_section_data(section, &amt.to_le_bytes(), 1); + obj.append_section_data(section, object::bytes_of_slice(&self.offsets), 1); + obj.append_section_data(section, object::bytes_of_slice(&self.positions), 1); + } +} + +/// Lookup an `offset` within an encoded address map section, returning the +/// original `FilePos` that corresponds to the offset, if found. +/// +/// This function takes a `section` as its first argument which must have been +/// created with `AddressMapSection` above. This is intended to be the raw +/// `ELF_WASMTIME_ADDRMAP` section from the compilation artifact. +/// +/// The `offset` provided is a relative offset from the start of the text +/// section of the pc that is being looked up. If `offset` is out of range or +/// doesn't correspond to anything in this file then `None` is returned. +pub fn lookup_file_pos(section: &[u8], offset: usize) -> Option { + let mut section = Bytes(section); + // NB: this matches the encoding written by `append_to` above. + let count = section.read::>().ok()?; + let count = usize::try_from(count.get(LittleEndian)).ok()?; + let (offsets, section) = + object::slice_from_bytes::>(section.0, count).ok()?; + let (positions, section) = + object::slice_from_bytes::>(section, count).ok()?; + debug_assert!(section.is_empty()); + + // First perform a binary search on the `offsets` array. This is a sorted + // array of offsets within the text section, which is conveniently what our + // `offset` also is. Note that we are somewhat unlikely to find a precise + // match on the element in the array, so we're largely interested in which + // "bucket" the `offset` falls into. + let offset = u32::try_from(offset).ok()?; + let index = match offsets.binary_search_by_key(&offset, |v| v.get(LittleEndian)) { + // Exact hit! + Ok(i) => i, + + // This *would* be at the first slot in the array, so no + // instructions cover `pc`. + Err(0) => return None, + + // This would be at the `nth` slot, so we're at the `n-1`th slot. + Err(n) => n - 1, + }; + + // Using the `index` we found of which bucket `offset` corresponds to we can + // lookup the actual `FilePos` value in the `positions` array. + let pos = positions.get(index)?; + Some(FilePos(pos.get(LittleEndian))) +} diff --git a/crates/environ/src/compilation.rs b/crates/environ/src/compilation.rs index beca93a448..4d4bae1c78 100644 --- a/crates/environ/src/compilation.rs +++ b/crates/environ/src/compilation.rs @@ -2,8 +2,8 @@ //! module. use crate::{ - DefinedFuncIndex, FunctionAddressMap, FunctionBodyData, ModuleTranslation, PrimaryMap, - StackMap, Tunables, TypeTables, WasmError, WasmFuncType, + DefinedFuncIndex, FilePos, FunctionBodyData, ModuleTranslation, PrimaryMap, StackMap, Tunables, + TypeTables, WasmError, WasmFuncType, }; use anyhow::Result; use object::write::Object; @@ -21,7 +21,7 @@ use thiserror::Error; #[allow(missing_docs)] pub struct FunctionInfo { pub traps: Vec, - pub address_map: FunctionAddressMap, + pub start_srcloc: FilePos, pub stack_maps: Vec, } diff --git a/crates/jit/src/instantiate.rs b/crates/jit/src/instantiate.rs index f7691f6ea6..b2dc4e3de1 100644 --- a/crates/jit/src/instantiate.rs +++ b/crates/jit/src/instantiate.rs @@ -18,7 +18,7 @@ use thiserror::Error; use wasmtime_environ::{ CompileError, DefinedFuncIndex, FunctionInfo, InstanceSignature, InstanceTypeIndex, Module, ModuleSignature, ModuleTranslation, ModuleTypeIndex, PrimaryMap, SignatureIndex, - StackMapInformation, Tunables, WasmFuncType, + StackMapInformation, Tunables, WasmFuncType, ELF_WASMTIME_ADDRMAP, }; use wasmtime_runtime::{GdbJitImageRegistration, InstantiationError, VMFunctionBody, VMTrampoline}; @@ -268,6 +268,7 @@ impl ModuleCode { /// A compiled wasm module, ready to be instantiated. pub struct CompiledModule { wasm_data: Range, + address_map_data: Range, artifacts: CompilationArtifacts, module: Arc, funcs: PrimaryMap, @@ -313,6 +314,7 @@ impl CompiledModule { let module = Arc::new(info.module); let funcs = info.funcs; let wasm_data = subslice_range(section(ELF_WASM_DATA)?, &artifacts.obj); + let address_map_data = subslice_range(section(ELF_WASMTIME_ADDRMAP)?, &artifacts.obj); // Allocate all of the compiled functions into executable memory, // copying over their contents. @@ -334,6 +336,7 @@ impl CompiledModule { module, artifacts, wasm_data, + address_map_data, code: Arc::new(ModuleCode { range: (start, end), code_memory, @@ -384,6 +387,12 @@ impl CompiledModule { &self.artifacts.obj[self.wasm_data.clone()] } + /// Returns the encoded address map section used to pass to + /// `wasmtime_environ::lookup_file_pos`. + pub fn address_map_data(&self) -> &[u8] { + &self.artifacts.obj[self.address_map_data.clone()] + } + /// Return a reference-counting pointer to a module. pub fn module(&self) -> &Arc { &self.module @@ -421,10 +430,13 @@ impl CompiledModule { /// Lookups a defined function by a program counter value. /// - /// Returns the defined function index, the start address, and the end address (exclusive). - pub fn func_by_pc(&self, pc: usize) -> Option<(DefinedFuncIndex, usize, usize)> { + /// Returns the defined function index and the relative address of + /// `text_offfset` within the function itself. + pub fn func_by_text_offset(&self, text_offset: usize) -> Option<(DefinedFuncIndex, u32)> { let functions = self.finished_functions(); + let text_section = self.code().range().0; + let pc = text_section + text_offset; let index = match functions.binary_search_values_by_key(&pc, |body| unsafe { debug_assert!(!(**body).is_empty()); // Return the inclusive "end" of the function @@ -453,7 +465,7 @@ impl CompiledModule { return None; } - Some((index, start, end)) + Some((index, (text_offset - (start - text_section)) as u32)) } /// Gets the function information for a given function index. diff --git a/crates/wasmtime/src/module/registry.rs b/crates/wasmtime/src/module/registry.rs index 6f9faa81bc..530baf1f88 100644 --- a/crates/wasmtime/src/module/registry.rs +++ b/crates/wasmtime/src/module/registry.rs @@ -5,9 +5,7 @@ use std::{ collections::BTreeMap, sync::{Arc, RwLock}, }; -use wasmtime_environ::{ - DefinedFuncIndex, EntityRef, FilePos, FunctionAddressMap, StackMap, TrapInformation, -}; +use wasmtime_environ::{EntityRef, FilePos, StackMap, TrapInformation}; use wasmtime_jit::CompiledModule; use wasmtime_runtime::{ModuleInfo, VMCallerCheckedAnyfunc, VMTrampoline}; @@ -15,11 +13,6 @@ lazy_static::lazy_static! { static ref GLOBAL_MODULES: RwLock = Default::default(); } -fn func_by_pc(module: &CompiledModule, pc: usize) -> Option<(DefinedFuncIndex, u32)> { - let (index, start, _) = module.func_by_pc(pc)?; - Some((index, (pc - start) as u32)) -} - /// Used for registering modules with a store. /// /// The map is from the ending (exclusive) address for the module code to @@ -121,31 +114,10 @@ struct RegisteredModule { signatures: Arc, } -impl RegisteredModule { - fn instr_pos(offset: u32, addr_map: &FunctionAddressMap) -> Option { - // Use our relative position from the start of the function to find the - // machine instruction that corresponds to `pc`, which then allows us to - // map that to a wasm original source location. - match addr_map - .instructions - .binary_search_by_key(&offset, |map| map.code_offset) - { - // Exact hit! - Ok(pos) => Some(pos), - - // This *would* be at the first slot in the array, so no - // instructions cover `pc`. - Err(0) => None, - - // This would be at the `nth` slot, so we're at the `n-1`th slot. - Err(n) => Some(n - 1), - } - } -} - impl ModuleInfo for RegisteredModule { fn lookup_stack_map(&self, pc: usize) -> Option<&StackMap> { - let (index, offset) = func_by_pc(&self.module, pc)?; + let text_offset = pc - self.start; + let (index, func_offset) = self.module.func_by_text_offset(text_offset)?; let info = self.module.func_info(index); // Do a binary search to find the stack map for the given offset. @@ -192,7 +164,7 @@ impl ModuleInfo for RegisteredModule { let index = match info .stack_maps - .binary_search_by_key(&offset, |i| i.code_offset) + .binary_search_by_key(&func_offset, |i| i.code_offset) { // Exact hit. Ok(i) => i, @@ -246,23 +218,22 @@ impl GlobalModuleRegistry { let modules = GLOBAL_MODULES.read().unwrap(); match modules.module(pc) { - Some(entry) => match func_by_pc(&entry.module, pc) { - Some((index, offset)) => { - let info = entry.module.func_info(index); - RegisteredModule::instr_pos(offset, &info.address_map).is_some() - } - None => false, - }, + Some((entry, text_offset)) => { + wasmtime_environ::lookup_file_pos(entry.module.address_map_data(), text_offset) + .is_some() + } None => false, } } - fn module(&self, pc: usize) -> Option<&GlobalRegisteredModule> { + /// Returns, if found, the corresponding module for the `pc` as well as the + /// pc transformed to a relative offset within the text section. + fn module(&self, pc: usize) -> Option<(&GlobalRegisteredModule, usize)> { let (end, info) = self.0.range(pc..).next()?; if pc < info.start || *end < pc { return None; } - Some(info) + Some((info, pc - info.start)) } // Work with the global instance of `GlobalModuleRegistry`. Note that only @@ -280,8 +251,8 @@ impl GlobalModuleRegistry { /// boolean indicates whether the engine used to compile this module is /// using environment variables to control debuginfo parsing. pub(crate) fn lookup_frame_info(&self, pc: usize) -> Option<(FrameInfo, bool, bool)> { - let module = self.module(pc)?; - module.lookup_frame_info(pc).map(|info| { + let (module, offset) = self.module(pc)?; + module.lookup_frame_info(offset).map(|info| { ( info, module.has_unparsed_debuginfo(), @@ -292,7 +263,8 @@ impl GlobalModuleRegistry { /// Fetches trap information about a program counter in a backtrace. pub(crate) fn lookup_trap_info(&self, pc: usize) -> Option<&TrapInformation> { - self.module(pc)?.lookup_trap_info(pc) + let (module, offset) = self.module(pc)?; + module.lookup_trap_info(offset) } /// Registers a new region of code, described by `(start, end)` and with @@ -336,20 +308,22 @@ impl GlobalRegisteredModule { /// /// Returns an object if this `pc` is known to this module, or returns `None` /// if no information can be found. - pub fn lookup_frame_info(&self, pc: usize) -> Option { - let (index, offset) = func_by_pc(&self.module, pc)?; + pub fn lookup_frame_info(&self, text_offset: usize) -> Option { + let (index, _func_offset) = self.module.func_by_text_offset(text_offset)?; let info = self.module.func_info(index); - let pos = RegisteredModule::instr_pos(offset, &info.address_map); + let instr = wasmtime_environ::lookup_file_pos(self.module.address_map_data(), text_offset); // In debug mode for now assert that we found a mapping for `pc` within // the function, because otherwise something is buggy along the way and // not accounting for all the instructions. This isn't super critical // though so we can omit this check in release mode. - debug_assert!(pos.is_some(), "failed to find instruction for {:x}", pc); + debug_assert!( + instr.is_some(), + "failed to find instruction for {:#x}", + text_offset + ); - let instr = pos - .map(|i| info.address_map.instructions[i].srcloc) - .unwrap_or(info.address_map.start_srcloc); + let instr = instr.unwrap_or(info.start_srcloc); // Use our wasm-relative pc to symbolize this frame. If there's a // symbolication context (dwarf debug info) available then we can try to @@ -393,18 +367,18 @@ impl GlobalRegisteredModule { func_index: index.index() as u32, func_name: module.func_names.get(&index).cloned(), instr, - func_start: info.address_map.start_srcloc, + func_start: info.start_srcloc, symbols, }) } /// Fetches trap information about a program counter in a backtrace. - pub fn lookup_trap_info(&self, pc: usize) -> Option<&TrapInformation> { - let (index, offset) = func_by_pc(&self.module, pc)?; + pub fn lookup_trap_info(&self, text_offset: usize) -> Option<&TrapInformation> { + let (index, func_offset) = self.module.func_by_text_offset(text_offset)?; let info = self.module.func_info(index); let idx = info .traps - .binary_search_by_key(&offset, |info| info.code_offset) + .binary_search_by_key(&func_offset, |info| info.code_offset) .ok()?; Some(&info.traps[idx]) }