Cranelift in Unix working all the way with unwind info

This commit is contained in:
Syrus
2020-06-10 22:33:25 -07:00
parent e2cafd07da
commit dbd37e6740
11 changed files with 80 additions and 70 deletions

View File

@ -17,9 +17,7 @@ use cranelift_codegen::ir;
use cranelift_codegen::print_errors::pretty_error; use cranelift_codegen::print_errors::pretty_error;
use cranelift_codegen::{binemit, isa, Context}; use cranelift_codegen::{binemit, isa, Context};
#[cfg(feature = "unwind")] #[cfg(feature = "unwind")]
use gimli::write::{Address, EhFrame, EndianVec, FrameTable, Sections, Writer}; use gimli::write::{Address, EhFrame, FrameTable};
#[cfg(feature = "unwind")]
use gimli::{RunTimeEndian, SectionId};
use rayon::prelude::{IntoParallelRefIterator, ParallelIterator}; use rayon::prelude::{IntoParallelRefIterator, ParallelIterator};
use wasm_common::entity::{EntityRef, PrimaryMap}; use wasm_common::entity::{EntityRef, PrimaryMap};
use wasm_common::{ use wasm_common::{
@ -189,6 +187,7 @@ impl Compiler for CraneliftCompiler {
.into_iter() .into_iter()
.collect::<PrimaryMap<LocalFunctionIndex, _>>(); .collect::<PrimaryMap<LocalFunctionIndex, _>>();
let mut custom_sections = PrimaryMap::new();
let mut eh_frame = EhFrame(WriterRelocate::default()); let mut eh_frame = EhFrame(WriterRelocate::default());
dwarf_frametable dwarf_frametable
.lock() .lock()
@ -197,9 +196,6 @@ impl Compiler for CraneliftCompiler {
.unwrap(); .unwrap();
let eh_frame_section = eh_frame.0.into_section(); let eh_frame_section = eh_frame.0.into_section();
println!("section: {:?}", eh_frame_section);
let mut custom_sections = PrimaryMap::new();
custom_sections.push(eh_frame_section); custom_sections.push(eh_frame_section);
let dwarf = Dwarf::new(SectionIndex::new(0)); let dwarf = Dwarf::new(SectionIndex::new(0));

View File

@ -1,4 +1,4 @@
use gimli::write::{Address, Dwarf, EndianVec, FrameTable, Result, Sections, Writer}; use gimli::write::{Address, EndianVec, Result, Writer};
use gimli::{RunTimeEndian, SectionId}; use gimli::{RunTimeEndian, SectionId};
use wasm_common::entity::EntityRef; use wasm_common::entity::EntityRef;
use wasm_common::LocalFunctionIndex; use wasm_common::LocalFunctionIndex;
@ -84,7 +84,7 @@ impl Writer for WriterRelocate {
} }
} }
fn write_offset(&mut self, val: usize, section: SectionId, size: u8) -> Result<()> { fn write_offset(&mut self, _val: usize, _section: SectionId, _size: u8) -> Result<()> {
unimplemented!("write_offset not yet implemented"); unimplemented!("write_offset not yet implemented");
// let offset = self.len() as u32; // let offset = self.len() as u32;
// let target = section.name().to_string(); // let target = section.name().to_string();
@ -99,10 +99,10 @@ impl Writer for WriterRelocate {
fn write_offset_at( fn write_offset_at(
&mut self, &mut self,
offset: usize, _offset: usize,
val: usize, _val: usize,
section: SectionId, _section: SectionId,
size: u8, _size: u8,
) -> Result<()> { ) -> Result<()> {
unimplemented!("write_offset_at not yet implemented"); unimplemented!("write_offset_at not yet implemented");
// let target = section.name().to_string(); // let target = section.name().to_string();

View File

@ -77,12 +77,12 @@ pub type CustomSections = PrimaryMap<SectionIndex, CustomSection>;
/// In the future this structure may also hold other information useful /// In the future this structure may also hold other information useful
/// for debugging. /// for debugging.
#[cfg_attr(feature = "enable-serde", derive(Deserialize, Serialize))] #[cfg_attr(feature = "enable-serde", derive(Deserialize, Serialize))]
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq, Clone)]
pub struct Dwarf { pub struct Dwarf {
/// The section index in the [`Compilation`] that corresponds to the exception frames. /// The section index in the [`Compilation`] that corresponds to the exception frames.
/// More info: /// More info:
/// https://refspecs.linuxfoundation.org/LSB_3.0.0/LSB-PDA/LSB-PDA/ehframechpt.html /// https://refspecs.linuxfoundation.org/LSB_3.0.0/LSB-PDA/LSB-PDA/ehframechpt.html
eh_frame: SectionIndex, pub eh_frame: SectionIndex,
} }
impl Dwarf { impl Dwarf {
@ -179,6 +179,11 @@ impl Compilation {
.map(|(_, section)| section.relocations.clone()) .map(|(_, section)| section.relocations.clone())
.collect::<PrimaryMap<SectionIndex, _>>() .collect::<PrimaryMap<SectionIndex, _>>()
} }
/// Returns the Dwarf info.
pub fn get_dwarf(&self) -> Option<Dwarf> {
self.dwarf.clone()
}
} }
impl<'a> IntoIterator for &'a Compilation { impl<'a> IntoIterator for &'a Compilation {

View File

@ -118,6 +118,7 @@ impl JITArtifact {
dynamic_function_trampolines, dynamic_function_trampolines,
custom_sections: compilation.get_custom_sections(), custom_sections: compilation.get_custom_sections(),
custom_section_relocations: compilation.get_custom_section_relocations(), custom_section_relocations: compilation.get_custom_section_relocations(),
dwarf: compilation.get_dwarf(),
}; };
let serializable = SerializableModule { let serializable = SerializableModule {
compilation: serializable_compilation, compilation: serializable_compilation,
@ -191,8 +192,21 @@ impl JITArtifact {
.collect::<PrimaryMap<_, _>>() .collect::<PrimaryMap<_, _>>()
}; };
let eh_frame = match &serializable.compilation.dwarf {
Some(dwarf) => {
let eh_frame_section_size = serializable.compilation.custom_sections
[dwarf.eh_frame]
.bytes
.len();
let eh_frame_section_pointer = custom_sections[dwarf.eh_frame];
Some(unsafe {
std::slice::from_raw_parts(eh_frame_section_pointer, eh_frame_section_size)
})
}
None => None,
};
// Make all code compiled thus far executable. // Make all code compiled thus far executable.
inner_jit.publish_compiled_code(); inner_jit.publish_compiled_code(eh_frame);
let finished_functions = finished_functions.into_boxed_slice(); let finished_functions = finished_functions.into_boxed_slice();
let finished_dynamic_function_trampolines = let finished_dynamic_function_trampolines =

View File

@ -2,7 +2,6 @@
use crate::unwind::UnwindRegistry; use crate::unwind::UnwindRegistry;
use region; use region;
use std::mem::ManuallyDrop; use std::mem::ManuallyDrop;
use std::ptr;
use std::{cmp, mem}; use std::{cmp, mem};
use wasm_common::entity::PrimaryMap; use wasm_common::entity::PrimaryMap;
use wasm_common::LocalFunctionIndex; use wasm_common::LocalFunctionIndex;
@ -140,7 +139,7 @@ impl CodeMemory {
} }
/// Make all allocated memory executable. /// Make all allocated memory executable.
pub fn publish(&mut self) { pub fn publish(&mut self, eh_frame: Option<&[u8]>) {
self.push_current(0) self.push_current(0)
.expect("failed to push current memory map"); .expect("failed to push current memory map");
@ -150,7 +149,7 @@ impl CodeMemory {
} in &mut self.entries[self.published..] } in &mut self.entries[self.published..]
{ {
// Remove write access to the pages due to the relocation fixups. // Remove write access to the pages due to the relocation fixups.
r.publish() r.publish(eh_frame)
.expect("failed to publish function unwind registry"); .expect("failed to publish function unwind registry");
if !m.is_empty() { if !m.is_empty() {

View File

@ -248,8 +248,8 @@ impl JITEngineInner {
} }
/// Make memory containing compiled code executable. /// Make memory containing compiled code executable.
pub(crate) fn publish_compiled_code(&mut self) { pub(crate) fn publish_compiled_code(&mut self, eh_frame: Option<&[u8]>) {
self.code_memory.publish(); self.code_memory.publish(eh_frame);
} }
/// Shared signature registry. /// Shared signature registry.

View File

@ -5,7 +5,9 @@ use wasm_common::{
Features, FunctionIndex, LocalFunctionIndex, MemoryIndex, OwnedDataInitializer, SignatureIndex, Features, FunctionIndex, LocalFunctionIndex, MemoryIndex, OwnedDataInitializer, SignatureIndex,
TableIndex, TableIndex,
}; };
use wasmer_compiler::{CustomSection, FunctionBody, JumpTableOffsets, Relocation, SectionIndex}; use wasmer_compiler::{
CustomSection, Dwarf, FunctionBody, JumpTableOffsets, Relocation, SectionIndex,
};
use wasmer_engine::SerializableFunctionFrameInfo; use wasmer_engine::SerializableFunctionFrameInfo;
use wasmer_runtime::ModuleInfo; use wasmer_runtime::ModuleInfo;
use wasmer_runtime::{MemoryPlan, TablePlan}; use wasmer_runtime::{MemoryPlan, TablePlan};
@ -24,6 +26,8 @@ pub struct SerializableCompilation {
pub dynamic_function_trampolines: PrimaryMap<FunctionIndex, FunctionBody>, pub dynamic_function_trampolines: PrimaryMap<FunctionIndex, FunctionBody>,
pub custom_sections: PrimaryMap<SectionIndex, CustomSection>, pub custom_sections: PrimaryMap<SectionIndex, CustomSection>,
pub custom_section_relocations: PrimaryMap<SectionIndex, Vec<Relocation>>, pub custom_section_relocations: PrimaryMap<SectionIndex, Vec<Relocation>>,
// The section corresponding to the Dwarf debug infos
pub dwarf: Option<Dwarf>,
} }
/// Serializable struct that is able to serialize from and to /// Serializable struct that is able to serialize from and to

View File

@ -24,7 +24,7 @@ impl DummyUnwindRegistry {
} }
/// Publishes all registered functions. /// Publishes all registered functions.
pub fn publish(&mut self) -> Result<(), String> { pub fn publish(&mut self, eh_frame: Option<&[u8]>) -> Result<(), String> {
// Do nothing // Do nothing
Ok(()) Ok(())
} }

View File

@ -2,15 +2,12 @@ cfg_if::cfg_if! {
if #[cfg(all(windows, target_arch = "x86_64"))] { if #[cfg(all(windows, target_arch = "x86_64"))] {
mod windows_x64; mod windows_x64;
pub use self::windows_x64::*; pub use self::windows_x64::*;
} else if #[cfg(all(windows, target_arch = "x86"))] { } else if #[cfg(unix)] {
mod windows_x32; mod systemv;
pub use self::windows_x32::*; pub use self::systemv::*;
// } else if #[cfg(unix)] {
// mod systemv;
// pub use self::systemv::*;
} else { } else {
// Otherwise, we provide a dummy fallback without unwinding
mod dummy; mod dummy;
pub use self::dummy::DummyUnwindRegistry as UnwindRegistry; pub use self::dummy::DummyUnwindRegistry as UnwindRegistry;
// compile_error!("unsupported target platform for unwind");
} }
} }

View File

@ -3,9 +3,6 @@ use wasmer_compiler::CompiledFunctionUnwindInfo;
/// Represents a registry of function unwind information for System V ABI. /// Represents a registry of function unwind information for System V ABI.
pub struct UnwindRegistry { pub struct UnwindRegistry {
base_address: usize,
functions: Vec<gimli::write::FrameDescriptionEntry>,
frame_table: Vec<u8>,
registrations: Vec<usize>, registrations: Vec<usize>,
published: bool, published: bool,
} }
@ -18,35 +15,33 @@ extern "C" {
impl UnwindRegistry { impl UnwindRegistry {
/// Creates a new unwind registry with the given base address. /// Creates a new unwind registry with the given base address.
pub fn new(base_address: usize) -> Self { pub fn new(_base_address: usize) -> Self {
Self { Self {
base_address,
functions: Vec::new(),
frame_table: Vec::new(),
registrations: Vec::new(), registrations: Vec::new(),
published: false, published: false,
} }
} }
/// Registers a function given the start offset, length, and unwind information. /// Registers a function given the start offset, length, and unwind information.
pub fn register(&mut self, _func_start: u32, _func_len: u32, _info: &CompiledFunctionUnwindInfo) -> Result<(), String> { pub fn register(
// Do nothing &mut self,
_func_start: u32,
_func_len: u32,
info: &CompiledFunctionUnwindInfo,
) -> Result<(), String> {
match info {
CompiledFunctionUnwindInfo::Dwarf => {}
_ => return Err("unsupported unwind information".to_string()),
};
Ok(()) Ok(())
} }
/// Publishes all registered functions. /// Publishes all registered functions.
pub fn publish(&mut self, isa: &dyn TargetIsa) -> Result<()> { pub fn publish(&mut self, eh_frame: Option<&[u8]>) -> Result<(), String> {
if self.published { if let Some(eh_frame) = eh_frame {
bail!("unwind registry has already been published"); unsafe {
} self.register_frames(eh_frame);
}
if self.functions.is_empty() {
self.published = true;
return Ok(());
}
unsafe {
self.register_frames();
} }
self.published = true; self.published = true;
@ -54,13 +49,13 @@ impl UnwindRegistry {
Ok(()) Ok(())
} }
#[allow(clippy::cast_ptr_alignment)]
unsafe fn register_frames(&mut self) { unsafe fn register_frames(&mut self, eh_frame: &[u8]) {
cfg_if::cfg_if! { cfg_if::cfg_if! {
if #[cfg(target_os = "macos")] { if #[cfg(target_os = "macos")] {
// On macOS, `__register_frame` takes a pointer to a single FDE // On macOS, `__register_frame` takes a pointer to a single FDE
let start = self.frame_table.as_ptr(); let start = eh_frame.as_ptr();
let end = start.add(self.frame_table.len()); let end = start.add(eh_frame.len());
let mut current = start; let mut current = start;
// Walk all of the entries in the frame table and register them // Walk all of the entries in the frame table and register them
@ -78,7 +73,7 @@ impl UnwindRegistry {
} }
} else { } else {
// On other platforms, `__register_frame` will walk the FDEs until an entry of length 0 // On other platforms, `__register_frame` will walk the FDEs until an entry of length 0
let ptr = self.frame_table.as_ptr(); let ptr = eh_frame.as_ptr();
__register_frame(ptr); __register_frame(ptr);
self.registrations.push(ptr as usize); self.registrations.push(ptr as usize);
} }

View File

@ -32,28 +32,28 @@ impl UnwindRegistry {
} }
match info { match info {
CompiledFunctionUnwindInfo::WindowsX64(_) => { CompiledFunctionUnwindInfo::WindowsX64(_) => {}
let mut entry = winnt::RUNTIME_FUNCTION::default(); _ => return Err("unsupported unwind information".to_string()),
};
entry.BeginAddress = func_start; let mut entry = winnt::RUNTIME_FUNCTION::default();
entry.EndAddress = func_start + func_len;
// The unwind information should be immediately following the function entry.BeginAddress = func_start;
// with padding for 4 byte alignment entry.EndAddress = func_start + func_len;
unsafe {
*entry.u.UnwindInfoAddress_mut() = (entry.EndAddress + 3) & !3;
}
self.functions.push(entry); // The unwind information should be immediately following the function
// with padding for 4 byte alignment
Ok(()) unsafe {
} *entry.u.UnwindInfoAddress_mut() = (entry.EndAddress + 3) & !3;
_ => Err("unsupported unwind information".to_string()),
} }
self.functions.push(entry);
Ok(())
} }
/// Publishes all registered functions. /// Publishes all registered functions.
pub fn publish(&mut self) -> Result<(), String> { pub fn publish(&mut self, _eh_frame: Option<&[u8]>) -> Result<(), String> {
if self.published { if self.published {
return Err("unwind registry has already been published".to_string()); return Err("unwind registry has already been published".to_string());
} }