From 4599234c6fbac71be78f5d47a85465d9171f6aa9 Mon Sep 17 00:00:00 2001 From: Yury Delendik Date: Mon, 3 Feb 2020 14:41:29 -0600 Subject: [PATCH] Don't generate DWARF sections when no functions were compiled. (#894) --- crates/debug/src/transform/expression.rs | 34 ++++++++-- crates/debug/src/transform/utils.rs | 56 ++++++++++------ crates/environ/src/address_map.rs | 14 +++- crates/environ/src/lib.rs | 3 +- crates/fuzzing/tests/regressions.rs | 6 ++ .../tests/regressions/empty_with_memory.wat | 1 + crates/jit/src/compiler.rs | 67 ++++++++++--------- src/commands/wasm2obj.rs | 19 ++++-- 8 files changed, 138 insertions(+), 62 deletions(-) create mode 100644 crates/fuzzing/tests/regressions/empty_with_memory.wat diff --git a/crates/debug/src/transform/expression.rs b/crates/debug/src/transform/expression.rs index ac93c21db2..e24fa7fb9f 100644 --- a/crates/debug/src/transform/expression.rs +++ b/crates/debug/src/transform/expression.rs @@ -7,14 +7,28 @@ use wasmtime_environ::entity::EntityRef; use wasmtime_environ::ir::{StackSlots, ValueLabel, ValueLabelsRanges, ValueLoc}; use wasmtime_environ::isa::RegUnit; use wasmtime_environ::wasm::{get_vmctx_value_label, DefinedFuncIndex}; +use wasmtime_environ::ModuleMemoryOffset; #[derive(Debug)] pub struct FunctionFrameInfo<'a> { pub value_ranges: &'a ValueLabelsRanges, - pub memory_offset: i64, + pub memory_offset: ModuleMemoryOffset, pub stack_slots: &'a StackSlots, } +impl<'a> FunctionFrameInfo<'a> { + fn vmctx_memory_offset(&self) -> Option { + match self.memory_offset { + ModuleMemoryOffset::Defined(x) => Some(x as i64), + ModuleMemoryOffset::Imported(_) => { + // TODO implement memory offset for imported memory + None + } + ModuleMemoryOffset::None => None, + } + } +} + #[derive(Debug)] enum CompiledExpressionPart { Code(Vec), @@ -142,20 +156,32 @@ fn append_memory_deref( ) -> write::Result { use gimli::write::Writer; let mut writer = write::EndianVec::new(endian); + // FIXME for imported memory match vmctx_loc { ValueLoc::Reg(vmctx_reg) => { let reg = map_reg(vmctx_reg); writer.write_u8(gimli::constants::DW_OP_breg0.0 + reg.0 as u8)?; - writer.write_sleb128(frame_info.memory_offset)?; + let memory_offset = match frame_info.vmctx_memory_offset() { + Some(offset) => offset, + None => { + return Ok(false); + } + }; + writer.write_sleb128(memory_offset)?; } ValueLoc::Stack(ss) => { if let Some(ss_offset) = frame_info.stack_slots[ss].offset { writer.write_u8(gimli::constants::DW_OP_breg0.0 + X86_64::RBP.0 as u8)?; writer.write_sleb128(ss_offset as i64 + 16)?; writer.write_u8(gimli::constants::DW_OP_deref.0 as u8)?; - writer.write_u8(gimli::constants::DW_OP_consts.0 as u8)?; - writer.write_sleb128(frame_info.memory_offset)?; + let memory_offset = match frame_info.vmctx_memory_offset() { + Some(offset) => offset, + None => { + return Ok(false); + } + }; + writer.write_sleb128(memory_offset)?; writer.write_u8(gimli::constants::DW_OP_plus.0 as u8)?; } else { return Ok(false); diff --git a/crates/debug/src/transform/utils.rs b/crates/debug/src/transform/utils.rs index 9faaa9f62a..2b921581cb 100644 --- a/crates/debug/src/transform/utils.rs +++ b/crates/debug/src/transform/utils.rs @@ -3,7 +3,7 @@ use super::expression::{CompiledExpression, FunctionFrameInfo}; use anyhow::Error; use gimli::write; use wasmtime_environ::wasm::DefinedFuncIndex; -use wasmtime_environ::{ModuleVmctxInfo, ValueLabelsRanges}; +use wasmtime_environ::{ModuleMemoryOffset, ModuleVmctxInfo, ValueLabelsRanges}; pub(crate) fn add_internal_types( comp_unit: &mut write::Unit, @@ -46,32 +46,46 @@ pub(crate) fn add_internal_types( write::AttributeValue::ThisUnitEntryRef(memory_byte_die_id), ); - let memory_offset = module_info.memory_offset; + // Create artificial VMContext type and its reference for convinience viewing + // its fields (such as memory ref) in a debugger. let vmctx_die_id = comp_unit.add(root_id, gimli::DW_TAG_structure_type); let vmctx_die = comp_unit.get_mut(vmctx_die_id); vmctx_die.set( gimli::DW_AT_name, write::AttributeValue::StringRef(out_strings.add("WasmtimeVMContext")), ); - vmctx_die.set( - gimli::DW_AT_byte_size, - write::AttributeValue::Data4(memory_offset as u32 + 8), - ); - let m_die_id = comp_unit.add(vmctx_die_id, gimli::DW_TAG_member); - let m_die = comp_unit.get_mut(m_die_id); - m_die.set( - gimli::DW_AT_name, - write::AttributeValue::StringRef(out_strings.add("memory")), - ); - m_die.set( - gimli::DW_AT_type, - write::AttributeValue::ThisUnitEntryRef(memory_bytes_die_id), - ); - m_die.set( - gimli::DW_AT_data_member_location, - write::AttributeValue::Udata(memory_offset as u64), - ); + match module_info.memory_offset { + ModuleMemoryOffset::Defined(memory_offset) => { + // The context has defined memory: extend the WasmtimeVMContext size + // past the "memory" field. + const MEMORY_FIELD_SIZE_PLUS_PADDING: u32 = 8; + vmctx_die.set( + gimli::DW_AT_byte_size, + write::AttributeValue::Data4(memory_offset + MEMORY_FIELD_SIZE_PLUS_PADDING), + ); + + // Define the "memory" field which is a direct pointer to allocated Wasm memory. + let m_die_id = comp_unit.add(vmctx_die_id, gimli::DW_TAG_member); + let m_die = comp_unit.get_mut(m_die_id); + m_die.set( + gimli::DW_AT_name, + write::AttributeValue::StringRef(out_strings.add("memory")), + ); + m_die.set( + gimli::DW_AT_type, + write::AttributeValue::ThisUnitEntryRef(memory_bytes_die_id), + ); + m_die.set( + gimli::DW_AT_data_member_location, + write::AttributeValue::Udata(memory_offset as u64), + ); + } + ModuleMemoryOffset::Imported(_) => { + // TODO implement convinience pointer to and additional types for VMMemoryImport. + } + ModuleMemoryOffset::None => (), + } let vmctx_ptr_die_id = comp_unit.add(root_id, gimli::DW_TAG_pointer_type); let vmctx_ptr_die = comp_unit.get_mut(vmctx_ptr_die_id); @@ -141,7 +155,7 @@ where if let Some(value_ranges) = value_ranges.get(func_index) { let frame_info = FunctionFrameInfo { value_ranges, - memory_offset: module_info.memory_offset, + memory_offset: module_info.memory_offset.clone(), stack_slots: &module_info.stack_slots[func_index], }; Some(frame_info) diff --git a/crates/environ/src/address_map.rs b/crates/environ/src/address_map.rs index 3ca12abebb..461f550eb2 100644 --- a/crates/environ/src/address_map.rs +++ b/crates/environ/src/address_map.rs @@ -48,10 +48,22 @@ pub type ValueLabelsRanges = PrimaryMap; +/// Memory definition offset in the VMContext structure. +#[derive(Debug, Clone)] +pub enum ModuleMemoryOffset { + /// Not available. + None, + /// Offset to the defined memory. + Defined(u32), + /// Offset to the imported memory. + Imported(u32), +} + /// Module `vmctx` related info. +#[derive(Debug, Clone)] pub struct ModuleVmctxInfo { /// The memory definition offset in the VMContext structure. - pub memory_offset: i64, + pub memory_offset: ModuleMemoryOffset, /// The functions stack slots. pub stack_slots: StackSlots, diff --git a/crates/environ/src/lib.rs b/crates/environ/src/lib.rs index ec5525fec2..316af5b77e 100644 --- a/crates/environ/src/lib.rs +++ b/crates/environ/src/lib.rs @@ -40,7 +40,8 @@ pub mod cranelift; pub mod lightbeam; pub use crate::address_map::{ - FunctionAddressMap, InstructionAddressMap, ModuleAddressMap, ModuleVmctxInfo, ValueLabelsRanges, + FunctionAddressMap, InstructionAddressMap, ModuleAddressMap, ModuleMemoryOffset, + ModuleVmctxInfo, ValueLabelsRanges, }; pub use crate::cache::{create_new_config as cache_create_new_config, init as cache_init}; pub use crate::compilation::{ diff --git a/crates/fuzzing/tests/regressions.rs b/crates/fuzzing/tests/regressions.rs index 925720c0da..5ad96e689d 100644 --- a/crates/fuzzing/tests/regressions.rs +++ b/crates/fuzzing/tests/regressions.rs @@ -13,3 +13,9 @@ fn instantiate_empty_module() { let data = wat::parse_str(include_str!("./regressions/empty.wat")).unwrap(); oracles::instantiate(&data, Strategy::Auto); } + +#[test] +fn instantiate_empty_module_with_memory() { + let data = wat::parse_str(include_str!("./regressions/empty_with_memory.wat")).unwrap(); + oracles::instantiate(&data, Strategy::Auto); +} diff --git a/crates/fuzzing/tests/regressions/empty_with_memory.wat b/crates/fuzzing/tests/regressions/empty_with_memory.wat new file mode 100644 index 0000000000..4503196d52 --- /dev/null +++ b/crates/fuzzing/tests/regressions/empty_with_memory.wat @@ -0,0 +1 @@ +(module (memory 1)) diff --git a/crates/jit/src/compiler.rs b/crates/jit/src/compiler.rs index 302eed0c3d..ca4d8012d8 100644 --- a/crates/jit/src/compiler.rs +++ b/crates/jit/src/compiler.rs @@ -14,10 +14,11 @@ use std::convert::TryFrom; use wasmtime_debug::{emit_debugsections_image, DebugInfoData}; use wasmtime_environ::entity::{EntityRef, PrimaryMap}; use wasmtime_environ::isa::{TargetFrontendConfig, TargetIsa}; -use wasmtime_environ::wasm::{DefinedFuncIndex, DefinedMemoryIndex}; +use wasmtime_environ::wasm::{DefinedFuncIndex, DefinedMemoryIndex, MemoryIndex}; use wasmtime_environ::{ Compilation, CompileError, CompiledFunction, CompiledFunctionUnwindInfo, Compiler as _C, - FunctionBodyData, Module, ModuleVmctxInfo, Relocations, Traps, Tunables, VMOffsets, + FunctionBodyData, Module, ModuleMemoryOffset, ModuleVmctxInfo, Relocations, Traps, Tunables, + VMOffsets, }; use wasmtime_runtime::{ get_mut_trap_registry, jit_function_registry, InstantiationError, SignatureRegistry, @@ -174,38 +175,42 @@ impl Compiler { jit_function_registry::register(ptr as usize, ptr as usize + body_len, tag); } - let dbg = if let Some(debug_data) = debug_data { + // Translate debug info (DWARF) only if at least one function is present. + let dbg = if debug_data.is_some() && !allocated_functions.is_empty() { let target_config = self.isa.frontend_config(); let ofs = VMOffsets::new(target_config.pointer_bytes(), &module); - if ofs.num_defined_memories > 0 { - let mut funcs = Vec::new(); - for (i, allocated) in allocated_functions.into_iter() { - let ptr = (*allocated) as *const u8; - let body_len = compilation.get(i).body.len(); - funcs.push((ptr, body_len)); - } - let module_vmctx_info = { - let memory_offset = - ofs.vmctx_vmmemory_definition_base(DefinedMemoryIndex::new(0)) as i64; - ModuleVmctxInfo { - memory_offset, - stack_slots, - } - }; - let bytes = emit_debugsections_image( - self.isa.triple().clone(), - target_config, - &debug_data, - &module_vmctx_info, - &address_transform, - &value_ranges, - &funcs, - ) - .map_err(SetupError::DebugInfo)?; - Some(bytes) - } else { - None + + let mut funcs = Vec::new(); + for (i, allocated) in allocated_functions.into_iter() { + let ptr = (*allocated) as *const u8; + let body_len = compilation.get(i).body.len(); + funcs.push((ptr, body_len)); } + let module_vmctx_info = { + ModuleVmctxInfo { + memory_offset: if ofs.num_imported_memories > 0 { + ModuleMemoryOffset::Imported(ofs.vmctx_vmmemory_import(MemoryIndex::new(0))) + } else if ofs.num_defined_memories > 0 { + ModuleMemoryOffset::Defined( + ofs.vmctx_vmmemory_definition_base(DefinedMemoryIndex::new(0)), + ) + } else { + ModuleMemoryOffset::None + }, + stack_slots, + } + }; + let bytes = emit_debugsections_image( + self.isa.triple().clone(), + target_config, + debug_data.as_ref().unwrap(), + &module_vmctx_info, + &address_transform, + &value_ranges, + &funcs, + ) + .map_err(SetupError::DebugInfo)?; + Some(bytes) } else { None }; diff --git a/src/commands/wasm2obj.rs b/src/commands/wasm2obj.rs index 7c7798926b..efd44e0279 100644 --- a/src/commands/wasm2obj.rs +++ b/src/commands/wasm2obj.rs @@ -17,7 +17,8 @@ use wasmtime_debug::{emit_debugsections, read_debuginfo}; use wasmtime_environ::Lightbeam; use wasmtime_environ::{ cache_init, entity::EntityRef, settings, settings::Configurable, wasm::DefinedMemoryIndex, - Compiler, Cranelift, ModuleEnvironment, ModuleVmctxInfo, Tunables, VMOffsets, + wasm::MemoryIndex, Compiler, Cranelift, ModuleEnvironment, ModuleMemoryOffset, ModuleVmctxInfo, + Tunables, VMOffsets, }; use wasmtime_jit::native; use wasmtime_obj::emit_module; @@ -161,12 +162,22 @@ impl WasmToObjCommand { } .context("failed to compile module")?; + if compilation.is_empty() { + bail!("no functions were found/compiled"); + } + let module_vmctx_info = { let ofs = VMOffsets::new(target_config.pointer_bytes(), &module); - let memory_offset = - ofs.vmctx_vmmemory_definition_base(DefinedMemoryIndex::new(0)) as i64; ModuleVmctxInfo { - memory_offset, + memory_offset: if ofs.num_imported_memories > 0 { + ModuleMemoryOffset::Imported(ofs.vmctx_vmmemory_import(MemoryIndex::new(0))) + } else if ofs.num_defined_memories > 0 { + ModuleMemoryOffset::Defined( + ofs.vmctx_vmmemory_definition_base(DefinedMemoryIndex::new(0)), + ) + } else { + ModuleMemoryOffset::None + }, stack_slots, } };