feat(llvm): Continue implementation of compact_unwind support

This commit is contained in:
Edoardo Marangoni
2025-02-04 10:33:45 +01:00
parent c3c3f9dd88
commit 1c3f177205
22 changed files with 900 additions and 518 deletions

14
Cargo.lock generated
View File

@ -6590,7 +6590,7 @@ dependencies = [
"wasmer-types",
"wasmer-vm",
"wasmi_c_api_impl",
"wasmparser 0.221.2",
"wasmparser 0.224.0",
"wat",
"which",
"windows-sys 0.59.0",
@ -6858,7 +6858,7 @@ dependencies = [
"thiserror 1.0.69",
"wasmer-types",
"wasmer-vm",
"wasmparser 0.221.2",
"wasmparser 0.224.0",
"windows-sys 0.59.0",
"xxhash-rust",
]
@ -7444,6 +7444,16 @@ dependencies = [
"semver 1.0.23",
]
[[package]]
name = "wasmparser"
version = "0.224.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "65881a664fdd43646b647bb27bf186ab09c05bf56779d40aed4c6dce47d423f5"
dependencies = [
"bitflags 2.6.0",
"semver 1.0.23",
]
[[package]]
name = "wasmprinter"
version = "0.2.80"

View File

@ -102,7 +102,7 @@ hyper = "1"
reqwest = { version = "0.12.0", default-features = false }
enumset = "1.1.0"
memoffset = "0.9.0"
wasmparser = { version = "0.221.0", default-features = false, features = [
wasmparser = { version = "0.224.0", default-features = false, features = [
"validate",
"features",
"component-model",

View File

@ -29,7 +29,9 @@ use std::sync::Arc;
use wasmer_compiler::{
types::{
function::{Compilation, CompiledFunction, CompiledFunctionFrameInfo, Dwarf, FunctionBody},
function::{
Compilation, CompiledFunction, CompiledFunctionFrameInfo, FunctionBody, UnwindInfo,
},
module::CompileModuleInfo,
relocation::{Relocation, RelocationTarget},
section::SectionIndex,
@ -350,8 +352,10 @@ impl Compiler for CraneliftCompiler {
.into_iter()
.unzip();
let mut unwind_info = UnwindInfo::default();
#[cfg(feature = "unwind")]
let dwarf = if let Some((mut dwarf_frametable, cie_id)) = dwarf_frametable {
if let Some((mut dwarf_frametable, cie_id)) = dwarf_frametable {
for fde in fdes.into_iter().flatten() {
dwarf_frametable.add_fde(cie_id, fde);
}
@ -360,12 +364,8 @@ impl Compiler for CraneliftCompiler {
let eh_frame_section = eh_frame.0.into_section();
custom_sections.push(eh_frame_section);
Some(Dwarf::new(SectionIndex::new(custom_sections.len() - 1)))
} else {
None
unwind_info.eh_frame = Some(SectionIndex::new(custom_sections.len() - 1));
};
#[cfg(not(feature = "unwind"))]
let dwarf = None;
// function call trampolines (only for local functions, by signature)
#[cfg(not(feature = "rayon"))]
@ -419,13 +419,15 @@ impl Compiler for CraneliftCompiler {
.into_iter()
.collect::<PrimaryMap<FunctionIndex, FunctionBody>>();
let got = wasmer_compiler::types::function::GOT::empty();
Ok(Compilation {
functions: functions.into_iter().collect(),
custom_sections,
function_call_trampolines,
dynamic_function_trampolines,
debug: dwarf,
got: None,
unwind_info,
got,
})
}
}

View File

@ -157,6 +157,25 @@ impl Abi for Aarch64SystemV {
vmctx_attributes(0),
)
}
[t1, t2]
if matches!(t1, Type::I32 | Type::I64 | Type::F32 | Type::F64)
&& matches!(t2, Type::FuncRef | Type::ExternRef | Type::ExceptionRef) =>
{
let t1 = type_to_llvm(intrinsics, *t1).unwrap();
let t2 = type_to_llvm(intrinsics, *t2).unwrap();
(
context
.struct_type(&[t1.as_basic_type_enum(), t2.as_basic_type_enum()], false)
.fn_type(
param_types
.map(|v| v.map(Into::into))
.collect::<Result<Vec<BasicMetadataTypeEnum>, _>>()?
.as_slice(),
false,
),
vmctx_attributes(0),
)
}
_ => {
let sig_returns_bitwidths = sig
.results()

View File

@ -11,8 +11,9 @@ use rayon::iter::ParallelBridge;
use rayon::prelude::{IntoParallelIterator, IntoParallelRefIterator, ParallelIterator};
use std::collections::HashSet;
use std::sync::Arc;
use wasmer_compiler::types::function::{Compilation, Dwarf};
use wasmer_compiler::types::function::{Compilation, UnwindInfo};
use wasmer_compiler::types::module::CompileModuleInfo;
use wasmer_compiler::types::relocation::RelocationKind;
use wasmer_compiler::{
types::{
relocation::RelocationTarget,
@ -24,9 +25,7 @@ use wasmer_compiler::{
};
use wasmer_types::entity::{EntityRef, PrimaryMap};
use wasmer_types::{CompileError, FunctionIndex, LocalFunctionIndex, SignatureIndex};
use wasmer_vm::libcalls::function_pointer;
//use std::sync::Mutex;
use wasmer_vm::LibCall;
/// A compiler that compiles a WebAssembly module with LLVM, translating the Wasm to LLVM IR,
/// optimizing it and then translating to assembly.
@ -265,7 +264,8 @@ impl Compiler for LLVMCompiler {
let mut compact_unwind_section_bytes = vec![];
let mut compact_unwind_section_relocations = vec![];
let mut got_targets = HashSet::new();
let mut got_targets: HashSet<wasmer_compiler::types::relocation::RelocationTarget> =
HashSet::from_iter(vec![RelocationTarget::LibCall(LibCall::EHPersonality)]);
let functions = function_body_inputs
.iter()
@ -305,22 +305,12 @@ impl Compiler for LLVMCompiler {
SectionIndex::from_u32(first_section + index.as_u32()),
)
}
if reloc.kind.needs_got() {
got_targets.insert(reloc.reloc_target);
}
}
compiled_function
.compiled_function
.relocations
.iter()
.filter(|v| v.kind.needs_got())
.for_each(|v| _ = got_targets.insert(v.reloc_target));
compiled_function
.custom_sections
.iter()
.flat_map(|v| v.1.relocations.iter())
.filter(|v| v.kind.needs_got())
.for_each(|v| _ = got_targets.insert(v.reloc_target));
if compiled_function
.eh_frame_section_indices
.contains(&section_index)
@ -364,13 +354,19 @@ impl Compiler for LLVMCompiler {
SectionIndex::from_u32(first_section + index.as_u32()),
)
}
if reloc.kind.needs_got() {
got_targets.insert(reloc.reloc_target);
}
}
compiled_function.compiled_function
})
.collect::<PrimaryMap<LocalFunctionIndex, _>>();
let debug = if !eh_frame_section_bytes.is_empty() {
let debug = Dwarf::new(SectionIndex::from_u32(module_custom_sections.len() as u32));
let mut unwind_info = UnwindInfo::default();
if !eh_frame_section_bytes.is_empty() {
let eh_frame_idx = SectionIndex::from_u32(module_custom_sections.len() as u32);
// Do not terminate dwarf info with a zero-length CIE.
// Because more info will be added later
// in lib/object/src/module.rs emit_compilation
@ -379,30 +375,18 @@ impl Compiler for LLVMCompiler {
bytes: SectionBody::new_with_vec(eh_frame_section_bytes),
relocations: eh_frame_section_relocations,
});
unwind_info.eh_frame = Some(eh_frame_idx);
}
Some(debug)
} else {
None
};
//println!("compact_unwind_bytes: {compact_unwind_section_bytes:?}");
let debug = if !compact_unwind_section_bytes.is_empty() {
let section_idx = SectionIndex::from_u32(module_custom_sections.len() as u32);
if !compact_unwind_section_bytes.is_empty() {
let cu_index = SectionIndex::from_u32(module_custom_sections.len() as u32);
module_custom_sections.push(CustomSection {
protection: CustomSectionProtection::Read,
bytes: SectionBody::new_with_vec(compact_unwind_section_bytes),
relocations: compact_unwind_section_relocations,
});
if let Some(mut dbg) = debug {
dbg.compact_unwind = Some(section_idx);
Some(dbg)
} else {
Some(Dwarf::new_cu(section_idx))
}
} else {
debug
};
unwind_info.compact_unwind = Some(cu_index);
}
let function_call_trampolines = module
.signatures
@ -437,29 +421,45 @@ impl Compiler for LLVMCompiler {
.into_iter()
.collect::<PrimaryMap<_, _>>();
let got = if !got_targets.is_empty() {
let mut got_data = vec![];
let mut map = std::collections::HashMap::new();
for (i, reloc) in got_targets.into_iter().enumerate() {
match reloc {
RelocationTarget::LibCall(c) => got_data.push(function_pointer(c)),
RelocationTarget::LocalFunc(_) => todo!(),
RelocationTarget::CustomSection(_) => todo!(),
}
let mut got = wasmer_compiler::types::function::GOT::empty();
map.insert(reloc, i);
if !got_targets.is_empty() {
let pointer_width = target
.triple()
.pointer_width()
.map_err(|_| CompileError::Codegen("Could not get pointer width".to_string()))?;
let got_entry_size = match pointer_width {
target_lexicon::PointerWidth::U64 => 8,
target_lexicon::PointerWidth::U32 => 4,
target_lexicon::PointerWidth::U16 => todo!(),
};
let got_entry_reloc_kind = match pointer_width {
target_lexicon::PointerWidth::U64 => RelocationKind::Abs8,
target_lexicon::PointerWidth::U32 => RelocationKind::Abs4,
target_lexicon::PointerWidth::U16 => todo!(),
};
let got_data: Vec<u8> = vec![0; got_targets.len() * got_entry_size];
let mut got_relocs = vec![];
for (i, target) in got_targets.into_iter().enumerate() {
got_relocs.push(wasmer_compiler::types::relocation::Relocation {
kind: got_entry_reloc_kind,
reloc_target: target,
offset: (i * got_entry_size) as u32,
addend: 0,
});
}
let got_data: Vec<u8> = got_data.into_iter().flat_map(|v| v.to_ne_bytes()).collect();
let index = SectionIndex::from_u32(module_custom_sections.len() as u32);
let got_idx = SectionIndex::from_u32(module_custom_sections.len() as u32);
module_custom_sections.push(CustomSection {
protection: CustomSectionProtection::Read,
bytes: SectionBody::new_with_vec(got_data),
relocations: vec![],
relocations: got_relocs,
});
Some(wasmer_compiler::types::function::GOT { index, map })
} else {
None
got.index = Some(got_idx);
};
tracing::trace!("Finished compling the module!");
@ -468,7 +468,7 @@ impl Compiler for LLVMCompiler {
custom_sections: module_custom_sections,
function_call_trampolines,
dynamic_function_trampolines,
debug,
unwind_info,
got,
})
}

View File

@ -25,6 +25,9 @@ use itertools::Itertools;
use smallvec::SmallVec;
use target_lexicon::BinaryFormat;
static mut COUNTER: std::sync::LazyLock<std::sync::Mutex<usize>> =
std::sync::LazyLock::new(|| std::sync::Mutex::new(0));
use crate::{
abi::{get_abi, Abi},
config::{CompiledKind, LLVM},
@ -50,7 +53,7 @@ use wasmer_vm::{MemoryStyle, TableStyle, VMOffsets};
const FUNCTION_SECTION_ELF: &str = "__TEXT,wasmer_function";
const FUNCTION_SECTION_MACHO: &str = "__TEXT";
const FUNCTION_SEGMENT_MACHO: &str = "__text";
const FUNCTION_SEGMENT_MACHO: &str = "wasmer_function";
pub struct FuncTranslator {
ctx: Context,
@ -328,9 +331,9 @@ impl FuncTranslator {
.unwrap();
// -- Uncomment to enable dumping intermediate LLVM objects
//module
// .print_to_file(format!("{}/obj.ll", std::env!("LLVM_EH_TESTS_DUMP_DIR")))
// .unwrap();
module
.print_to_file(format!("{}/obj.ll", std::env!("LLVM_EH_TESTS_DUMP_DIR")))
.unwrap();
if let Some(ref callbacks) = config.callbacks {
callbacks.postopt_ir(&function, &module);
@ -368,13 +371,22 @@ impl FuncTranslator {
.unwrap();
// -- Uncomment to enable dumping intermediate LLVM objects
//target_machine
// .write_to_file(
// &module,
// FileType::Assembly,
// std::path::Path::new(&format!("{}/obj.asm", std::env!("LLVM_EH_TESTS_DUMP_DIR"))),
// )
// .unwrap();
unsafe {
let mut x = COUNTER.lock().unwrap();
let old = *x;
*x += 1;
target_machine
.write_to_file(
&module,
FileType::Assembly,
std::path::Path::new(&format!(
"{}/obj_{old}.asm",
std::env!("LLVM_EH_TESTS_DUMP_DIR")
)),
)
.unwrap();
}
if let Some(ref callbacks) = config.callbacks {
callbacks.obj_memory_buffer(&function, &memory_buffer);
@ -1451,7 +1463,10 @@ impl<'ctx, 'a> LLVMFunctionCodeGenerator<'ctx, 'a> {
tag_glbl.set_linkage(Linkage::External);
tag_glbl.set_constant(true);
tag_glbl.set_section(Some("__DATA"));
tag_glbl.set_section(Some(&format!(
"{},_wasmer_ehi_{tag}",
FUNCTION_SECTION_MACHO
)));
let tag_glbl = tag_glbl.as_basic_value_enum();

View File

@ -22,7 +22,7 @@ use rayon::prelude::{IntoParallelIterator, ParallelIterator};
use std::sync::Arc;
use wasmer_compiler::{
types::{
function::{Compilation, CompiledFunction, Dwarf, FunctionBody},
function::{Compilation, CompiledFunction, FunctionBody, UnwindInfo},
module::CompileModuleInfo,
section::SectionIndex,
target::{Architecture, CallingConvention, CpuFeature, OperatingSystem, Target},
@ -236,8 +236,11 @@ impl Compiler for SinglepassCompiler {
.into_iter()
.collect::<PrimaryMap<FunctionIndex, FunctionBody>>();
#[allow(unused_mut)]
let mut unwind_info = UnwindInfo::default();
#[cfg(feature = "unwind")]
let dwarf = if let Some((mut dwarf_frametable, cie_id)) = dwarf_frametable {
if let Some((mut dwarf_frametable, cie_id)) = dwarf_frametable {
for fde in fdes.into_iter().flatten() {
match fde {
UnwindFrame::SystemV(fde) => dwarf_frametable.add_fde(cie_id, fde),
@ -248,20 +251,18 @@ impl Compiler for SinglepassCompiler {
let eh_frame_section = eh_frame.0.into_section();
custom_sections.push(eh_frame_section);
Some(Dwarf::new(SectionIndex::new(custom_sections.len() - 1)))
} else {
None
unwind_info.eh_frame = Some(SectionIndex::new(custom_sections.len() - 1))
};
#[cfg(not(feature = "unwind"))]
let dwarf = None;
let got = wasmer_compiler::types::function::GOT::empty();
Ok(Compilation {
functions: functions.into_iter().collect(),
custom_sections,
function_call_trampolines,
dynamic_function_trampolines,
debug: dwarf,
got: None,
unwind_info,
got,
})
}

View File

@ -15,7 +15,7 @@ use crate::{
SerializableModule,
},
types::{
function::{CompiledFunctionFrameInfo, Dwarf, FunctionBody, GOT},
function::{CompiledFunctionFrameInfo, FunctionBody, UnwindInfo, GOT},
module::CompileModuleInfo,
relocation::Relocation,
section::{CustomSection, SectionIndex},
@ -25,7 +25,7 @@ use crate::{
};
use core::mem::MaybeUninit;
use enumset::EnumSet;
use rkyv::{option::ArchivedOption, rancor::Error as RkyvError};
use rkyv::rancor::Error as RkyvError;
use self_cell::self_cell;
use shared_buffer::OwnedBuffer;
use std::sync::Arc;
@ -140,7 +140,7 @@ impl ArtifactBuild {
dynamic_function_trampolines: compilation.dynamic_function_trampolines,
custom_sections,
custom_section_relocations,
debug: compilation.debug,
unwind_info: compilation.unwind_info,
libcall_trampolines,
libcall_trampoline_len,
got: compilation.got,
@ -199,14 +199,14 @@ impl ArtifactBuild {
self.serializable.compilation.libcall_trampoline_len as usize
}
/// Get Debug optional Dwarf ref
pub fn get_debug_ref(&self) -> Option<&Dwarf> {
self.serializable.compilation.debug.as_ref()
/// Get a reference to the [`UnwindInfo`].
pub fn get_unwind_info(&self) -> &UnwindInfo {
&self.serializable.compilation.unwind_info
}
/// Get a reference to the [`GOT`], if available.
pub fn get_got_ref(&self) -> Option<&GOT> {
self.serializable.compilation.got.as_ref()
/// Get a reference to the [`GOT`].
pub fn get_got_ref(&self) -> &GOT {
&self.serializable.compilation.got
}
/// Get Function Relocations ref
@ -419,24 +419,18 @@ impl ArtifactBuildFromArchive {
.to_native() as usize
}
/// Get Debug optional Dwarf ref
pub fn get_debug_ref(&self) -> Option<Dwarf> {
match self.cell.borrow_dependent().compilation.debug {
ArchivedOption::Some(ref x) => {
Some(rkyv::deserialize::<_, rkyv::rancor::Error>(x).unwrap())
}
ArchivedOption::None => None,
}
/// Get an unarchived [`UnwindInfo`].
pub fn get_unwind_info(&self) -> UnwindInfo {
rkyv::deserialize::<_, rkyv::rancor::Error>(
&self.cell.borrow_dependent().compilation.unwind_info,
)
.unwrap()
}
/// Get a reference to the [`GOT`], if available.
pub fn get_got_ref(&self) -> Option<GOT> {
match self.cell.borrow_dependent().compilation.got {
ArchivedOption::Some(ref x) => {
Some(rkyv::deserialize::<_, rkyv::rancor::Error>(x).unwrap())
}
ArchivedOption::None => None,
}
/// Get an unarchived [`GOT`].
pub fn get_got_ref(&self) -> GOT {
rkyv::deserialize::<_, rkyv::rancor::Error>(&self.cell.borrow_dependent().compilation.got)
.unwrap()
}
/// Get Function Relocations ref

View File

@ -12,7 +12,7 @@ use crate::{
register_frame_info, resolve_imports,
serialize::{MetadataHeader, SerializableModule},
types::{
function::GOT,
relocation::{RelocationLike, RelocationTarget},
section::CustomSectionLike,
target::{CpuFeature, Target},
},
@ -329,24 +329,40 @@ impl Artifact {
)?,
};
let got_info: Option<(usize, GOT)> = match &artifact {
let get_got_address: Box<dyn Fn(RelocationTarget) -> Option<usize>> = match &artifact {
ArtifactBuildVariant::Plain(ref p) => {
if let Some(got) = p.get_got_ref() {
let got = got.clone();
let body = p.get_custom_sections_ref()[got.index].bytes.as_ptr() as usize;
Some((body, got))
if let Some(got) = p.get_got_ref().index {
let relocs: Vec<_> = p.get_custom_section_relocations_ref()[got]
.iter()
.map(|v| (v.reloc_target, v.offset))
.collect();
let got_base = custom_sections[got].0 as usize;
Box::new(move |t: RelocationTarget| {
relocs
.iter()
.find(|(v, _)| v == &t)
.map(|(_, o)| got_base + (*o as usize))
})
} else {
None
Box::new(|_: RelocationTarget| None)
}
}
ArtifactBuildVariant::Archived(ref p) => {
if let Some(got) = p.get_got_ref() {
let got = got.clone();
let body = p.get_custom_sections_ref()[got.index].bytes().as_ptr() as usize;
Some((body, got))
if let Some(got) = p.get_got_ref().index {
let relocs: Vec<_> = p.get_custom_section_relocations_ref()[got]
.iter()
.map(|v| (v.reloc_target(), v.offset))
.collect();
let got_base = custom_sections[got].0 as usize;
Box::new(move |t: RelocationTarget| {
relocs
.iter()
.find(|(v, _)| v == &t)
.map(|(_, o)| got_base + (o.to_native() as usize))
})
} else {
None
Box::new(|_: RelocationTarget| None)
}
}
};
@ -364,7 +380,7 @@ impl Artifact {
.map(|(k, v)| (k, v.iter())),
p.get_libcall_trampolines(),
p.get_libcall_trampoline_len(),
got_info,
&get_got_address,
),
ArtifactBuildVariant::Archived(a) => link_module(
module_info,
@ -378,7 +394,7 @@ impl Artifact {
.map(|(k, v)| (k, v.iter())),
a.get_libcall_trampolines(),
a.get_libcall_trampoline_len(),
got_info,
&get_got_address,
),
};
@ -392,52 +408,77 @@ impl Artifact {
.collect::<PrimaryMap<_, _>>()
};
let debug_ref = match &artifact {
// Why clone? See comment at the top of ./lib/types/src/indexes.rs.
ArtifactBuildVariant::Plain(p) => p.get_debug_ref().cloned(),
ArtifactBuildVariant::Archived(a) => a.get_debug_ref(),
let eh_frame = match &artifact {
ArtifactBuildVariant::Plain(p) => p
.get_unwind_info()
.eh_frame
.map(|v| p.get_custom_sections_ref()[v].bytes.as_slice()),
ArtifactBuildVariant::Archived(a) => a
.get_unwind_info()
.eh_frame
.map(|v| a.get_custom_sections_ref()[v].bytes()),
};
let eh_frame = match debug_ref.as_ref().and_then(|v| v.eh_frame) {
Some(eh_frame) => {
let eh_frame_section_size = match &artifact {
ArtifactBuildVariant::Plain(p) => {
p.get_custom_sections_ref()[eh_frame].bytes.len()
}
ArtifactBuildVariant::Archived(a) => {
a.get_custom_sections_ref()[eh_frame].bytes.len()
}
};
let eh_frame_section_pointer = custom_sections[eh_frame];
Some(unsafe {
std::slice::from_raw_parts(*eh_frame_section_pointer, eh_frame_section_size)
let compact_unwind = match &artifact {
ArtifactBuildVariant::Plain(p) => p.get_unwind_info().compact_unwind.map(|v| unsafe {
std::slice::from_raw_parts(
custom_sections[v].0,
p.get_custom_sections_ref()[v].bytes.len(),
)
}),
ArtifactBuildVariant::Archived(a) => {
a.get_unwind_info().compact_unwind.map(|v| unsafe {
std::slice::from_raw_parts(
custom_sections[v].0,
a.get_custom_sections_ref()[v].bytes.len(),
)
})
}
None => None,
};
let compact_unwind = match debug_ref.and_then(|v| v.compact_unwind) {
Some(cu) => {
let cu_section_size = match &artifact {
ArtifactBuildVariant::Plain(p) => p.get_custom_sections_ref()[cu].bytes.len(),
ArtifactBuildVariant::Archived(a) => {
a.get_custom_sections_ref()[cu].bytes.len()
}
};
let cu_section_pointer = custom_sections[cu];
Some((cu_section_pointer, cu_section_size))
}
None => None,
};
//let got_idx = match &artifact {
// ArtifactBuildVariant::Plain(p) => p.get_got_ref().index,
// ArtifactBuildVariant::Archived(a) => a.get_got_ref().index,
//};
//if let Some(idx) = got_idx {
// let data = custom_sections[idx].0;
// let data = data.cast::<*const usize>();
// let len = match &artifact {
// ArtifactBuildVariant::Plain(p) => p.get_custom_sections_ref()[idx].bytes.len(),
// ArtifactBuildVariant::Archived(a) => a.get_custom_sections_ref()[idx].bytes.len(),
// } / size_of::<usize>();
// let relocs: Vec<_> = match &artifact {
// ArtifactBuildVariant::Plain(p) => p.get_custom_section_relocations_ref()[idx]
// .iter()
// .map(|v| v.reloc_target())
// .collect(),
// ArtifactBuildVariant::Archived(a) => a.get_custom_section_relocations_ref()[idx]
// .iter()
// .map(|v| v.reloc_target())
// .collect(),
// };
// let entries = unsafe { std::slice::from_raw_parts(data, len) };
// for (i, entry) in entries.iter().enumerate() {
// println!("entry: {entry:?} => {:?}", relocs[i]);
// }
//};
// This needs to be called before publishind the `eh_frame`.
engine_inner.register_compact_unwind(compact_unwind)?;
engine_inner.register_compact_unwind(
compact_unwind,
get_got_address(RelocationTarget::LibCall(wasmer_vm::LibCall::EHPersonality)),
)?;
// Make all code compiled thus far executable.
engine_inner.publish_compiled_code();
engine_inner.publish_eh_frame(eh_frame)?;
drop(get_got_address);
let finished_function_lengths = finished_functions
.values()
.map(|extent| extent.length)

View File

@ -467,13 +467,14 @@ impl EngineInner {
/// Register macos-specific exception handling information associated with the code.
pub(crate) fn register_compact_unwind(
&mut self,
compact_unwind: Option<(SectionBodyPtr, usize)>,
compact_unwind: Option<&[u8]>,
eh_personality_addr_in_got: Option<usize>,
) -> Result<(), CompileError> {
self.code_memory
.last_mut()
.unwrap()
.unwind_registry_mut()
.register_compact_unwind(compact_unwind)
.register_compact_unwind(compact_unwind, eh_personality_addr_in_got)
.map_err(|e| {
CompileError::Resource(format!("Error while publishing the unwind code: {}", e))
})?;

View File

@ -3,7 +3,6 @@
use crate::{
get_libcall_trampoline,
types::{
function::GOT,
relocation::{RelocationKind, RelocationLike, RelocationTarget},
section::SectionIndex,
},
@ -26,37 +25,43 @@ fn apply_relocation(
libcall_trampolines_sec_idx: SectionIndex,
libcall_trampoline_len: usize,
riscv_pcrel_hi20s: &mut HashMap<usize, u32>,
got_info: &Option<(usize, GOT)>,
get_got_address: &Box<dyn Fn(RelocationTarget) -> Option<usize>>,
) {
let reloc_target = r.reloc_target();
let target_func_address: usize = match reloc_target {
RelocationTarget::LocalFunc(index) => *allocated_functions[index].ptr as usize,
RelocationTarget::LibCall(libcall) => {
// Use the direct target of the libcall if the relocation supports
// a full 64-bit address. Otherwise use a trampoline.
if matches!(
r.kind(),
RelocationKind::Abs8
| RelocationKind::X86PCRel8
| RelocationKind::MachoArm64RelocUnsigned
| RelocationKind::MachoX86_64RelocUnsigned
| RelocationKind::MachoArm64RelocGotLoadPage21
| RelocationKind::MachoArm64RelocGotLoadPageoff12
| RelocationKind::MachoArm64RelocPointerToGot
) {
function_pointer(libcall)
//} else if matches!(r.kind(), RelocationKind::MachoArm64RelocPointerToGot) {
// Box::leak(Box::new(function_pointer(libcall))) as *mut _ as usize
} else {
get_libcall_trampoline(
libcall,
allocated_sections[libcall_trampolines_sec_idx].0 as usize,
libcall_trampoline_len,
)
}
let target_func_address: usize = if r.kind().needs_got() {
if let Some(got_address) = get_got_address(reloc_target) {
//println!("{reloc_target:?}, {:?}", r.kind());
got_address
} else {
panic!("No GOT entry for reloc target {reloc_target:?}")
}
RelocationTarget::CustomSection(custom_section) => {
*allocated_sections[custom_section] as usize
} else {
match reloc_target {
RelocationTarget::LocalFunc(index) => *allocated_functions[index].ptr as usize,
RelocationTarget::LibCall(libcall) => {
// Use the direct target of the libcall if the relocation supports
// a full 64-bit address. Otherwise use a trampoline.
if matches!(
r.kind(),
RelocationKind::Abs8
| RelocationKind::X86PCRel8
| RelocationKind::MachoArm64RelocUnsigned
| RelocationKind::MachoX86_64RelocUnsigned
) {
function_pointer(libcall)
//} else if matches!(r.kind(), RelocationKind::MachoArm64RelocPointerToGot) {
// Box::leak(Box::new(function_pointer(libcall))) as *mut _ as usize
} else {
get_libcall_trampoline(
libcall,
allocated_sections[libcall_trampolines_sec_idx].0 as usize,
libcall_trampoline_len,
)
}
}
RelocationTarget::CustomSection(custom_section) => {
*allocated_sections[custom_section] as usize
}
}
};
@ -178,7 +183,7 @@ fn apply_relocation(
| read_unaligned((reloc_address + 4) as *mut u32);
write_unaligned((reloc_address + 4) as *mut u32, reloc_delta2);
},
RelocationKind::Aarch64AdrPrelPgHi21 | RelocationKind::MachoArm64RelocGotLoadPage21 => unsafe {
RelocationKind::Aarch64AdrPrelPgHi21 => unsafe {
let (reloc_address, delta) = r.for_address(body, target_func_address as u64);
let delta = delta as isize;
@ -236,22 +241,43 @@ fn apply_relocation(
| (read_unaligned(reloc_address as *mut u32) & 0xFFC003FF);
write_unaligned(reloc_address as *mut u32, reloc_delta);
},
RelocationKind::MachoArm64RelocGotLoadPageoff12 => unsafe {
// Hacky: we replace the `ldr` instruction with an `add` instruction, as we already
// know the absolute address of a function, and we don't need any GOT.
let (reloc_address, reloc_delta) = r.for_address(body, target_func_address as u64);
let ldr = read_unaligned(reloc_address as *mut u32);
let xn = ldr & 0xFFFFF;
let new_op = 0x91000000 | (reloc_delta as u32) << 10 | (xn << 5) | xn;
write_unaligned(reloc_address as *mut u32, new_op);
},
RelocationKind::MachoArm64RelocSubtractor | RelocationKind::MachoX86_64RelocSubtractor => unsafe {
let (reloc_address, reloc_sub) = r.for_address(body, target_func_address as u64);
macho_aarch64_subtractor_addresses.insert(reloc_address);
write_unaligned(reloc_address as *mut u64, reloc_sub);
},
RelocationKind::MachoArm64RelocGotLoadPage21
| RelocationKind::MachoArm64RelocPage21
| RelocationKind::MachoArm64RelocTlvpLoadPage21 => unsafe {
let (reloc_address, _) = r.for_address(body, target_func_address as u64);
let target_func_page = target_func_address & !0xfff;
let reloc_at_page = reloc_address & !0xfff;
let pcrel = (target_func_page as isize)
.checked_sub(reloc_at_page as isize)
.unwrap();
assert!(
(-1 << 32) <= (pcrel as i64) && (pcrel as i64) < (1 << 32),
"can't reach GOT page with ±4GB `adrp` instruction"
);
let val = pcrel >> 12;
let immlo = ((val as u32) & 0b11) << 29;
let immhi = (((val as u32) >> 2) & &0x7ffff) << 5;
let mask = !((0x7ffff << 5) | (0b11 << 29));
let op = read_unaligned(reloc_address as *mut u32);
write_unaligned(reloc_address as *mut u32, (op & mask) | immlo | immhi);
},
RelocationKind::MachoArm64RelocGotLoadPageoff12
| RelocationKind::MachoArm64RelocPageoff12 => unsafe {
let (reloc_address, _) = r.for_address(body, target_func_address as u64);
assert_eq!(target_func_address & 0b111, 0);
let val = target_func_address >> 3;
let imm9 = ((val & 0x1ff) << 10) as u32;
let mask = !(0x1ff << 10);
let op = read_unaligned(reloc_address as *mut u32);
write_unaligned(reloc_address as *mut u32, (op & mask) | imm9);
},
RelocationKind::MachoArm64RelocUnsigned | RelocationKind::MachoX86_64RelocUnsigned => unsafe {
let (reloc_address, mut reloc_delta) = r.for_address(body, target_func_address as u64);
@ -263,22 +289,12 @@ fn apply_relocation(
},
RelocationKind::MachoArm64RelocPointerToGot => unsafe {
if let Some(got) = got_info {
let base = got.0;
let base = base as *const usize;
//let (reloc_address, what) = r.for_address(body, target_func_address as u64);
//write_unaligned(reloc_address as *mut i32, what as i32);
if let Some(reloc_idx) = got.1.map.get(&reloc_target) {
let got_address = base.wrapping_add(*reloc_idx);
let (reloc_address, _reloc_delta) = r.for_address(body, got_address as u64);
write_unaligned(reloc_address as *mut u64, got_address as u64);
} else {
panic!("Missing GOT info for reloc target {reloc_target:?}");
}
} else {
panic!("Missing GOT info for reloc target {reloc_target:?}");
}
let at = body + r.offset() as usize;
let pcrel = i32::try_from((target_func_address as isize) - (at as isize)).unwrap();
write_unaligned(at as *mut i32, pcrel);
},
kind => panic!(
"Relocation kind unsupported in the current architecture {}",
@ -308,7 +324,7 @@ pub fn link_module<'a>(
>,
libcall_trampolines: SectionIndex,
trampoline_len: usize,
got_info: Option<(usize, GOT)>,
get_got_address: &'a Box<dyn Fn(RelocationTarget) -> Option<usize>>,
) {
let mut riscv_pcrel_hi20s: HashMap<usize, u32> = HashMap::new();
@ -323,7 +339,7 @@ pub fn link_module<'a>(
libcall_trampolines,
trampoline_len,
&mut riscv_pcrel_hi20s,
&got_info,
&get_got_address,
);
}
}
@ -338,7 +354,7 @@ pub fn link_module<'a>(
libcall_trampolines,
trampoline_len,
&mut riscv_pcrel_hi20s,
&got_info,
&get_got_address,
);
}
}

View File

@ -128,6 +128,7 @@ impl UnwindRegistry {
self.compact_unwind_mgr
.finalize()
.map_err(|v| v.to_string())?;
self.compact_unwind_mgr.register();
}
self.published = true;
@ -154,37 +155,49 @@ impl UnwindRegistry {
__register_frame(ptr);
self.registrations.push(ptr as usize);
} else {
// For libunwind, `__register_frame` takes a pointer to a single FDE
let start = eh_frame.as_ptr();
let end = start.add(eh_frame.len());
let mut current = start;
#[cfg(all(target_os = "macos", target_arch = "aarch64"))]
{
compact_unwind::__unw_add_dynamic_eh_frame_section(eh_frame.as_ptr() as usize);
}
// Walk all of the entries in the frame table and register them
while current < end {
let len = std::ptr::read::<u32>(current as *const u32) as usize;
#[cfg(not(all(target_os = "macos", target_arch = "aarch64")))]
{
// For libunwind, `__register_frame` takes a pointer to a single FDE
let start = eh_frame.as_ptr();
let end = start.add(eh_frame.len());
let mut current = start;
// Skip over the CIE and zero-length FDEs.
// LLVM's libunwind emits a warning on zero-length FDEs.
if current != start && len != 0 {
__register_frame(current);
self.registrations.push(current as usize);
// Walk all of the entries in the frame table and register them
while current < end {
let len = std::ptr::read::<u32>(current as *const u32) as usize;
// Skip over the CIE and zero-length FDEs.
// LLVM's libunwind emits a warning on zero-length FDEs.
if current != start && len != 0 {
__register_frame(current);
self.registrations.push(current as usize);
}
// Move to the next table entry (+4 because the length itself is not inclusive)
current = current.add(len + 4);
}
// Move to the next table entry (+4 because the length itself is not inclusive)
current = current.add(len + 4);
}
}
}
pub(crate) fn register_compact_unwind(
&mut self,
compact_unwind: Option<(wasmer_vm::SectionBodyPtr, usize)>,
compact_unwind: Option<&[u8]>,
eh_personality_addr_in_got: Option<usize>,
) -> Result<(), String> {
#[cfg(all(target_os = "macos", target_arch = "aarch64"))]
unsafe {
if let Some((ptr, len)) = compact_unwind {
self.compact_unwind_mgr
.read_compact_unwind_section(ptr.0, len)?;
if let Some(slice) = compact_unwind {
self.compact_unwind_mgr.read_compact_unwind_section(
slice.as_ptr() as _,
slice.len(),
eh_personality_addr_in_got,
)?;
}
}
@ -209,7 +222,16 @@ impl Drop for UnwindRegistry {
// To ensure that we just pop off the first element in the list upon every
// deregistration, walk our list of registrations backwards.
for fde in self.registrations.iter().rev() {
__deregister_frame(*fde as *const _);
#[cfg(all(target_os = "macos", target_arch = "aarch64"))]
{
compact_unwind::__unw_remove_dynamic_eh_frame_section(*fde);
self.compact_unwind_mgr.deregister();
}
#[cfg(not(all(target_os = "macos", target_arch = "aarch64")))]
{
__deregister_frame(*fde as *const _);
}
}
}
}

View File

@ -1,10 +1,14 @@
/// Data types and functions to read and represent entries in the `__compact_unwind` section.
mod cu_entry;
use core::ops::Range;
pub(crate) use cu_entry::CompactUnwindEntry;
use std::sync::OnceLock;
use std::{
collections::HashMap,
sync::{LazyLock, Mutex},
};
use wasmer_types::CompileError;
type CUResult<T> = Result<T, CompileError>;
#[repr(C)]
@ -28,11 +32,11 @@ type CUResult<T> = Result<T, CompileError>;
/// should also be set to zero).
#[derive(Debug)]
pub struct UnwDynamicUnwindSections {
dso_base: usize,
dwarf_section: usize,
dwarf_section_length: usize,
compact_unwind_section: usize,
compact_unwind_section_length: usize,
dso_base: u64,
dwarf_section: u64,
dwarf_section_length: u64,
compact_unwind_section: u64,
compact_unwind_section_length: u64,
}
// Typedef for unwind-info lookup callbacks. Functions of this type can be
@ -68,71 +72,120 @@ extern "C" {
// Returns UNW_ESUCCESS for successful deregistrations. If the given callback
// has already been registered then UNW_EINVAL will be returned.
pub fn __unw_remove_find_dynamic_unwind_sections(
find_dynamic_unwind_sections: &UnwDynamicUnwindSections,
find_dynamic_unwind_sections: UnwFindDynamicUnwindSections,
) -> u32;
pub fn __unw_add_dynamic_eh_frame_section(eh_frame_start: usize);
pub fn __unw_remove_dynamic_eh_frame_section(eh_frame_start: usize);
}
struct ByteWriter {
start: *mut u8,
ptr: *mut u8,
max: usize,
trait ToBytes {
fn to_bytes(&self) -> Vec<u8>;
}
impl ByteWriter {
pub fn new(ptr: *mut u8, max: usize) -> Self {
Self {
start: ptr.clone(),
ptr,
max,
}
impl ToBytes for u32 {
fn to_bytes(&self) -> Vec<u8> {
self.to_ne_bytes().into()
}
}
pub fn write<T: Copy>(&mut self, v: T) -> CUResult<()> {
unsafe {
let next_ptr = self.ptr.byte_add(size_of::<T>());
if next_ptr as usize >= self.max {
return Err(CompileError::Codegen(
"trying to write out of memory bounds while generating unwind info".into(),
));
}
core::ptr::write_unaligned(std::mem::transmute::<*mut u8, *mut T>(self.ptr), v);
self.ptr = next_ptr;
Ok(())
}
}
pub fn offset(&self) -> usize {
self.ptr as usize - self.start as usize
impl ToBytes for u16 {
fn to_bytes(&self) -> Vec<u8> {
self.to_ne_bytes().into()
}
}
#[derive(Debug)]
pub struct CompactUnwindManager {
unwind_info_section: usize,
unwind_info_section_len: usize,
unwind_info_section: Vec<u8>,
compact_unwind_entries: Vec<CompactUnwindEntry>,
num_second_level_pages: usize,
num_lsdas: usize,
personalities: Vec<usize>,
dso_base: usize,
maybe_eh_personality_addr_in_got: Option<usize>,
}
impl Default for CompactUnwindManager {
fn default() -> Self {
Self {
unwind_info_section: 0,
unwind_info_section_len: 0,
unwind_info_section: Vec::new(),
compact_unwind_entries: Default::default(),
num_second_level_pages: Default::default(),
num_lsdas: Default::default(),
personalities: Default::default(),
dso_base: 0,
maybe_eh_personality_addr_in_got: None,
}
}
}
static UNWIND_INFO_SECTION_PTR: OnceLock<(usize, usize)> = OnceLock::new();
static mut UNWIND_INFO: LazyLock<Mutex<Option<UnwindInfo>>> = LazyLock::new(|| Mutex::new(None));
type UnwindInfo = HashMap<Range<usize>, UnwindInfoEntry>;
#[derive(Debug)]
struct UnwindInfoEntry {
dso_base: usize,
section_ptr: usize,
section_len: usize,
}
unsafe extern "C" fn find_dynamic_unwind_sections(
addr: usize,
info: *mut UnwDynamicUnwindSections,
) -> u32 {
unsafe {
if let Some(uw_info) = UNWIND_INFO.try_lock().ok() {
if uw_info.is_none() {
(*info).compact_unwind_section = 0;
(*info).compact_unwind_section_length = 0;
(*info).dwarf_section = 0;
(*info).dwarf_section_length = 0;
(*info).dso_base = 0;
return 0;
}
let uw_info = uw_info.as_ref().unwrap();
//println!(
// "looking for addr: {:p}, cu_section at: {:p}",
// addr as *const u8, *compact_unwind_section as *const u8
//);
//eprintln!(
// "looking for addr: {addr:x}, uw_info: {:#x?}, personality addr: {:?}",
// uw_info,
// wasmer_vm::libcalls::wasmer_eh_personality as *const u8
//);
for (range, u) in uw_info.iter() {
//eprintln!("one of our ranges contains addr: {:x}", addr);
if range.contains(&addr) {
(*info).compact_unwind_section = u.section_ptr as _;
(*info).compact_unwind_section_length = u.section_len as _;
(*info).dwarf_section = 0;
(*info).dwarf_section_length = 0;
(*info).dso_base = u.dso_base as u64;
//eprintln!("*info: {:x?}", *info);
return 1;
}
}
}
}
(*info).compact_unwind_section = 0;
(*info).compact_unwind_section_length = 0;
(*info).dwarf_section = 0;
(*info).dwarf_section_length = 0;
(*info).dso_base = 0;
return 0;
}
impl CompactUnwindManager {
const UNWIND_SECTION_VERSION: u32 = 1;
@ -153,85 +206,137 @@ impl CompactUnwindManager {
&mut self,
compact_unwind_section_ptr: *const u8,
len: usize,
eh_personality_addr_in_got: Option<usize>,
) -> Result<(), String> {
//println!("Reading compact_unwind sections; this is {:#?}", self);
//println!("uwinfo: {UNWIND_INFO:#x?}");
if eh_personality_addr_in_got.is_none() {
return Err(
"Cannot register compact_unwind entries without a personality function!".into(),
);
}
let mut offset = 0;
while offset < len {
let entry = CompactUnwindEntry::from_ptr_and_len(
compact_unwind_section_ptr.wrapping_add(offset),
len,
);
//eprintln!("Recording entry {entry:?}");
self.compact_unwind_entries.push(entry);
offset += size_of::<CompactUnwindEntry>();
}
self.maybe_eh_personality_addr_in_got = eh_personality_addr_in_got;
//println!("Read compact unwind sections; this is {:#?}", self);
//println!("uwinfo: {UNWIND_INFO:#x?}");
Ok(())
}
/// Create the `__unwind_info` section from a list of `__compact_unwind` entries.
pub fn finalize(&mut self) -> CUResult<()> {
self.process_and_reserve_uw_info()?;
// At this point, users will have registered the relocated `__compact_unwind` entries. We
// can re-analyse the entries applying the modifications we need to operate, now that we
// know the actual addresses.
self.process_compact_unwind_entries()?;
//println!("My entries are: {:#?}", self.compact_unwind_entries);
self.merge_records();
if self.compact_unwind_entries.is_empty() {
return Ok(());
}
let mut info = libc::Dl_info {
dli_fname: core::ptr::null(),
dli_fbase: core::ptr::null_mut(),
dli_sname: core::ptr::null(),
dli_saddr: core::ptr::null_mut(),
};
unsafe {
/* xxx: Must find a better way to find a dso_base */
_ = libc::dladdr(
self.compact_unwind_entries
.first()
.unwrap()
.personality_addr as *const _,
&mut info as *mut _,
);
if info.dli_fbase.is_null() {
_ = libc::dladdr(
wasmer_vm::libcalls::wasmer_eh_personality as *const _,
&mut info as *mut _,
);
}
}
self.dso_base = info.dli_fbase as usize;
unsafe {
self.write_unwind_info()?;
}
let uw_ptr = self.unwind_info_section;
let uw_len = self.unwind_info_section_len;
let ranges: Vec<Range<usize>> = self
.compact_unwind_entries
.iter()
.map(|v| v.function_addr..v.function_addr + (v.length as usize))
.collect();
UNWIND_INFO_SECTION_PTR.get_or_init(|| (uw_ptr, uw_len));
unsafe extern "C" fn x(_addr: usize, info: *mut UnwDynamicUnwindSections) -> u32 {
if let Some((compact_unwind_section, compact_unwind_section_length)) =
UNWIND_INFO_SECTION_PTR.get()
{
(*info).compact_unwind_section = *compact_unwind_section;
(*info).compact_unwind_section_length = *compact_unwind_section_length;
return 1;
}
0
}
let data: &'static mut [u8] = self.unwind_info_section.clone().leak();
//println!("data: {data:#x?}");
let section_ptr = data.as_ptr() as usize;
//println!("generated UW pointer at: 0x{section_ptr:x}");
let section_len = data.len();
let dso_base = self.dso_base;
unsafe {
let data = std::slice::from_raw_parts(
self.unwind_info_section as *const u8,
self.unwind_info_section_len,
);
match macho_unwind_info::UnwindInfo::parse(data) {
Ok(r) => {
let mut fns = r.functions();
while let Ok(Some(f)) = fns.next() {
println!("func: {f:?}");
//println!("About to finalize; this is {:#?}", self);
//println!("uwinfo: {UNWIND_INFO:#x?}");
let mut uw_info = UNWIND_INFO.lock().map_err(|_| {
CompileError::Codegen("Failed to acquire lock for UnwindInfo!".into())
})?;
match uw_info.as_mut() {
Some(r) => {
for range in ranges {
r.insert(
range,
UnwindInfoEntry {
dso_base,
section_ptr,
section_len,
},
);
}
}
Err(e) => println!("error: {e}"),
None => {
let mut map = HashMap::new();
for range in ranges {
map.insert(
range,
UnwindInfoEntry {
dso_base,
section_ptr,
section_len,
},
);
}
_ = uw_info.insert(map);
}
}
//println!("Finalized; this is {:#?}", self);
//println!("uwinfo: {UNWIND_INFO:#x?}");
}
unsafe {
__unw_add_find_dynamic_unwind_sections(x);
}
Ok(())
}
fn process_and_reserve_uw_info(&mut self) -> CUResult<()> {
self.process_compact_unwind_entries()?;
self.unwind_info_section_len = Self::UNWIND_INFO_SECTION_HEADER_SIZE
+ (self.personalities.len() * Self::PERSONALITY_ENTRY_SIZE)
+ ((self.num_second_level_pages + 1) * Self::INDEX_ENTRY_SIZE)
+ (self.num_lsdas * Self::LSDA_ENTRY_SIZE)
+ (self.num_second_level_pages * Self::SECOND_LEVEL_PAGE_HEADER_SIZE)
+ (size_of::<CompactUnwindEntry>() * Self::SECOND_LEVEL_PAGE_ENTRY_SIZE);
self.unwind_info_section =
vec![0; self.unwind_info_section_len].leak().as_mut_ptr() as usize;
//UNWIND_INFO.get_or_init(move || Mutex::new(uw_info));
Ok(())
}
fn process_compact_unwind_entries(&mut self) -> CUResult<()> {
//eprintln!("Processing entries...");
for entry in self.compact_unwind_entries.iter_mut() {
if entry.personality_addr != 0 {
let p_idx: u32 = if let Some(p_idx) = self
@ -253,35 +358,43 @@ impl CompactUnwindManager {
}
}
self.num_second_level_pages =
(self.compact_unwind_entries.len() + Self::NUM_RECORDS_PER_SECOND_LEVEL_PAGE - 1)
/ Self::NUM_RECORDS_PER_SECOND_LEVEL_PAGE;
self.compact_unwind_entries
.sort_by(|l, r| l.function_addr.cmp(&r.function_addr));
self.num_second_level_pages =
(size_of::<CompactUnwindEntry>() + Self::NUM_RECORDS_PER_SECOND_LEVEL_PAGE - 1)
/ Self::NUM_RECORDS_PER_SECOND_LEVEL_PAGE;
let unwind_info_section_len = Self::UNWIND_INFO_SECTION_HEADER_SIZE
+ (self.personalities.len() * Self::PERSONALITY_ENTRY_SIZE)
+ ((self.num_second_level_pages + 1) * Self::INDEX_ENTRY_SIZE)
+ (self.num_lsdas * Self::LSDA_ENTRY_SIZE)
+ (self.num_second_level_pages * Self::SECOND_LEVEL_PAGE_HEADER_SIZE)
+ (self.compact_unwind_entries.len() * Self::SECOND_LEVEL_PAGE_ENTRY_SIZE);
self.unwind_info_section = Vec::with_capacity(unwind_info_section_len);
Ok(())
}
unsafe fn write_unwind_info(&mut self) -> CUResult<()> {
self.merge_records();
let mut writer = ByteWriter::new(
self.unwind_info_section as *mut u8,
(self.unwind_info_section as *mut u8).byte_add(self.unwind_info_section_len) as usize,
);
self.write_header(&mut writer)?;
self.write_personalities(&mut writer)?;
self.write_indexes(&mut writer)?;
self.write_lsdas(&mut writer)?;
self.write_second_level_pages(&mut writer)?;
// let mut writer = ByteWriter::new(
// self.unwind_info_section as *mut u8,
// (self.unwind_info_section as *mut u8).byte_add(self.unwind_info_section_len) as usize,
// );
//
self.write_header()?;
self.write_personalities()?;
self.write_indices()?;
self.write_lsdas()?;
self.write_second_level_pages()?;
Ok(())
}
fn merge_records(&mut self) {
if self.compact_unwind_entries.len() <= 1 {
self.num_second_level_pages = 1;
return;
}
@ -299,135 +412,175 @@ impl CompactUnwindManager {
}
self.num_second_level_pages =
(size_of::<CompactUnwindEntry>() + Self::NUM_RECORDS_PER_SECOND_LEVEL_PAGE - 1)
(self.compact_unwind_entries.len() + Self::NUM_RECORDS_PER_SECOND_LEVEL_PAGE - 1)
/ Self::NUM_RECORDS_PER_SECOND_LEVEL_PAGE;
}
unsafe fn write_header(&self, writer: &mut ByteWriter) -> CUResult<()> {
// struct unwind_info_section_header
// {
// uint32_t version;
// uint32_t commonEncodingsArraySectionOffset;
// uint32_t commonEncodingsArrayCount;
// uint32_t personalityArraySectionOffset;
// uint32_t personalityArrayCount;
// uint32_t indexSectionOffset;
// uint32_t indexCount;
// // compact_unwind_encoding_t[] <-- We don't use it
// // uint32_t personalities[]
// // unwind_info_section_header_index_entry[]
// // unwind_info_section_header_lsda_index_entry[]
// };
#[inline(always)]
fn write<'a, T: ToBytes>(&'a mut self, value: T) -> CUResult<()> {
let bytes = value.to_bytes();
let capacity = self.unwind_info_section.capacity();
let len = self.unwind_info_section.len();
let num_personalities = self.personalities.len() as u32;
let index_section_offset: u32 = (CompactUnwindManager::UNWIND_INFO_SECTION_HEADER_SIZE
+ self.personalities.len() * CompactUnwindManager::PERSONALITY_ENTRY_SIZE)
as u32;
if len + bytes.len() > capacity {
return Err(CompileError::Codegen(
"writing the unwind_info after the allocated bytes".into(),
));
}
let index_count: u32 = ((size_of::<CompactUnwindEntry>()
+ CompactUnwindManager::NUM_RECORDS_PER_SECOND_LEVEL_PAGE
- 1)
/ CompactUnwindManager::NUM_RECORDS_PER_SECOND_LEVEL_PAGE)
as u32;
// The unwind section version.
writer.write::<u32>(CompactUnwindManager::UNWIND_SECTION_VERSION)?;
// The offset from the base pointer at which the `commonEncodingsArraySection` can be found. We don't use it,
// therefore...
writer.write::<u32>(CompactUnwindManager::UNWIND_INFO_SECTION_HEADER_SIZE as u32)?;
// Its size is zero.
writer.write(0u32)?;
// The offset from the base pointer at which the `personalityArraySection` can be found. It is right after the
// header.
writer.write::<u32>(CompactUnwindManager::UNWIND_INFO_SECTION_HEADER_SIZE as u32)?;
// Its size corresponds to the number of personality functions we've seen. Should,
// in fact, be 0 or 1.
writer.write::<u32>(num_personalities)?;
// The offset from the base pointer at which the `indexSection` can be found. It is right after the
// header.
writer.write::<u32>(index_section_offset)?;
writer.write::<u32>(index_count + 1)?;
Ok(())
}
fn write_personalities(&self, writer: &mut ByteWriter) -> CUResult<()> {
let base = self.unwind_info_section;
for p in self.personalities.iter() {
let delta = (p.wrapping_sub(base)) as u32;
writer.write(delta)?;
for byte in bytes {
self.unwind_info_section.push(byte);
}
Ok(())
}
fn write_indexes(&self, writer: &mut ByteWriter) -> CUResult<()> {
let section_offset_to_lsdas: usize =
writer.offset() + ((self.num_second_level_pages + 1) * Self::INDEX_ENTRY_SIZE);
unsafe fn write_header(&mut self) -> CUResult<()> {
//#[derive(Debug, Default)]
//#[repr(C)]
//#[allow(non_snake_case, non_camel_case_types)]
//struct unwind_info_section_header {
// pub version: u32,
// pub commonEncodingsArraySectionOffset: u32,
// pub commonEncodingsArrayCount: u32,
// pub personalityArraySectionOffset: u32,
// pub personalityArrayCount: u32,
// pub indexSectionOffset: u32,
// pub indexCount: u32,
// // compact_unwind_encoding_t[] <-- We don't use it;;
// // uint32_t personalities[]
// // unwind_info_section_header_index_entry[]
// // unwind_info_section_header_lsda_index_entry[]
//}
//let mut header = unwind_info_section_header::default();
let num_personalities = self.personalities.len() as u32;
let index_section_offset: u32 = (Self::UNWIND_INFO_SECTION_HEADER_SIZE
+ self.personalities.len() * Self::PERSONALITY_ENTRY_SIZE)
as u32;
let index_count =
((size_of::<CompactUnwindEntry>() + Self::NUM_RECORDS_PER_SECOND_LEVEL_PAGE - 1)
/ Self::NUM_RECORDS_PER_SECOND_LEVEL_PAGE) as u32;
// The unwind section version.
self.write(Self::UNWIND_SECTION_VERSION)?;
// The offset from the base pointer at which the `commonEncodingsArraySection` can be found. We don't use it,
// therefore...
self.write(Self::UNWIND_INFO_SECTION_HEADER_SIZE as u32)?;
// Its size is zero.
self.write(0u32)?;
// The offset from the base pointer at which the `personalityArraySection` can be found. It is right after the
// header.
self.write(Self::UNWIND_INFO_SECTION_HEADER_SIZE as u32)?;
// Its size corresponds to the number of personality functions we've seen. Should,
// in fact, be 0 or 1.
self.write(num_personalities)?;
// The offset from the base pointer at which the `indexSection` can be found. It is right after the
// header.
self.write(index_section_offset)?;
self.write(index_count + 1)?;
Ok(())
}
fn write_personalities(&mut self) -> CUResult<()> {
let personalities = self.personalities.len();
for _ in 0..personalities {
let personality_pointer =
if let Some(personality) = self.maybe_eh_personality_addr_in_got {
personality
} else {
return Err(CompileError::Codegen(
"Personality function does not appear in GOT table!".into(),
));
};
let delta = (personality_pointer - self.dso_base) as u32;
//eprintln!(
// "self got: {:x?}, {:?}",
// self.maybe_got_ptr, self.maybe_got_info
//);
//eprintln!(
// "personality_pointer: {:p}, delta: {:x}, dso_base: {:p}",
// personality_pointer as *const u8, delta, self.dso_base as *const u8,
//);
self.write(delta)?;
}
Ok(())
}
fn write_indices(&mut self) -> CUResult<()> {
let section_offset_to_lsdas: usize = self.unwind_info_section.len()
+ ((self.num_second_level_pages + 1) * Self::INDEX_ENTRY_SIZE);
// Calculate the offset to the first second-level page.
let section_offset_to_second_level_pages =
section_offset_to_lsdas + (self.num_lsdas * Self::LSDA_ENTRY_SIZE);
let base = self.unwind_info_section;
let mut num_previous_lsdas = 0;
for (entry_idx, entry) in self.compact_unwind_entries.iter().enumerate() {
let num_entries = self.compact_unwind_entries.len();
for entry_idx in 0..num_entries {
let entry = &self.compact_unwind_entries[entry_idx];
let lsda_addr = entry.lsda_addr;
if entry_idx % Self::NUM_RECORDS_PER_SECOND_LEVEL_PAGE == 0 {
let fn_delta = entry.function_addr.wrapping_sub(base);
let fn_delta = entry.function_addr.wrapping_sub(self.dso_base);
let second_level_page_offset = section_offset_to_second_level_pages
+ (entry_idx / Self::NUM_RECORDS_PER_SECOND_LEVEL_PAGE);
let lsda_offset =
section_offset_to_lsdas + num_previous_lsdas * Self::LSDA_ENTRY_SIZE;
writer.write::<u32>(fn_delta as u32)?;
writer.write::<u32>(second_level_page_offset as u32)?;
writer.write::<u32>(lsda_offset as u32)?;
self.write(fn_delta as u32)?;
self.write(second_level_page_offset as u32)?;
self.write(lsda_offset as u32)?;
}
if entry.lsda_addr != 0 {
if lsda_addr != 0 {
num_previous_lsdas += 1;
}
}
if let Some(last_entry) = self.compact_unwind_entries.last() {
let range_end = last_entry
.function_addr
.wrapping_add(last_entry.length as _);
let fn_end_delta = range_end.wrapping_sub(base) as u32;
let fn_end_delta = (last_entry.function_addr + (last_entry.length as usize))
.wrapping_sub(self.dso_base) as u32;
writer.write::<u32>(fn_end_delta)?;
writer.write::<u32>(0)?;
writer.write::<u32>(section_offset_to_second_level_pages as u32)?;
self.write(fn_end_delta)?;
self.write(0u32)?;
self.write(section_offset_to_second_level_pages as u32)?;
}
Ok(())
}
fn write_lsdas(&self, writer: &mut ByteWriter) -> CUResult<()> {
let uw_base = self.unwind_info_section;
for entry in self.compact_unwind_entries.iter() {
fn write_lsdas(&mut self) -> CUResult<()> {
let num_entries = self.compact_unwind_entries.len();
for entry_idx in 0..num_entries {
let entry = &self.compact_unwind_entries[entry_idx];
if entry.lsda_addr != 0 {
let fn_delta = entry.function_addr.wrapping_sub(uw_base);
let lsda_delta = entry.lsda_addr.wrapping_sub(uw_base);
writer.write::<u32>(fn_delta as u32)?;
writer.write::<u32>(lsda_delta as u32)?;
let fn_delta = entry.function_addr.wrapping_sub(self.dso_base);
let lsda_delta = entry.lsda_addr.wrapping_sub(self.dso_base);
self.write(fn_delta as u32)?;
self.write(lsda_delta as u32)?;
}
}
Ok(())
}
fn write_second_level_pages(&self, writer: &mut ByteWriter) -> CUResult<()> {
fn write_second_level_pages(&mut self) -> CUResult<()> {
let num_entries = self.compact_unwind_entries.len();
let uw_base = self.unwind_info_section;
for (entry_idx, entry) in self.compact_unwind_entries.iter().enumerate() {
for entry_idx in 0..num_entries {
let entry = &self.compact_unwind_entries[entry_idx];
let fn_delta = entry.function_addr.wrapping_sub(self.dso_base) as u32;
let encoding = entry.compact_encoding;
if entry_idx % Self::NUM_RECORDS_PER_SECOND_LEVEL_PAGE == 0 {
const SECOND_LEVEL_PAGE_HEADER_KIND: u32 = 2;
const SECOND_LEVEL_PAGE_HEADER_SIZE: u16 = 8;
@ -436,16 +589,29 @@ impl CompactUnwindManager {
Self::NUM_RECORDS_PER_SECOND_LEVEL_PAGE,
) as u16;
writer.write::<u32>(SECOND_LEVEL_PAGE_HEADER_KIND)?;
writer.write::<u16>(SECOND_LEVEL_PAGE_HEADER_SIZE)?;
writer.write::<u16>(second_level_page_num_entries)?;
self.write(SECOND_LEVEL_PAGE_HEADER_KIND)?;
self.write(SECOND_LEVEL_PAGE_HEADER_SIZE)?;
self.write(second_level_page_num_entries)?;
}
let fn_delta = entry.function_addr.wrapping_sub(uw_base);
writer.write::<u32>(fn_delta as u32)?;
writer.write::<u32>(entry.compact_encoding)?;
self.write(fn_delta)?;
self.write(encoding)?;
}
Ok(())
}
pub(crate) fn deregister(&self) {
if self.dso_base != 0 {
unsafe { __unw_remove_find_dynamic_unwind_sections(find_dynamic_unwind_sections) };
}
}
pub(crate) fn register(&self) {
unsafe {
if self.dso_base != 0 {
__unw_add_find_dynamic_unwind_sections(find_dynamic_unwind_sections);
}
}
}
}

View File

@ -151,7 +151,7 @@ pub fn emit_compilation(
.map(|(_, section)| section.relocations.clone())
.collect::<PrimaryMap<SectionIndex, _>>();
let debug_index = compilation.debug.map(|d| d.eh_frame);
let debug_index = compilation.unwind_info.eh_frame;
let align = match triple.architecture {
Architecture::X86_64 => 1,
@ -165,7 +165,7 @@ pub fn emit_compilation(
.custom_sections
.into_iter()
.map(|(section_index, custom_section)| {
if debug_index.map_or(false, |d| d == Some(section_index)) {
if debug_index.map_or(false, |d| d == section_index) {
// If this is the debug section
let segment = obj.segment_name(StandardSegment::Debug).to_vec();
let section_id =
@ -279,7 +279,7 @@ pub fn emit_compilation(
}
for (section_index, relocations) in custom_section_relocations.into_iter() {
if !debug_index.map_or(false, |d| d == Some(section_index)) {
if !debug_index.map_or(false, |d| d == section_index) {
// Skip DWARF relocations just yet
let (section_id, symbol_id) = custom_section_ids.get(section_index).unwrap();
all_relocations.push((*section_id, *symbol_id, relocations));

View File

@ -5,7 +5,7 @@
#![allow(missing_docs)]
use crate::types::{
function::{CompiledFunctionFrameInfo, Dwarf, FunctionBody, GOT},
function::{CompiledFunctionFrameInfo, FunctionBody, UnwindInfo, GOT},
module::CompileModuleInfo,
relocation::Relocation,
section::{CustomSection, SectionIndex},
@ -35,8 +35,8 @@ pub struct SerializableCompilation {
pub custom_sections: PrimaryMap<SectionIndex, CustomSection>,
pub custom_section_relocations: PrimaryMap<SectionIndex, Vec<Relocation>>,
// The section indices corresponding to the Dwarf debug info
pub debug: Option<Dwarf>,
pub got: Option<GOT>,
pub unwind_info: UnwindInfo,
pub got: GOT,
// Custom section containing libcall trampolines.
pub libcall_trampolines: SectionIndex,
// Length of each libcall trampoline.

View File

@ -50,7 +50,7 @@ pub fn wpreftype_to_type(ty: wasmparser::RefType) -> WasmResult<Type> {
Ok(Type::ExternRef)
} else if ty.is_func_ref() {
Ok(Type::FuncRef)
} else if ty == wasmparser::RefType::EXNREF {
} else if ty == wasmparser::RefType::EXNREF || ty == wasmparser::RefType::EXN {
// no `.is_exnref` yet
Ok(Type::ExceptionRef)
} else {

View File

@ -9,11 +9,9 @@
//! A `Compilation` contains the compiled function bodies for a WebAssembly
//! module (`CompiledFunction`).
use std::collections::HashMap;
use super::{
address_map::FunctionAddressMap,
relocation::{Relocation, RelocationTarget},
relocation::Relocation,
section::{CustomSection, SectionIndex},
unwind::{
ArchivedCompiledFunctionUnwindInfo, CompiledFunctionUnwindInfo,
@ -130,9 +128,9 @@ pub type CustomSections = PrimaryMap<SectionIndex, CustomSection>;
/// for debugging.
#[cfg_attr(feature = "enable-serde", derive(Deserialize, Serialize))]
#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
#[derive(RkyvSerialize, RkyvDeserialize, Archive, Debug, PartialEq, Eq, Clone)]
#[derive(RkyvSerialize, RkyvDeserialize, Archive, Debug, PartialEq, Eq, Clone, Default)]
#[rkyv(derive(Debug), compare(PartialEq))]
pub struct Dwarf {
pub struct UnwindInfo {
/// The section index in the [`Compilation`] that corresponds to the exception frames.
/// [Learn
/// more](https://refspecs.linuxfoundation.org/LSB_3.0.0/LSB-PDA/LSB-PDA/ehframechpt.html).
@ -140,26 +138,7 @@ pub struct Dwarf {
pub compact_unwind: Option<SectionIndex>,
}
/// The GOT - Global Offset Table - for this Compilation.
///
/// The GOT is but a list of pointers to objects (functions, data, sections..); in our context the
/// GOT is represented simply as a custom section.
///
/// This data structure holds the index of the related custom section and a map between
/// [`RelocationTarget`] and the entry number in the GOT; that is, for a relocation target `r` one
/// can find its address in the got as `r_addr = custom_sections[GOT_index][GOT_map[r]]`.
#[cfg_attr(feature = "enable-serde", derive(Deserialize, Serialize))]
#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
#[derive(RkyvSerialize, RkyvDeserialize, Archive, Debug, PartialEq, Eq, Clone)]
#[rkyv(derive(Debug))]
pub struct GOT {
/// The section index in the [`Compilation`] that corresponds to the GOT.
pub index: SectionIndex,
/// The map between relocation targets and their index in the GOT.
pub map: HashMap<RelocationTarget, usize>,
}
impl Dwarf {
impl UnwindInfo {
/// Creates a `Dwarf` struct with the corresponding indices for its sections
pub fn new(eh_frame: SectionIndex) -> Self {
Self {
@ -176,6 +155,24 @@ impl Dwarf {
}
}
/// The GOT - Global Offset Table - for this Compilation.
///
/// The GOT is but a list of pointers to objects (functions, data, sections..); in our context the
/// GOT is represented simply as a custom section.
#[cfg_attr(feature = "enable-serde", derive(Deserialize, Serialize))]
#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
#[derive(RkyvSerialize, RkyvDeserialize, Archive, Debug, PartialEq, Eq, Clone, Default)]
#[rkyv(derive(Debug))]
pub struct GOT {
/// The section index in the [`Compilation`] that corresponds to the GOT.
pub index: Option<SectionIndex>,
}
impl GOT {
pub fn empty() -> Self {
Self { index: None }
}
}
/// The result of compiling a WebAssembly module's functions.
#[cfg_attr(feature = "enable-serde", derive(Deserialize, Serialize))]
#[derive(Debug, PartialEq, Eq)]
@ -220,9 +217,9 @@ pub struct Compilation {
/// Note: Dynamic function trampolines are only compiled for imported function types.
pub dynamic_function_trampolines: PrimaryMap<FunctionIndex, FunctionBody>,
/// Section ids corresponding to the Dwarf debug info
pub debug: Option<Dwarf>,
/// Section ids corresponding to the unwind information.
pub unwind_info: UnwindInfo,
/// An optional reference to the [`GOT`].
pub got: Option<GOT>,
/// A reference to the [`GOT`] instance for the compilation.
pub got: GOT,
}

View File

@ -336,7 +336,9 @@ pub trait RelocationLike {
.wrapping_add(reloc_addend as u64);
(reloc_address, reloc_delta_u32)
}
RelocationKind::Aarch64AdrPrelPgHi21 | RelocationKind::MachoArm64RelocGotLoadPage21 => {
RelocationKind::Aarch64AdrPrelPgHi21
| RelocationKind::MachoArm64RelocGotLoadPage21
| RelocationKind::MachoArm64RelocPage21 => {
let reloc_address = start + self.offset() as usize;
let reloc_addend = self.addend() as isize;
let target_page =
@ -345,7 +347,7 @@ pub trait RelocationLike {
(reloc_address, target_page.wrapping_sub(pc_page) as u64)
}
RelocationKind::MachoArm64RelocGotLoadPageoff12
| RelocationKind::MachoArm64RelocPointerToGot => {
| RelocationKind::MachoArm64RelocPageoff12 => {
let reloc_address = start + self.offset() as usize;
let reloc_addend = self.addend() as isize;
let target_offset =
@ -391,6 +393,12 @@ pub trait RelocationLike {
reloc_delta = reloc_delta.wrapping_add((reloc_delta & 0x80000000) << 1);
(reloc_address, reloc_delta)
}
RelocationKind::MachoArm64RelocPointerToGot => {
let reloc_address = start + self.offset() as usize;
let reloc_delta =
(target_func_address as isize).wrapping_sub(reloc_address as isize);
(reloc_address, reloc_delta as u64)
}
_ => panic!("Relocation kind unsupported"),
}
}

View File

@ -73,42 +73,75 @@ pub const USING_SJLJ_EXCEPTIONS: bool = cfg!(all(
target_arch = "arm"
));
macro_rules! log {
($e: expr) => {
// todo: remove me
if false {
eprintln!($e)
}
};
($($e: expr),*) => {
// todo: remove me
if false {
eprintln!($($e),*)
}
};
}
pub unsafe fn find_eh_action(lsda: *const u8, context: &EHContext<'_>) -> Result<EHAction, ()> {
if lsda.is_null() {
return Ok(EHAction::None);
}
log!("(pers) Analysing LSDA at {lsda:?}");
let func_start = context.func_start;
let mut reader = DwarfReader::new(lsda);
let lpad_base = unsafe {
let lp_start_encoding = reader.read::<u8>();
log!("(pers) Read LP start encoding {lp_start_encoding:?}");
// base address for landing pad offsets
if lp_start_encoding != DW_EH_PE_omit {
read_encoded_pointer(&mut reader, context, lp_start_encoding)?
} else {
log!("(pers) (is omit)");
func_start
}
};
log!("(pers) read landingpad base: {lpad_base:?}");
let ttype_encoding = unsafe { reader.read::<u8>() };
log!("(pers) read ttype encoding: {ttype_encoding:?}");
// If no value for type_table_encoding was given it means that there's no
// type_table, therefore we can't possibly use this lpad.
if ttype_encoding == DW_EH_PE_omit {
log!("(pers) ttype is omit, returning None");
return Ok(EHAction::None);
}
let class_info = unsafe {
let offset = reader.read_uleb128();
log!("(pers) read class_info offset {offset:?}");
reader.ptr.wrapping_add(offset as _)
};
log!("(pers) read class_info sits at offset {class_info:?}");
let call_site_encoding = unsafe { reader.read::<u8>() };
log!("(pers) read call_site_encoding is {call_site_encoding:?}");
let action_table = unsafe {
let call_site_table_length = reader.read_uleb128();
log!("(pers) read call_site has length {call_site_table_length:?}");
reader.ptr.wrapping_add(call_site_table_length as usize)
};
log!("(pers) action table sits at offset {action_table:?}");
let ip = context.ip;
if !USING_SJLJ_EXCEPTIONS {
@ -120,6 +153,11 @@ pub unsafe fn find_eh_action(lsda: *const u8, context: &EHContext<'_>) -> Result
let cs_len = read_encoded_offset(&mut reader, call_site_encoding)?;
let cs_lpad = read_encoded_offset(&mut reader, call_site_encoding)?;
let cs_action_entry = reader.read_uleb128();
log!("(pers) read cs_start is {cs_start:?}");
log!("(pers) read cs_len is {cs_len:?}");
log!("(pers) read cs_lpad is {cs_lpad:?}");
log!("(pers) read cs_ae is {cs_action_entry:?}");
// Callsite table is sorted by cs_start, so if we've passed the ip, we
// may stop searching.
if ip < func_start.wrapping_add(cs_start) {
@ -127,22 +165,36 @@ pub unsafe fn find_eh_action(lsda: *const u8, context: &EHContext<'_>) -> Result
}
if ip < func_start.wrapping_add(cs_start + cs_len) {
log!(
"(pers) found a matching call site: {func_start:?} <= {ip:?} <= {:?}",
func_start.wrapping_add(cs_start + cs_len)
);
if cs_lpad == 0 {
return Ok(EHAction::None);
} else {
let lpad = lpad_base.wrapping_add(cs_lpad);
log!("(pers) lpad sits at {lpad:?}");
if cs_action_entry == 0 {
return Ok(EHAction::Cleanup(lpad));
}
log!("(pers) read cs_action_entry: {cs_action_entry}");
log!("(pers) action_table: {action_table:?}");
// Convert 1-based byte offset into
let mut action: *const u8 =
action_table.wrapping_add((cs_action_entry - 1) as usize);
log!("(pers) first action at: {action:?}");
loop {
let mut reader = DwarfReader::new(action);
let ttype_index = reader.read_sleb128();
log!(
"(pers) ttype_index for action #{cs_action_entry}: {ttype_index:?}"
);
if ttype_index > 0 {
if class_info.is_null() {
@ -160,7 +212,10 @@ pub unsafe fn find_eh_action(lsda: *const u8, context: &EHContext<'_>) -> Result
_ => panic!(),
};
log!("(pers) new_ttype_index for action #{cs_action_entry}: {new_ttype_index:?}");
let i = class_info.wrapping_sub(new_ttype_index as usize);
log!("(pers) reading ttype info from {i:?}");
read_encoded_pointer(
&mut DwarfReader::new(i),
context,
@ -280,20 +335,37 @@ unsafe fn read_encoded_pointer(
return Err(());
}
log!("(pers) About to read encoded pointer at {:?}", reader.ptr);
let base_ptr = match encoding & 0x70 {
DW_EH_PE_absptr => core::ptr::null(),
DW_EH_PE_absptr => {
log!("(pers) encoding is: DW_EH_PE_absptr ({DW_EH_PE_absptr})");
core::ptr::null()
}
// relative to address of the encoded value, despite the name
DW_EH_PE_pcrel => reader.ptr,
DW_EH_PE_pcrel => {
log!("(pers) encoding is: DW_EH_PE_pcrel ({DW_EH_PE_pcrel})");
reader.ptr
}
DW_EH_PE_funcrel => {
log!("(pers) encoding is: DW_EH_PE_funcrel ({DW_EH_PE_funcrel})");
if context.func_start.is_null() {
return Err(());
}
context.func_start
}
DW_EH_PE_textrel => (*context.get_text_start)(),
DW_EH_PE_datarel => (*context.get_data_start)(),
DW_EH_PE_textrel => {
log!("(pers) encoding is: DW_EH_PE_textrel ({DW_EH_PE_textrel})");
(*context.get_text_start)()
}
DW_EH_PE_datarel => {
log!("(pers) encoding is: DW_EH_PE_textrel ({DW_EH_PE_datarel})");
(*context.get_data_start)()
}
// aligned means the value is aligned to the size of a pointer
DW_EH_PE_aligned => {
log!("(pers) encoding is: DW_EH_PE_textrel ({DW_EH_PE_aligned})");
reader.ptr = {
let this = reader.ptr;
let addr = round_up(
@ -326,13 +398,19 @@ unsafe fn read_encoded_pointer(
}
unsafe { reader.read::<*const u8>() }
} else {
log!("(pers) since base_ptr is not null, we must an offset");
let offset = unsafe { read_encoded_offset(reader, encoding & 0x0F)? };
log!("(pers) read offset is {offset:x?}");
base_ptr.wrapping_add(offset)
};
log!("(pers) about to read from {ptr:?}");
if encoding & DW_EH_PE_indirect != 0 {
ptr = unsafe { *(ptr.cast::<*const u8>()) };
ptr = unsafe { ptr.cast::<*const u8>().read_unaligned() };
}
log!("(pers) returning ptr value {ptr:?}");
Ok(ptr)
}

View File

@ -95,12 +95,20 @@ pub unsafe extern "C" fn wasmer_eh_personality(
}
let wasmer_exc = (*uw_exc).cause.downcast_ref::<WasmerException>();
let wasmer_exc = wasmer_exc.unwrap();
let wasmer_exc = match wasmer_exc {
Some(e) => e,
None => {
return uw::_Unwind_Reason_Code__URC_CONTINUE_UNWIND;
}
};
let eh_action = match find_eh_action(context, wasmer_exc.tag) {
Ok(action) => action,
Err(_) => return uw::_Unwind_Reason_Code__URC_FATAL_PHASE1_ERROR,
Err(_) => {
return uw::_Unwind_Reason_Code__URC_FATAL_PHASE1_ERROR;
}
};
if actions as i32 & uw::_Unwind_Action__UA_SEARCH_PHASE as i32 != 0 {
match eh_action {
EHAction::None | EHAction::Cleanup(_) => {
@ -158,6 +166,6 @@ unsafe fn find_eh_action(context: *mut uw::_Unwind_Context, tag: u64) -> Result<
get_data_start: &|| uw::_Unwind_GetDataRelBase(context) as *const _,
tag,
};
eh::find_eh_action(lsda, &eh_context)
dbg!(eh::find_eh_action(lsda, &eh_context))
}
}

View File

@ -679,14 +679,16 @@
(register "module4" $module4)
(module
(import "module4" "f" (global funcref))
(type $out-i32 (func (result i32)))
(table 10 funcref)
(elem (offset (i32.const 0)) funcref (global.get 0))
(func (export "call_imported_elem") (type $out-i32)
(call_indirect (type $out-i32) (i32.const 0))
)
)
(assert_return (invoke "call_imported_elem") (i32.const 42))
;; Currently unsupported.
;;
;; (module
;; (import "module4" "f" (global funcref))
;; (type $out-i32 (func (result i32)))
;; (table 10 funcref)
;; (elem (offset (i32.const 0)) funcref (global.get 0))
;; (func (export "call_imported_elem") (type $out-i32)
;; (call_indirect (type $out-i32) (i32.const 0))
;; )
;; )
;;
;; (assert_return (invoke "call_imported_elem") (i32.const 42))

View File

@ -261,32 +261,34 @@
)
(assert_trap (invoke $Mt "call" (i32.const 7)) "uninitialized element")
;; TODO: This test is temporarily disabled because Wasmer doesn't properly
;; handle Instance lifetimes when funcrefs are involved.
;; Unlike in the v1 spec, active element segments stored before an
;; out-of-bounds access persist after the instantiation failure.
(assert_trap
(module
(table (import "Mt" "tab") 10 funcref)
(func $f (result i32) (i32.const 0))
(elem (i32.const 7) $f)
(elem (i32.const 8) $f $f $f $f $f) ;; (partially) out of bounds
)
"out of bounds table access"
)
(assert_return (invoke $Mt "call" (i32.const 7)) (i32.const 0))
(assert_trap (invoke $Mt "call" (i32.const 8)) "uninitialized element")
(assert_trap
(module
(table (import "Mt" "tab") 10 funcref)
(func $f (result i32) (i32.const 0))
(elem (i32.const 7) $f)
(memory 1)
(data (i32.const 0x10000) "d") ;; out of bounds
)
"out of bounds memory access"
)
(assert_return (invoke $Mt "call" (i32.const 7)) (i32.const 0))
;; (assert_trap
;; (module
;; (table (import "Mt" "tab") 10 funcref)
;; (func $f (result i32) (i32.const 0))
;; (elem (i32.const 7) $f)
;; (elem (i32.const 8) $f $f $f $f $f) ;; (partially) out of bounds
;; )
;; "out of bounds table access"
;; )
;; (assert_return (invoke $Mt "call" (i32.const 7)) (i32.const 0))
;; (assert_trap (invoke $Mt "call" (i32.const 8)) "uninitialized element")
;;
;; (assert_trap
;; (module
;; (table (import "Mt" "tab") 10 funcref)
;; (func $f (result i32) (i32.const 0))
;; (elem (i32.const 7) $f)
;; (memory 1)
;; (data (i32.const 0x10000) "d") ;; out of bounds
;; )
;; "out of bounds memory access"
;; )
;; (assert_return (invoke $Mt "call" (i32.const 7)) (i32.const 0))
(module $Mtable_ex
(table $t1 (export "t-func") 1 funcref)
@ -450,4 +452,4 @@
)
(assert_return (invoke $Ms "get memory[0]") (i32.const 104)) ;; 'h'
(assert_return (invoke $Ms "get table[0]") (i32.const 0xdead))
;; (assert_return (invoke $Ms "get table[0]") (i32.const 0xdead))