diff --git a/Cargo.lock b/Cargo.lock index 470071fbda..d080caa56e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3713,6 +3713,7 @@ dependencies = [ "indexmap", "log", "more-asserts", + "object", "serde", "target-lexicon", "thiserror", @@ -3798,6 +3799,7 @@ dependencies = [ "cranelift-codegen", "gimli", "lightbeam", + "object", "target-lexicon", "wasmparser", "wasmtime-environ", diff --git a/crates/cranelift/src/compiler.rs b/crates/cranelift/src/compiler.rs index c930fbb4f5..199c08f525 100644 --- a/crates/cranelift/src/compiler.rs +++ b/crates/cranelift/src/compiler.rs @@ -1,6 +1,6 @@ use crate::debug::ModuleMemoryOffset; use crate::func_environ::{get_func_name, FuncEnvironment}; -use crate::obj::{ObjectBuilder, ObjectBuilderTarget}; +use crate::obj::ObjectBuilder; use crate::{ blank_sig, func_signature, indirect_signature, value_type, wasmtime_call_conv, CompiledFunction, Relocation, RelocationTarget, @@ -18,6 +18,7 @@ use cranelift_wasm::{ DefinedFuncIndex, DefinedMemoryIndex, FuncIndex, FuncTranslator, MemoryIndex, SignatureIndex, WasmFuncType, }; +use object::write::Object; use std::any::Any; use std::cmp; use std::collections::{BTreeMap, BTreeSet}; @@ -221,7 +222,8 @@ impl wasmtime_environ::Compiler for Compiler { types: &TypeTables, funcs: PrimaryMap>, emit_dwarf: bool, - ) -> Result<(Vec, PrimaryMap)> { + obj: &mut Object, + ) -> Result> { const CODE_SECTION_ALIGNMENT: u64 = 0x1000; let funcs: crate::CompiledFunctions = funcs .into_iter() @@ -244,8 +246,7 @@ impl wasmtime_environ::Compiler for Compiler { trampolines.push((i, func)); } - let target = ObjectBuilderTarget::elf(self.isa.triple().architecture)?; - let mut builder = ObjectBuilder::new(target, &translation.module); + let mut builder = ObjectBuilder::new(obj, &translation.module); for (i, func) in funcs.iter() { builder.func(i, func); @@ -285,21 +286,24 @@ impl wasmtime_environ::Compiler for Compiler { builder.dwarf_sections(&dwarf_sections)?; } - Ok(( - builder.finish(&*self.isa)?, - funcs.into_iter().map(|(_, f)| f.info).collect(), - )) + builder.finish(&*self.isa)?; + Ok(funcs.into_iter().map(|(_, f)| f.info).collect()) } - fn emit_trampoline_obj(&self, ty: &WasmFuncType, host_fn: usize) -> Result> { + fn emit_trampoline_obj( + &self, + ty: &WasmFuncType, + host_fn: usize, + obj: &mut Object, + ) -> Result<()> { let host_to_wasm = self.host_to_wasm_trampoline(ty)?; let wasm_to_host = self.wasm_to_host_trampoline(ty, host_fn)?; - let target = ObjectBuilderTarget::elf(self.isa.triple().architecture)?; let module = Module::new(); - let mut builder = ObjectBuilder::new(target, &module); + let mut builder = ObjectBuilder::new(obj, &module); builder.trampoline(SignatureIndex::new(0), &host_to_wasm); builder.trampoline(SignatureIndex::new(1), &wasm_to_host); - Ok(builder.finish(&*self.isa)?) + builder.finish(&*self.isa)?; + Ok(()) } fn triple(&self) -> &target_lexicon::Triple { diff --git a/crates/cranelift/src/obj.rs b/crates/cranelift/src/obj.rs index fe39ffe4a9..4bd545aa8b 100644 --- a/crates/cranelift/src/obj.rs +++ b/crates/cranelift/src/obj.rs @@ -29,8 +29,8 @@ use object::write::{ SymbolSection, }; use object::{ - elf, Architecture, BinaryFormat, Endianness, RelocationEncoding, RelocationKind, SectionKind, - SymbolFlags, SymbolKind, SymbolScope, + elf, Architecture, RelocationEncoding, RelocationKind, SectionKind, SymbolFlags, SymbolKind, + SymbolScope, }; use std::collections::HashMap; use std::convert::TryFrom; @@ -39,22 +39,6 @@ use wasmtime_environ::{ DefinedFuncIndex, EntityRef, FuncIndex, Module, PrimaryMap, SignatureIndex, }; -fn to_object_architecture( - arch: target_lexicon::Architecture, -) -> Result { - use target_lexicon::Architecture::*; - Ok(match arch { - X86_32(_) => Architecture::I386, - X86_64 => Architecture::X86_64, - Arm(_) => Architecture::Arm, - Aarch64(_) => Architecture::Aarch64, - S390x => Architecture::S390x, - architecture => { - anyhow::bail!("target architecture {:?} is unsupported", architecture,); - } - }) -} - const TEXT_SECTION_NAME: &[u8] = b".text"; /// Iterates through all `LibCall` members and all runtime exported functions. @@ -106,27 +90,8 @@ fn write_libcall_symbols(obj: &mut Object) -> HashMap { libcalls } -pub struct ObjectBuilderTarget { - pub(crate) binary_format: BinaryFormat, - pub(crate) architecture: Architecture, - pub(crate) endianness: Endianness, -} - -impl ObjectBuilderTarget { - pub fn elf(arch: target_lexicon::Architecture) -> Result { - Ok(Self { - binary_format: BinaryFormat::Elf, - architecture: to_object_architecture(arch)?, - endianness: match arch.endianness().unwrap() { - target_lexicon::Endianness::Little => object::Endianness::Little, - target_lexicon::Endianness::Big => object::Endianness::Big, - }, - }) - } -} - pub struct ObjectBuilder<'a> { - obj: Object, + obj: &'a mut Object, module: &'a Module, text_section: SectionId, func_symbols: PrimaryMap, @@ -150,9 +115,7 @@ struct RUNTIME_FUNCTION { } impl<'a> ObjectBuilder<'a> { - pub fn new(target: ObjectBuilderTarget, module: &'a Module) -> Self { - let mut obj = Object::new(target.binary_format, target.architecture, target.endianness); - + pub fn new(obj: &'a mut Object, module: &'a Module) -> Self { // Entire code (functions and trampolines) will be placed // in the ".text" section. let text_section = obj.add_section( @@ -179,7 +142,7 @@ impl<'a> ObjectBuilder<'a> { func_symbols.push(symbol_id); } - let libcalls = write_libcall_symbols(&mut obj); + let libcalls = write_libcall_symbols(obj); Self { obj, @@ -309,7 +272,7 @@ impl<'a> ObjectBuilder<'a> { Ok(()) } - pub fn finish(&mut self, isa: &dyn TargetIsa) -> Result> { + pub fn finish(&mut self, isa: &dyn TargetIsa) -> Result<()> { self.append_relocations()?; if self.windows_unwind_info.len() > 0 { self.append_windows_unwind_info(); @@ -317,7 +280,7 @@ impl<'a> ObjectBuilder<'a> { if self.systemv_unwind_info.len() > 0 { self.append_systemv_unwind_info(isa); } - Ok(self.obj.write()?) + Ok(()) } fn append_relocations(&mut self) -> Result<()> { diff --git a/crates/environ/Cargo.toml b/crates/environ/Cargo.toml index a16f6f079b..6e3075c4bc 100644 --- a/crates/environ/Cargo.toml +++ b/crates/environ/Cargo.toml @@ -22,6 +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'] } target-lexicon = "0.12" [badges] diff --git a/crates/environ/src/compilation.rs b/crates/environ/src/compilation.rs index 607eccc2b9..beca93a448 100644 --- a/crates/environ/src/compilation.rs +++ b/crates/environ/src/compilation.rs @@ -6,6 +6,8 @@ use crate::{ StackMap, Tunables, TypeTables, WasmError, WasmFuncType, }; use anyhow::Result; +use object::write::Object; +use object::{Architecture, BinaryFormat}; use serde::{Deserialize, Serialize}; use std::any::Any; use std::borrow::Cow; @@ -184,21 +186,70 @@ pub trait Compiler: Send + Sync { types: &TypeTables, ) -> Result, CompileError>; - /// Collects the results of compilation and emits an in-memory ELF object - /// which is the serialized representation of all compiler artifacts. + /// Collects the results of compilation into an in-memory object. /// - /// Note that ELF is used regardless of the target architecture. + /// This function will receive the same `Box` produced as part of + /// `compile_function`, as well as the general compilation environment with + /// the translation/types. This method is expected to populate information + /// in the object file such as: + /// + /// * Compiled code in a `.text` section + /// * Unwind information in Wasmtime-specific sections + /// * DWARF debugging information for the host, if `emit_dwarf` is `true` + /// and the compiler supports it. + /// * Relocations, if necessary, for the text section + /// + /// The final result of compilation will contain more sections inserted by + /// the compiler-agnostic runtime. fn emit_obj( &self, module: &ModuleTranslation, types: &TypeTables, funcs: PrimaryMap>, emit_dwarf: bool, - ) -> Result<(Vec, PrimaryMap)>; + obj: &mut Object, + ) -> Result>; - /// Emits a small ELF object file in-memory which has two functions for the - /// host-to-wasm and wasm-to-host trampolines for the wasm type given. - fn emit_trampoline_obj(&self, ty: &WasmFuncType, host_fn: usize) -> Result>; + /// Inserts two functions for host-to-wasm and wasm-to-host trampolines into + /// the `obj` provided. + /// + /// This will configure the same sections as `emit_obj`, but will likely be + /// much smaller. + fn emit_trampoline_obj( + &self, + ty: &WasmFuncType, + host_fn: usize, + obj: &mut Object, + ) -> Result<()>; + + /// Creates a new `Object` file which is used to build the results of a + /// compilation into. + /// + /// The returned object file will have an appropriate + /// architecture/endianness for `self.triple()`, but at this time it is + /// always an ELF file, regardless of target platform. + fn object(&self) -> Result { + use target_lexicon::Architecture::*; + + let triple = self.triple(); + Ok(Object::new( + BinaryFormat::Elf, + match triple.architecture { + X86_32(_) => Architecture::I386, + X86_64 => Architecture::X86_64, + Arm(_) => Architecture::Arm, + Aarch64(_) => Architecture::Aarch64, + S390x => Architecture::S390x, + architecture => { + anyhow::bail!("target architecture {:?} is unsupported", architecture,); + } + }, + match triple.endianness().unwrap() { + target_lexicon::Endianness::Little => object::Endianness::Little, + target_lexicon::Endianness::Big => object::Endianness::Big, + }, + )) + } /// Returns the target triple that this compiler is compiling for. fn triple(&self) -> &target_lexicon::Triple; diff --git a/crates/jit/src/code_memory.rs b/crates/jit/src/code_memory.rs index 2788a69534..bd6594b4c0 100644 --- a/crates/jit/src/code_memory.rs +++ b/crates/jit/src/code_memory.rs @@ -38,14 +38,13 @@ impl Drop for CodeMemoryEntry { } } -pub struct CodeMemoryObjectAllocation<'a, 'b> { +pub struct CodeMemoryObjectAllocation<'a> { pub code_range: &'a mut [u8], funcs: BTreeMap, trampolines: BTreeMap, - pub obj: ObjectFile<'b>, } -impl<'a> CodeMemoryObjectAllocation<'a, '_> { +impl<'a> CodeMemoryObjectAllocation<'a> { pub fn funcs_len(&self) -> usize { self.funcs.len() } @@ -140,14 +139,22 @@ impl CodeMemory { unsafe { &mut *body_ptr } } + /// Alternative to `allocate_for_object`, but when the object file isn't + /// already parsed. + pub fn allocate_for_object_unparsed<'a>( + &'a mut self, + obj: &[u8], + ) -> Result> { + let obj = ObjectFile::parse(obj)?; + self.allocate_for_object(&obj) + } + /// Allocates and copies the ELF image code section into CodeMemory. /// Returns references to functions and trampolines defined there. - pub fn allocate_for_object<'a, 'b>( + pub fn allocate_for_object<'a>( &'a mut self, - obj: &'b [u8], - ) -> Result> { - let obj = ObjectFile::parse(obj) - .with_context(|| "failed to parse internal ELF compilation artifact")?; + obj: &ObjectFile, + ) -> Result> { let text_section = obj.section_by_name(".text").unwrap(); let text_section_size = text_section.size() as usize; @@ -157,7 +164,6 @@ impl CodeMemory { code_range: &mut [], funcs: BTreeMap::new(), trampolines: BTreeMap::new(), - obj, }); } @@ -212,7 +218,6 @@ impl CodeMemory { code_range: &mut entry.mmap.as_mut_slice()[..text_section_size], funcs, trampolines, - obj, }) } } diff --git a/crates/jit/src/instantiate.rs b/crates/jit/src/instantiate.rs index d0587ee5b0..b891d80ef1 100644 --- a/crates/jit/src/instantiate.rs +++ b/crates/jit/src/instantiate.rs @@ -6,15 +6,18 @@ use crate::code_memory::CodeMemory; use crate::debug::create_gdbjit_image; use crate::link::link_module; -use anyhow::Result; +use anyhow::{anyhow, Context, Result}; +use object::read::File; +use object::write::{Object, StandardSegment}; +use object::{Object as _, ObjectSection, SectionKind}; use serde::{Deserialize, Serialize}; use std::ops::Range; use std::sync::Arc; use thiserror::Error; use wasmtime_environ::{ - CompileError, DebugInfoData, DefinedFuncIndex, FunctionInfo, InstanceSignature, - InstanceTypeIndex, Module, ModuleSignature, ModuleTranslation, ModuleTypeIndex, PrimaryMap, - SignatureIndex, StackMapInformation, Tunables, WasmFuncType, + CompileError, DefinedFuncIndex, FunctionInfo, InstanceSignature, InstanceTypeIndex, Module, + ModuleSignature, ModuleTranslation, ModuleTypeIndex, PrimaryMap, SignatureIndex, + StackMapInformation, Tunables, WasmFuncType, }; use wasmtime_profiling::ProfilingAgent; use wasmtime_runtime::{GdbJitImageRegistration, InstantiationError, VMFunctionBody, VMTrampoline}; @@ -51,9 +54,6 @@ pub struct CompilationArtifacts { /// ELF image with functions code. obj: Box<[u8]>, - /// All data segments referenced by this module, both active and passive. - wasm_data: Box<[u8]>, - /// Descriptions of compiled functions funcs: PrimaryMap, @@ -64,25 +64,10 @@ pub struct CompilationArtifacts { /// we skipped and did not parse. has_unparsed_debuginfo: bool, - /// Debug information found in the wasm file, used for symbolicating - /// backtraces. - debug_info: Option, -} - -#[derive(Serialize, Deserialize)] -struct DebugInfo { - data: Box<[u8]>, + /// Offset in the original wasm file to the code section. code_section_offset: u64, - debug_abbrev: Range, - debug_addr: Range, - debug_aranges: Range, - debug_info: Range, - debug_line: Range, - debug_line_str: Range, - debug_ranges: Range, - debug_rnglists: Range, - debug_str: Range, - debug_str_offsets: Range, + + has_wasm_debuginfo: bool, } impl CompilationArtifacts { @@ -90,10 +75,10 @@ impl CompilationArtifacts { /// compilation. pub fn new( translation: ModuleTranslation<'_>, - obj: Vec, + mut obj: Object, funcs: PrimaryMap, tunables: &Tunables, - ) -> CompilationArtifacts { + ) -> Result { let ModuleTranslation { mut module, debuginfo, @@ -103,45 +88,67 @@ impl CompilationArtifacts { .. } = translation; - // Concatenate all the wasm data together, placing both active and - // passive data into the same chunk of data. Note that this - // implementation doesn't allow for unmapping or somehow releasing - // passive data on `data.drop`, and if we want to do that in the future - // we'll have to change this to store passive data segments separately - // from the main data segments. - // - // Also note that here we have to update all passive data segments and - // their relative indices. - let wasm_data_size = data - .iter() - .map(|s| s.len()) - .chain(passive_data.iter().map(|s| s.len())) - .sum(); - let mut wasm_data = Vec::with_capacity(wasm_data_size); + // Place all data from the wasm module into a section which will the + // source of the data later at runtime. + let segment = obj.segment_name(StandardSegment::Data).to_vec(); + let section_id = obj.add_section(segment, b".wasmdata".to_vec(), SectionKind::ReadOnlyData); + let mut total_data_len = 0; for data in data.iter() { - wasm_data.extend_from_slice(data); + obj.append_section_data(section_id, data, 1); + total_data_len += data.len(); } - let total_data_len = wasm_data.len(); for data in passive_data.iter() { - wasm_data.extend_from_slice(data); + obj.append_section_data(section_id, data, 1); } + + // Update passive data offsets since they're all located after the other + // data in the module. for (_, range) in module.passive_data_map.iter_mut() { range.start = range.start.checked_add(total_data_len as u32).unwrap(); range.end = range.end.checked_add(total_data_len as u32).unwrap(); } - CompilationArtifacts { + // Insert the wasm raw wasm-based debuginfo into the output, if + // requested. Note that this is distinct from the native debuginfo + // possibly generated by the native compiler, hence these sections + // getting wasm-specific names. + if tunables.parse_wasm_debuginfo { + push_debug(&mut obj, &debuginfo.dwarf.debug_abbrev); + push_debug(&mut obj, &debuginfo.dwarf.debug_addr); + push_debug(&mut obj, &debuginfo.dwarf.debug_aranges); + push_debug(&mut obj, &debuginfo.dwarf.debug_info); + push_debug(&mut obj, &debuginfo.dwarf.debug_line); + push_debug(&mut obj, &debuginfo.dwarf.debug_line_str); + push_debug(&mut obj, &debuginfo.dwarf.debug_str); + push_debug(&mut obj, &debuginfo.dwarf.debug_str_offsets); + push_debug(&mut obj, &debuginfo.debug_ranges); + push_debug(&mut obj, &debuginfo.debug_rnglists); + } + + return Ok(CompilationArtifacts { module: Arc::new(module), - obj: obj.into_boxed_slice(), - wasm_data: wasm_data.into(), + obj: obj.write()?.into(), funcs, native_debug_info_present: tunables.generate_native_debuginfo, - debug_info: if tunables.parse_wasm_debuginfo { - Some(debuginfo.into()) - } else { - None - }, has_unparsed_debuginfo, + code_section_offset: debuginfo.wasm_file.code_section_offset, + has_wasm_debuginfo: tunables.parse_wasm_debuginfo, + }); + + fn push_debug<'a, T>(obj: &mut Object, section: &T) + where + T: gimli::Section>, + { + if section.reader().slice().is_empty() { + return; + } + let segment = obj.segment_name(StandardSegment::Debug).to_vec(); + let section_id = obj.add_section( + segment, + wasm_section_name(T::id()).as_bytes().to_vec(), + SectionKind::Debug, + ); + obj.append_section_data(section_id, section.reader().slice(), 1); } } } @@ -178,6 +185,7 @@ impl ModuleCode { /// A compiled wasm module, ready to be instantiated. pub struct CompiledModule { + wasm_data: Range, artifacts: CompilationArtifacts, code: Arc, finished_functions: FinishedFunctions, @@ -189,11 +197,14 @@ impl CompiledModule { pub fn from_artifacts( artifacts: CompilationArtifacts, profiler: &dyn ProfilingAgent, - ) -> Result, SetupError> { + ) -> Result> { + let obj = File::parse(&artifacts.obj[..]) + .with_context(|| "failed to parse internal ELF compilation artifact")?; + // Allocate all of the compiled functions into executable memory, // copying over their contents. let (code_memory, code_range, finished_functions, trampolines) = - build_code_memory(&artifacts.obj, &artifacts.module).map_err(|message| { + build_code_memory(&obj, &artifacts.module).map_err(|message| { SetupError::Instantiate(InstantiationError::Resource(anyhow::anyhow!( "failed to build code memory for functions: {}", message @@ -220,8 +231,14 @@ impl CompiledModule { let start = code_range.0 as usize; let end = start + code_range.1; + let data = obj + .section_by_name(".wasmdata") + .ok_or_else(|| anyhow!("failed to find internal data section for wasm module"))?; + let wasm_data = subslice_range(data.data()?, &artifacts.obj); + Ok(Arc::new(Self { artifacts, + wasm_data, code: Arc::new(ModuleCode { range: (start, end), code_memory, @@ -243,7 +260,7 @@ impl CompiledModule { /// This is used for initialization of memories and all data ranges stored /// in a `Module` are relative to the slice returned here. pub fn wasm_data(&self) -> &[u8] { - &self.artifacts.wasm_data + &self.artifacts.obj[self.wasm_data.clone()] } /// Return a reference-counting pointer to a module. @@ -338,38 +355,25 @@ impl CompiledModule { /// /// Basically this makes a thing which parses debuginfo and can tell you /// what filename and line number a wasm pc comes from. - pub fn symbolize_context(&self) -> Result, gimli::Error> { + pub fn symbolize_context(&self) -> Result>> { use gimli::EndianSlice; - let info = match &self.artifacts.debug_info { - Some(info) => info, - None => return Ok(None), - }; - // For now we clone the data into the `SymbolizeContext`, but if this - // becomes prohibitive we could always `Arc` it with our own allocation - // here. - let data = info.data.clone(); - let endian = gimli::LittleEndian; - let cx = addr2line::Context::from_sections( - EndianSlice::new(&data[info.debug_abbrev.clone()], endian).into(), - EndianSlice::new(&data[info.debug_addr.clone()], endian).into(), - EndianSlice::new(&data[info.debug_aranges.clone()], endian).into(), - EndianSlice::new(&data[info.debug_info.clone()], endian).into(), - EndianSlice::new(&data[info.debug_line.clone()], endian).into(), - EndianSlice::new(&data[info.debug_line_str.clone()], endian).into(), - EndianSlice::new(&data[info.debug_ranges.clone()], endian).into(), - EndianSlice::new(&data[info.debug_rnglists.clone()], endian).into(), - EndianSlice::new(&data[info.debug_str.clone()], endian).into(), - EndianSlice::new(&data[info.debug_str_offsets.clone()], endian).into(), - EndianSlice::new(&[], endian), - )?; + if !self.artifacts.has_wasm_debuginfo { + return Ok(None); + } + let obj = File::parse(&self.artifacts.obj[..]) + .context("failed to parse internal ELF file representation")?; + let dwarf = gimli::Dwarf::load(|id| -> Result<_> { + let data = obj + .section_by_name(wasm_section_name(id)) + .and_then(|s| s.data().ok()) + .unwrap_or(&[]); + Ok(EndianSlice::new(data, gimli::LittleEndian)) + })?; + let cx = addr2line::Context::from_dwarf(dwarf) + .context("failed to create addr2line dwarf mapping context")?; Ok(Some(SymbolizeContext { - // See comments on `SymbolizeContext` for why we do this static - // lifetime promotion. - inner: unsafe { - std::mem::transmute::, Addr2LineContext<'static>>(cx) - }, - code_section_offset: info.code_section_offset, - _data: data, + inner: cx, + code_section_offset: self.artifacts.code_section_offset, })) } @@ -384,27 +388,16 @@ type Addr2LineContext<'a> = addr2line::Context, - inner: Addr2LineContext<'static>, +pub struct SymbolizeContext<'a> { + inner: Addr2LineContext<'a>, code_section_offset: u64, } -impl SymbolizeContext { +impl<'a> SymbolizeContext<'a> { /// Returns access to the [`addr2line::Context`] which can be used to query /// frame information with. - pub fn addr2line(&self) -> &Addr2LineContext<'_> { - // Here we demote our synthetic `'static` lifetime which doesn't - // actually exist back to a lifetime that's tied to `&self`, which - // should be safe. - unsafe { - std::mem::transmute::<&Addr2LineContext<'static>, &Addr2LineContext<'_>>(&self.inner) - } + pub fn addr2line(&self) -> &Addr2LineContext<'a> { + &self.inner } /// Returns the offset of the code section in the original wasm file, used @@ -429,7 +422,7 @@ fn create_dbg_image( } fn build_code_memory( - obj: &[u8], + obj: &File, module: &Module, ) -> Result<( CodeMemory, @@ -469,7 +462,7 @@ fn build_code_memory( trampolines.push((i, fnptr)); } - link_module(&allocation.obj, allocation.code_range); + link_module(obj, allocation.code_range); let code_range = (allocation.code_range.as_ptr(), allocation.code_range.len()); @@ -479,42 +472,6 @@ fn build_code_memory( Ok((code_memory, code_range, finished_functions, trampolines)) } -impl From> for DebugInfo { - fn from(raw: DebugInfoData<'_>) -> DebugInfo { - use gimli::Section; - - let mut data = Vec::new(); - let mut push = |section: &[u8]| { - data.extend_from_slice(section); - data.len() - section.len()..data.len() - }; - let debug_abbrev = push(raw.dwarf.debug_abbrev.reader().slice()); - let debug_addr = push(raw.dwarf.debug_addr.reader().slice()); - let debug_aranges = push(raw.dwarf.debug_aranges.reader().slice()); - let debug_info = push(raw.dwarf.debug_info.reader().slice()); - let debug_line = push(raw.dwarf.debug_line.reader().slice()); - let debug_line_str = push(raw.dwarf.debug_line_str.reader().slice()); - let debug_ranges = push(raw.debug_ranges.reader().slice()); - let debug_rnglists = push(raw.debug_rnglists.reader().slice()); - let debug_str = push(raw.dwarf.debug_str.reader().slice()); - let debug_str_offsets = push(raw.dwarf.debug_str_offsets.reader().slice()); - DebugInfo { - data: data.into(), - debug_abbrev, - debug_addr, - debug_aranges, - debug_info, - debug_line, - debug_line_str, - debug_ranges, - debug_rnglists, - debug_str, - debug_str_offsets, - code_section_offset: raw.wasm_file.code_section_offset, - } - } -} - mod arc_serde { use super::Arc; use serde::{de::Deserialize, ser::Serialize, Deserializer, Serializer}; @@ -535,3 +492,52 @@ mod arc_serde { Ok(Arc::new(T::deserialize(de)?)) } } + +/// Returns the range of `inner` within `outer`, such that `outer[range]` is the +/// same as `inner`. +/// +/// This method requires that `inner` is a sub-slice of `outer`, and if that +/// isn't true then this method will panic. +fn subslice_range(inner: &[u8], outer: &[u8]) -> Range { + if inner.len() == 0 { + return 0..0; + } + + assert!(outer.as_ptr() <= inner.as_ptr()); + assert!((&inner[inner.len() - 1] as *const _) <= (&outer[outer.len() - 1] as *const _)); + + let start = inner.as_ptr() as usize - outer.as_ptr() as usize; + start..start + inner.len() +} + +/// Returns the Wasmtime-specific section name for dwarf debugging sections. +/// +/// These sections, if configured in Wasmtime, will contain the original raw +/// dwarf debugging information found in the wasm file, unmodified. These tables +/// are then consulted later to convert wasm program counters to original wasm +/// source filenames/line numbers with `addr2line`. +fn wasm_section_name(id: gimli::SectionId) -> &'static str { + use gimli::SectionId::*; + match id { + DebugAbbrev => ".debug_abbrev.wasm", + DebugAddr => ".debug_addr.wasm", + DebugAranges => ".debug_aranges.wasm", + DebugFrame => ".debug_frame.wasm", + EhFrame => ".eh_frame.wasm", + EhFrameHdr => ".eh_frame_hdr.wasm", + DebugInfo => ".debug_info.wasm", + DebugLine => ".debug_line.wasm", + DebugLineStr => ".debug_line_str.wasm", + DebugLoc => ".debug_loc.wasm", + DebugLocLists => ".debug_loc_lists.wasm", + DebugMacinfo => ".debug_macinfo.wasm", + DebugMacro => ".debug_macro.wasm", + DebugPubNames => ".debug_pub_names.wasm", + DebugPubTypes => ".debug_pub_types.wasm", + DebugRanges => ".debug_ranges.wasm", + DebugRngLists => ".debug_rng_lists.wasm", + DebugStr => ".debug_str.wasm", + DebugStrOffsets => ".debug_str_offsets.wasm", + DebugTypes => ".debug_types.wasm", + } +} diff --git a/crates/lightbeam/wasmtime/Cargo.toml b/crates/lightbeam/wasmtime/Cargo.toml index c316bbbac2..e3ab5f46f1 100644 --- a/crates/lightbeam/wasmtime/Cargo.toml +++ b/crates/lightbeam/wasmtime/Cargo.toml @@ -19,3 +19,4 @@ lightbeam = { path = "..", version = "0.29.0" } wasmparser = "0.80" cranelift-codegen = { path = "../../../cranelift/codegen", version = "0.76.0" } wasmtime-environ = { path = "../../environ", version = "0.29.0" } +object = { version = "0.26.0", default-features = false } diff --git a/crates/lightbeam/wasmtime/src/lib.rs b/crates/lightbeam/wasmtime/src/lib.rs index 4850e3ffc0..affc180547 100644 --- a/crates/lightbeam/wasmtime/src/lib.rs +++ b/crates/lightbeam/wasmtime/src/lib.rs @@ -8,6 +8,7 @@ use anyhow::Result; use cranelift_codegen::binemit; use cranelift_codegen::ir::{self, ExternalName}; +use object::write::Object; use std::any::Any; use std::collections::BTreeMap; use wasmtime_environ::{ @@ -84,11 +85,17 @@ impl Compiler for Lightbeam { _types: &TypeTables, _funcs: PrimaryMap>, _emit_dwarf: bool, - ) -> Result<(Vec, PrimaryMap)> { + _obj: &mut Object, + ) -> Result> { unimplemented!() } - fn emit_trampoline_obj(&self, _ty: &WasmFuncType, _host_fn: usize) -> Result> { + fn emit_trampoline_obj( + &self, + _ty: &WasmFuncType, + _host_fn: usize, + _obj: &mut Object, + ) -> Result<()> { unimplemented!() } diff --git a/crates/wasmtime/src/module.rs b/crates/wasmtime/src/module.rs index 8fdd97ad11..d68b3b81b4 100644 --- a/crates/wasmtime/src/module.rs +++ b/crates/wasmtime/src/module.rs @@ -369,11 +369,13 @@ impl Module { .into_iter() .collect(); - let (obj, funcs) = engine.compiler().emit_obj( + let mut obj = engine.compiler().object()?; + let funcs = engine.compiler().emit_obj( &translation, &types, funcs, tunables.generate_native_debuginfo, + &mut obj, )?; // If configured, attempt to use paged memory initialization @@ -382,7 +384,12 @@ impl Module { translation.try_paged_init(); } - Ok(CompilationArtifacts::new(translation, obj, funcs, tunables)) + Ok(CompilationArtifacts::new( + translation, + obj, + funcs, + tunables, + )?) })?; Ok(( diff --git a/crates/wasmtime/src/trampoline/func.rs b/crates/wasmtime/src/trampoline/func.rs index b703b45978..03d36e40da 100644 --- a/crates/wasmtime/src/trampoline/func.rs +++ b/crates/wasmtime/src/trampoline/func.rs @@ -76,12 +76,14 @@ pub fn create_function( func: Box Result<(), Trap> + Send + Sync>, engine: &Engine, ) -> Result<(InstanceHandle, VMTrampoline)> { - let obj = engine + let mut obj = engine.compiler().object()?; + engine .compiler() - .emit_trampoline_obj(ft.as_wasm_func_type(), stub_fn as usize)?; + .emit_trampoline_obj(ft.as_wasm_func_type(), stub_fn as usize, &mut obj)?; + let obj = obj.write()?; let mut code_memory = CodeMemory::new(); - let alloc = code_memory.allocate_for_object(&obj)?; + let alloc = code_memory.allocate_for_object_unparsed(&obj)?; let mut trampolines = alloc.trampolines(); let (host_i, host_trampoline) = trampolines.next().unwrap(); assert_eq!(host_i.as_u32(), 0); diff --git a/src/obj.rs b/src/obj.rs index 876b6b709b..bff0957f7f 100644 --- a/src/obj.rs +++ b/src/obj.rs @@ -55,11 +55,13 @@ pub fn compile_to_obj( for (index, func) in mem::take(&mut translation[0].function_body_inputs) { funcs.push(compiler.compile_function(&translation[0], index, func, &tunables, &types)?); } - let (obj, _) = compiler.emit_obj( + let mut obj = compiler.object()?; + compiler.emit_obj( &translation[0], &types, funcs, tunables.generate_native_debuginfo, + &mut obj, )?; - Ok(obj) + Ok(obj.write()?) }