]>
Commit | Line | Data |
---|---|---|
29967ef6 XL |
1 | //! Unwind info generation (`.eh_frame`) |
2 | ||
3 | use crate::prelude::*; | |
4 | ||
a2a8927a | 5 | use cranelift_codegen::ir::Endianness; |
29967ef6 XL |
6 | use cranelift_codegen::isa::{unwind::UnwindInfo, TargetIsa}; |
7 | ||
c295e0f8 | 8 | use cranelift_object::ObjectProduct; |
29967ef6 | 9 | use gimli::write::{Address, CieId, EhFrame, FrameTable, Section}; |
17df50a5 | 10 | use gimli::RunTimeEndian; |
29967ef6 | 11 | |
c295e0f8 | 12 | use super::object::WriteDebugInfo; |
29967ef6 | 13 | |
17df50a5 XL |
14 | pub(crate) struct UnwindContext { |
15 | endian: RunTimeEndian, | |
29967ef6 XL |
16 | frame_table: FrameTable, |
17 | cie_id: Option<CieId>, | |
18 | } | |
19 | ||
17df50a5 | 20 | impl UnwindContext { |
a2a8927a XL |
21 | pub(crate) fn new(isa: &dyn TargetIsa, pic_eh_frame: bool) -> Self { |
22 | let endian = match isa.endianness() { | |
23 | Endianness::Little => RunTimeEndian::Little, | |
24 | Endianness::Big => RunTimeEndian::Big, | |
25 | }; | |
29967ef6 XL |
26 | let mut frame_table = FrameTable::default(); |
27 | ||
28 | let cie_id = if let Some(mut cie) = isa.create_systemv_cie() { | |
5869c6ff | 29 | if pic_eh_frame { |
29967ef6 XL |
30 | cie.fde_address_encoding = |
31 | gimli::DwEhPe(gimli::DW_EH_PE_pcrel.0 | gimli::DW_EH_PE_sdata4.0); | |
32 | } | |
33 | Some(frame_table.add_cie(cie)) | |
34 | } else { | |
35 | None | |
36 | }; | |
37 | ||
17df50a5 | 38 | UnwindContext { endian, frame_table, cie_id } |
29967ef6 XL |
39 | } |
40 | ||
41 | pub(crate) fn add_function(&mut self, func_id: FuncId, context: &Context, isa: &dyn TargetIsa) { | |
42 | let unwind_info = if let Some(unwind_info) = context.create_unwind_info(isa).unwrap() { | |
43 | unwind_info | |
44 | } else { | |
45 | return; | |
46 | }; | |
47 | ||
48 | match unwind_info { | |
49 | UnwindInfo::SystemV(unwind_info) => { | |
50 | self.frame_table.add_fde( | |
51 | self.cie_id.unwrap(), | |
6a06907d XL |
52 | unwind_info |
53 | .to_fde(Address::Symbol { symbol: func_id.as_u32() as usize, addend: 0 }), | |
29967ef6 XL |
54 | ); |
55 | } | |
56 | UnwindInfo::WindowsX64(_) => { | |
57 | // FIXME implement this | |
58 | } | |
59 | unwind_info => unimplemented!("{:?}", unwind_info), | |
60 | } | |
61 | } | |
62 | ||
c295e0f8 | 63 | pub(crate) fn emit(self, product: &mut ObjectProduct) { |
17df50a5 | 64 | let mut eh_frame = EhFrame::from(super::emit::WriterRelocate::new(self.endian)); |
29967ef6 XL |
65 | self.frame_table.write_eh_frame(&mut eh_frame).unwrap(); |
66 | ||
67 | if !eh_frame.0.writer.slice().is_empty() { | |
68 | let id = eh_frame.id(); | |
69 | let section_id = product.add_debug_section(id, eh_frame.0.writer.into_vec()); | |
70 | let mut section_map = FxHashMap::default(); | |
71 | section_map.insert(id, section_id); | |
72 | ||
73 | for reloc in &eh_frame.0.relocs { | |
74 | product.add_debug_reloc(§ion_map, §ion_id, reloc); | |
75 | } | |
76 | } | |
77 | } | |
78 | ||
17df50a5 XL |
79 | #[cfg(all(feature = "jit", windows))] |
80 | pub(crate) unsafe fn register_jit(self, _jit_module: &cranelift_jit::JITModule) {} | |
81 | ||
82 | #[cfg(all(feature = "jit", not(windows)))] | |
83 | pub(crate) unsafe fn register_jit(self, jit_module: &cranelift_jit::JITModule) { | |
84 | let mut eh_frame = EhFrame::from(super::emit::WriterRelocate::new(self.endian)); | |
29967ef6 XL |
85 | self.frame_table.write_eh_frame(&mut eh_frame).unwrap(); |
86 | ||
87 | if eh_frame.0.writer.slice().is_empty() { | |
17df50a5 | 88 | return; |
29967ef6 XL |
89 | } |
90 | ||
fc512014 | 91 | let mut eh_frame = eh_frame.0.relocate_for_jit(jit_module); |
29967ef6 XL |
92 | |
93 | // GCC expects a terminating "empty" length, so write a 0 length at the end of the table. | |
94 | eh_frame.extend(&[0, 0, 0, 0]); | |
95 | ||
17df50a5 XL |
96 | // FIXME support unregistering unwind tables once cranelift-jit supports deallocating |
97 | // individual functions | |
98 | #[allow(unused_variables)] | |
99 | let (eh_frame, eh_frame_len, _) = Vec::into_raw_parts(eh_frame); | |
29967ef6 XL |
100 | |
101 | // ======================================================================= | |
102 | // Everything after this line up to the end of the file is loosly based on | |
103 | // https://github.com/bytecodealliance/wasmtime/blob/4471a82b0c540ff48960eca6757ccce5b1b5c3e4/crates/jit/src/unwind/systemv.rs | |
104 | #[cfg(target_os = "macos")] | |
105 | { | |
106 | // On macOS, `__register_frame` takes a pointer to a single FDE | |
17df50a5 XL |
107 | let start = eh_frame; |
108 | let end = start.add(eh_frame_len); | |
29967ef6 XL |
109 | let mut current = start; |
110 | ||
111 | // Walk all of the entries in the frame table and register them | |
112 | while current < end { | |
113 | let len = std::ptr::read::<u32>(current as *const u32) as usize; | |
114 | ||
115 | // Skip over the CIE | |
116 | if current != start { | |
117 | __register_frame(current); | |
29967ef6 XL |
118 | } |
119 | ||
120 | // Move to the next table entry (+4 because the length itself is not inclusive) | |
121 | current = current.add(len + 4); | |
122 | } | |
123 | } | |
124 | #[cfg(not(target_os = "macos"))] | |
125 | { | |
126 | // On other platforms, `__register_frame` will walk the FDEs until an entry of length 0 | |
17df50a5 | 127 | __register_frame(eh_frame); |
29967ef6 | 128 | } |
29967ef6 XL |
129 | } |
130 | } | |
131 | ||
29967ef6 XL |
132 | extern "C" { |
133 | // libunwind import | |
134 | fn __register_frame(fde: *const u8); | |
29967ef6 | 135 | } |