Don't generate DWARF sections when no functions were compiled. (#894)
This commit is contained in:
@@ -7,14 +7,28 @@ use wasmtime_environ::entity::EntityRef;
|
|||||||
use wasmtime_environ::ir::{StackSlots, ValueLabel, ValueLabelsRanges, ValueLoc};
|
use wasmtime_environ::ir::{StackSlots, ValueLabel, ValueLabelsRanges, ValueLoc};
|
||||||
use wasmtime_environ::isa::RegUnit;
|
use wasmtime_environ::isa::RegUnit;
|
||||||
use wasmtime_environ::wasm::{get_vmctx_value_label, DefinedFuncIndex};
|
use wasmtime_environ::wasm::{get_vmctx_value_label, DefinedFuncIndex};
|
||||||
|
use wasmtime_environ::ModuleMemoryOffset;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct FunctionFrameInfo<'a> {
|
pub struct FunctionFrameInfo<'a> {
|
||||||
pub value_ranges: &'a ValueLabelsRanges,
|
pub value_ranges: &'a ValueLabelsRanges,
|
||||||
pub memory_offset: i64,
|
pub memory_offset: ModuleMemoryOffset,
|
||||||
pub stack_slots: &'a StackSlots,
|
pub stack_slots: &'a StackSlots,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> FunctionFrameInfo<'a> {
|
||||||
|
fn vmctx_memory_offset(&self) -> Option<i64> {
|
||||||
|
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)]
|
#[derive(Debug)]
|
||||||
enum CompiledExpressionPart {
|
enum CompiledExpressionPart {
|
||||||
Code(Vec<u8>),
|
Code(Vec<u8>),
|
||||||
@@ -142,20 +156,32 @@ fn append_memory_deref(
|
|||||||
) -> write::Result<bool> {
|
) -> write::Result<bool> {
|
||||||
use gimli::write::Writer;
|
use gimli::write::Writer;
|
||||||
let mut writer = write::EndianVec::new(endian);
|
let mut writer = write::EndianVec::new(endian);
|
||||||
|
// FIXME for imported memory
|
||||||
match vmctx_loc {
|
match vmctx_loc {
|
||||||
ValueLoc::Reg(vmctx_reg) => {
|
ValueLoc::Reg(vmctx_reg) => {
|
||||||
let reg = map_reg(vmctx_reg);
|
let reg = map_reg(vmctx_reg);
|
||||||
writer.write_u8(gimli::constants::DW_OP_breg0.0 + reg.0 as u8)?;
|
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) => {
|
ValueLoc::Stack(ss) => {
|
||||||
if let Some(ss_offset) = frame_info.stack_slots[ss].offset {
|
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_u8(gimli::constants::DW_OP_breg0.0 + X86_64::RBP.0 as u8)?;
|
||||||
writer.write_sleb128(ss_offset as i64 + 16)?;
|
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_deref.0 as u8)?;
|
||||||
|
|
||||||
writer.write_u8(gimli::constants::DW_OP_consts.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)?;
|
writer.write_u8(gimli::constants::DW_OP_plus.0 as u8)?;
|
||||||
} else {
|
} else {
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use super::expression::{CompiledExpression, FunctionFrameInfo};
|
|||||||
use anyhow::Error;
|
use anyhow::Error;
|
||||||
use gimli::write;
|
use gimli::write;
|
||||||
use wasmtime_environ::wasm::DefinedFuncIndex;
|
use wasmtime_environ::wasm::DefinedFuncIndex;
|
||||||
use wasmtime_environ::{ModuleVmctxInfo, ValueLabelsRanges};
|
use wasmtime_environ::{ModuleMemoryOffset, ModuleVmctxInfo, ValueLabelsRanges};
|
||||||
|
|
||||||
pub(crate) fn add_internal_types(
|
pub(crate) fn add_internal_types(
|
||||||
comp_unit: &mut write::Unit,
|
comp_unit: &mut write::Unit,
|
||||||
@@ -46,18 +46,26 @@ pub(crate) fn add_internal_types(
|
|||||||
write::AttributeValue::ThisUnitEntryRef(memory_byte_die_id),
|
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_id = comp_unit.add(root_id, gimli::DW_TAG_structure_type);
|
||||||
let vmctx_die = comp_unit.get_mut(vmctx_die_id);
|
let vmctx_die = comp_unit.get_mut(vmctx_die_id);
|
||||||
vmctx_die.set(
|
vmctx_die.set(
|
||||||
gimli::DW_AT_name,
|
gimli::DW_AT_name,
|
||||||
write::AttributeValue::StringRef(out_strings.add("WasmtimeVMContext")),
|
write::AttributeValue::StringRef(out_strings.add("WasmtimeVMContext")),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
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(
|
vmctx_die.set(
|
||||||
gimli::DW_AT_byte_size,
|
gimli::DW_AT_byte_size,
|
||||||
write::AttributeValue::Data4(memory_offset as u32 + 8),
|
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_id = comp_unit.add(vmctx_die_id, gimli::DW_TAG_member);
|
||||||
let m_die = comp_unit.get_mut(m_die_id);
|
let m_die = comp_unit.get_mut(m_die_id);
|
||||||
m_die.set(
|
m_die.set(
|
||||||
@@ -72,6 +80,12 @@ pub(crate) fn add_internal_types(
|
|||||||
gimli::DW_AT_data_member_location,
|
gimli::DW_AT_data_member_location,
|
||||||
write::AttributeValue::Udata(memory_offset as u64),
|
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_id = comp_unit.add(root_id, gimli::DW_TAG_pointer_type);
|
||||||
let vmctx_ptr_die = comp_unit.get_mut(vmctx_ptr_die_id);
|
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) {
|
if let Some(value_ranges) = value_ranges.get(func_index) {
|
||||||
let frame_info = FunctionFrameInfo {
|
let frame_info = FunctionFrameInfo {
|
||||||
value_ranges,
|
value_ranges,
|
||||||
memory_offset: module_info.memory_offset,
|
memory_offset: module_info.memory_offset.clone(),
|
||||||
stack_slots: &module_info.stack_slots[func_index],
|
stack_slots: &module_info.stack_slots[func_index],
|
||||||
};
|
};
|
||||||
Some(frame_info)
|
Some(frame_info)
|
||||||
|
|||||||
@@ -48,10 +48,22 @@ pub type ValueLabelsRanges = PrimaryMap<DefinedFuncIndex, cranelift_codegen::Val
|
|||||||
/// Stack slots for functions.
|
/// Stack slots for functions.
|
||||||
pub type StackSlots = PrimaryMap<DefinedFuncIndex, ir::StackSlots>;
|
pub type StackSlots = PrimaryMap<DefinedFuncIndex, ir::StackSlots>;
|
||||||
|
|
||||||
|
/// 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.
|
/// Module `vmctx` related info.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub struct ModuleVmctxInfo {
|
pub struct ModuleVmctxInfo {
|
||||||
/// The memory definition offset in the VMContext structure.
|
/// The memory definition offset in the VMContext structure.
|
||||||
pub memory_offset: i64,
|
pub memory_offset: ModuleMemoryOffset,
|
||||||
|
|
||||||
/// The functions stack slots.
|
/// The functions stack slots.
|
||||||
pub stack_slots: StackSlots,
|
pub stack_slots: StackSlots,
|
||||||
|
|||||||
@@ -40,7 +40,8 @@ pub mod cranelift;
|
|||||||
pub mod lightbeam;
|
pub mod lightbeam;
|
||||||
|
|
||||||
pub use crate::address_map::{
|
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::cache::{create_new_config as cache_create_new_config, init as cache_init};
|
||||||
pub use crate::compilation::{
|
pub use crate::compilation::{
|
||||||
|
|||||||
@@ -13,3 +13,9 @@ fn instantiate_empty_module() {
|
|||||||
let data = wat::parse_str(include_str!("./regressions/empty.wat")).unwrap();
|
let data = wat::parse_str(include_str!("./regressions/empty.wat")).unwrap();
|
||||||
oracles::instantiate(&data, Strategy::Auto);
|
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);
|
||||||
|
}
|
||||||
|
|||||||
1
crates/fuzzing/tests/regressions/empty_with_memory.wat
Normal file
1
crates/fuzzing/tests/regressions/empty_with_memory.wat
Normal file
@@ -0,0 +1 @@
|
|||||||
|
(module (memory 1))
|
||||||
@@ -14,10 +14,11 @@ use std::convert::TryFrom;
|
|||||||
use wasmtime_debug::{emit_debugsections_image, DebugInfoData};
|
use wasmtime_debug::{emit_debugsections_image, DebugInfoData};
|
||||||
use wasmtime_environ::entity::{EntityRef, PrimaryMap};
|
use wasmtime_environ::entity::{EntityRef, PrimaryMap};
|
||||||
use wasmtime_environ::isa::{TargetFrontendConfig, TargetIsa};
|
use wasmtime_environ::isa::{TargetFrontendConfig, TargetIsa};
|
||||||
use wasmtime_environ::wasm::{DefinedFuncIndex, DefinedMemoryIndex};
|
use wasmtime_environ::wasm::{DefinedFuncIndex, DefinedMemoryIndex, MemoryIndex};
|
||||||
use wasmtime_environ::{
|
use wasmtime_environ::{
|
||||||
Compilation, CompileError, CompiledFunction, CompiledFunctionUnwindInfo, Compiler as _C,
|
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::{
|
use wasmtime_runtime::{
|
||||||
get_mut_trap_registry, jit_function_registry, InstantiationError, SignatureRegistry,
|
get_mut_trap_registry, jit_function_registry, InstantiationError, SignatureRegistry,
|
||||||
@@ -174,10 +175,11 @@ impl Compiler {
|
|||||||
jit_function_registry::register(ptr as usize, ptr as usize + body_len, tag);
|
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 target_config = self.isa.frontend_config();
|
||||||
let ofs = VMOffsets::new(target_config.pointer_bytes(), &module);
|
let ofs = VMOffsets::new(target_config.pointer_bytes(), &module);
|
||||||
if ofs.num_defined_memories > 0 {
|
|
||||||
let mut funcs = Vec::new();
|
let mut funcs = Vec::new();
|
||||||
for (i, allocated) in allocated_functions.into_iter() {
|
for (i, allocated) in allocated_functions.into_iter() {
|
||||||
let ptr = (*allocated) as *const u8;
|
let ptr = (*allocated) as *const u8;
|
||||||
@@ -185,17 +187,23 @@ impl Compiler {
|
|||||||
funcs.push((ptr, body_len));
|
funcs.push((ptr, body_len));
|
||||||
}
|
}
|
||||||
let module_vmctx_info = {
|
let module_vmctx_info = {
|
||||||
let memory_offset =
|
|
||||||
ofs.vmctx_vmmemory_definition_base(DefinedMemoryIndex::new(0)) as i64;
|
|
||||||
ModuleVmctxInfo {
|
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,
|
stack_slots,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let bytes = emit_debugsections_image(
|
let bytes = emit_debugsections_image(
|
||||||
self.isa.triple().clone(),
|
self.isa.triple().clone(),
|
||||||
target_config,
|
target_config,
|
||||||
&debug_data,
|
debug_data.as_ref().unwrap(),
|
||||||
&module_vmctx_info,
|
&module_vmctx_info,
|
||||||
&address_transform,
|
&address_transform,
|
||||||
&value_ranges,
|
&value_ranges,
|
||||||
@@ -205,9 +213,6 @@ impl Compiler {
|
|||||||
Some(bytes)
|
Some(bytes)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let jt_offsets = compilation.get_jt_offsets();
|
let jt_offsets = compilation.get_jt_offsets();
|
||||||
|
|||||||
@@ -17,7 +17,8 @@ use wasmtime_debug::{emit_debugsections, read_debuginfo};
|
|||||||
use wasmtime_environ::Lightbeam;
|
use wasmtime_environ::Lightbeam;
|
||||||
use wasmtime_environ::{
|
use wasmtime_environ::{
|
||||||
cache_init, entity::EntityRef, settings, settings::Configurable, wasm::DefinedMemoryIndex,
|
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_jit::native;
|
||||||
use wasmtime_obj::emit_module;
|
use wasmtime_obj::emit_module;
|
||||||
@@ -161,12 +162,22 @@ impl WasmToObjCommand {
|
|||||||
}
|
}
|
||||||
.context("failed to compile module")?;
|
.context("failed to compile module")?;
|
||||||
|
|
||||||
|
if compilation.is_empty() {
|
||||||
|
bail!("no functions were found/compiled");
|
||||||
|
}
|
||||||
|
|
||||||
let module_vmctx_info = {
|
let module_vmctx_info = {
|
||||||
let ofs = VMOffsets::new(target_config.pointer_bytes(), &module);
|
let ofs = VMOffsets::new(target_config.pointer_bytes(), &module);
|
||||||
let memory_offset =
|
|
||||||
ofs.vmctx_vmmemory_definition_base(DefinedMemoryIndex::new(0)) as i64;
|
|
||||||
ModuleVmctxInfo {
|
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,
|
stack_slots,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user