diff --git a/Cargo.lock b/Cargo.lock index 8b7ded36bb38..66d4be9cfbff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -310,6 +310,7 @@ version = "0.1.0" dependencies = [ "ar", "byteorder", + "cfg-if", "cranelift-codegen", "cranelift-frontend", "cranelift-module", diff --git a/Cargo.toml b/Cargo.toml index 90f5429bb0e1..e9e6008f6cf3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,7 @@ target-lexicon = "0.10.0" ar = "0.8.0" byteorder = "1.2.7" indexmap = "1.0.2" +cfg-if = "0.1.10" [dependencies.object] version = "0.18.0" diff --git a/src/debuginfo/emit.rs b/src/debuginfo/emit.rs index af93620fa8a9..9cce630fed52 100644 --- a/src/debuginfo/emit.rs +++ b/src/debuginfo/emit.rs @@ -1,3 +1,5 @@ +use std::convert::TryInto; + use rustc_data_structures::fx::FxHashMap; use gimli::write::{Address, AttributeValue, EndianVec, Result, Sections, Writer}; @@ -67,6 +69,25 @@ impl WriterRelocate { writer: EndianVec::new(endian), } } + + pub(super) fn relocate_for_jit( + mut self, + jit_module: &mut cranelift_module::Module, + ) -> Vec { + for reloc in self.relocs.drain(..) { + match reloc.name { + super::DebugRelocName::Section(_) => unreachable!(), + super::DebugRelocName::Symbol(sym) => { + let addr = jit_module.get_finalized_function( + cranelift_module::FuncId::from_u32(sym.try_into().unwrap()), + ); + let val = (addr as u64 as i64 + reloc.addend) as u64; + self.writer.write_udata_at(reloc.offset as usize, val, reloc.size).unwrap(); + } + } + } + self.writer.into_vec() + } } impl Writer for WriterRelocate { diff --git a/src/debuginfo/unwind.rs b/src/debuginfo/unwind.rs index 987c4d8f3e8c..83795961377b 100644 --- a/src/debuginfo/unwind.rs +++ b/src/debuginfo/unwind.rs @@ -64,4 +64,89 @@ impl<'tcx> UnwindContext<'tcx> { } } } + + pub(crate) unsafe fn register_jit( + self, + jit_module: &mut Module, + ) -> Option { + let mut eh_frame = EhFrame::from(super::emit::WriterRelocate::new(super::target_endian(self.tcx))); + self.frame_table.write_eh_frame(&mut eh_frame).unwrap(); + + if eh_frame.0.writer.slice().is_empty() { + return None; + } + + let mut eh_frame = eh_frame.0.relocate_for_jit(jit_module); + + // GCC expects a terminating "empty" length, so write a 0 length at the end of the table. + eh_frame.extend(&[0, 0, 0, 0]); + + let mut registrations = Vec::new(); + + // ======================================================================= + // Everything after this line up to the end of the file is loosly based on + // https://github.com/bytecodealliance/wasmtime/blob/4471a82b0c540ff48960eca6757ccce5b1b5c3e4/crates/jit/src/unwind/systemv.rs + cfg_if::cfg_if! { + if #[cfg(target_os = "macos")] { + // On macOS, `__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; + + // Walk all of the entries in the frame table and register them + while current < end { + let len = std::ptr::read::(current as *const u32) as usize; + + // Skip over the CIE + if current != start { + __register_frame(current); + registrations.push(current as usize); + } + + // Move to the next table entry (+4 because the length itself is not inclusive) + current = current.add(len + 4); + } + } else { + // On other platforms, `__register_frame` will walk the FDEs until an entry of length 0 + let ptr = eh_frame.as_ptr(); + __register_frame(ptr); + registrations.push(ptr as usize); + } + } + + Some(UnwindRegistry { + _frame_table: eh_frame, + registrations, + }) + } +} + +/// Represents a registry of function unwind information for System V ABI. +pub(crate) struct UnwindRegistry { + _frame_table: Vec, + registrations: Vec, +} + +extern "C" { + // libunwind import + fn __register_frame(fde: *const u8); + fn __deregister_frame(fde: *const u8); +} + +impl Drop for UnwindRegistry { + fn drop(&mut self) { + unsafe { + // libgcc stores the frame entries as a linked list in decreasing sort order + // based on the PC value of the registered entry. + // + // As we store the registrations in increasing order, it would be O(N^2) to + // deregister in that order. + // + // 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 _); + } + } + } } diff --git a/src/driver/jit.rs b/src/driver/jit.rs index 4d7ca34d1ba3..08d21b5cc9f6 100644 --- a/src/driver/jit.rs +++ b/src/driver/jit.rs @@ -63,6 +63,8 @@ pub(super) fn run_jit(tcx: TyCtxt<'_>) -> ! { jit_module.finalize_definitions(); + let _unwind_register_guard = unsafe { unwind_context.register_jit(&mut jit_module) }; + tcx.sess.abort_if_errors(); let finalized_main: *const u8 = jit_module.get_finalized_function(main_func_id);