about summary refs log tree commit diff
path: root/src/debuginfo
diff options
context:
space:
mode:
authorbjorn3 <bjorn3@users.noreply.github.com>2020-05-01 20:57:51 +0200
committerbjorn3 <bjorn3@users.noreply.github.com>2020-05-01 20:58:27 +0200
commit020bcb31dbb1e6a96575806a560bdc082bfa2bb6 (patch)
treea3ace80021ada249422d23f52af0b4c49d1608e7 /src/debuginfo
parent4da6488d95cec9709bf77a28913c8040cc0508ee (diff)
downloadrust-020bcb31dbb1e6a96575806a560bdc082bfa2bb6.tar.gz
rust-020bcb31dbb1e6a96575806a560bdc082bfa2bb6.zip
Register frame table in JIT mode
Diffstat (limited to 'src/debuginfo')
-rw-r--r--src/debuginfo/emit.rs21
-rw-r--r--src/debuginfo/unwind.rs85
2 files changed, 106 insertions, 0 deletions
diff --git a/src/debuginfo/emit.rs b/src/debuginfo/emit.rs
index af93620fa8a..9cce630fed5 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<cranelift_simplejit::SimpleJITBackend>,
+    ) -> Vec<u8> {
+        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 987c4d8f3e8..83795961377 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<cranelift_simplejit::SimpleJITBackend>,
+    ) -> Option<UnwindRegistry> {
+        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::<u32>(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<u8>,
+    registrations: Vec<usize>,
+}
+
+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 _);
+            }
+        }
+    }
 }