]> git.lizzy.rs Git - rust.git/blob - src/debuginfo/unwind.rs
Implement more simd_reduce_* intrinsics
[rust.git] / src / debuginfo / unwind.rs
1 //! Unwind info generation (`.eh_frame`)
2
3 use crate::prelude::*;
4
5 use cranelift_codegen::isa::{unwind::UnwindInfo, TargetIsa};
6
7 use gimli::write::{Address, CieId, EhFrame, FrameTable, Section};
8
9 use crate::backend::WriteDebugInfo;
10
11 pub(crate) struct UnwindContext<'tcx> {
12     tcx: TyCtxt<'tcx>,
13     frame_table: FrameTable,
14     cie_id: Option<CieId>,
15 }
16
17 impl<'tcx> UnwindContext<'tcx> {
18     pub(crate) fn new(tcx: TyCtxt<'tcx>, isa: &dyn TargetIsa) -> Self {
19         let mut frame_table = FrameTable::default();
20
21         let cie_id = if let Some(mut cie) = isa.create_systemv_cie() {
22             if isa.flags().is_pic() {
23                 cie.fde_address_encoding =
24                     gimli::DwEhPe(gimli::DW_EH_PE_pcrel.0 | gimli::DW_EH_PE_sdata4.0);
25             }
26             Some(frame_table.add_cie(cie))
27         } else {
28             None
29         };
30
31         UnwindContext {
32             tcx,
33             frame_table,
34             cie_id,
35         }
36     }
37
38     pub(crate) fn add_function(&mut self, func_id: FuncId, context: &Context, isa: &dyn TargetIsa) {
39         let unwind_info = if let Some(unwind_info) = context.create_unwind_info(isa).unwrap() {
40             unwind_info
41         } else {
42             return;
43         };
44
45         match unwind_info {
46             UnwindInfo::SystemV(unwind_info) => {
47                 self.frame_table.add_fde(
48                     self.cie_id.unwrap(),
49                     unwind_info.to_fde(Address::Symbol {
50                         symbol: func_id.as_u32() as usize,
51                         addend: 0,
52                     }),
53                 );
54             }
55             UnwindInfo::WindowsX64(_) => {
56                 // FIXME implement this
57             }
58             unwind_info => unimplemented!("{:?}", unwind_info),
59         }
60     }
61
62     pub(crate) fn emit<P: WriteDebugInfo>(self, product: &mut P) {
63         let mut eh_frame = EhFrame::from(super::emit::WriterRelocate::new(super::target_endian(
64             self.tcx,
65         )));
66         self.frame_table.write_eh_frame(&mut eh_frame).unwrap();
67
68         if !eh_frame.0.writer.slice().is_empty() {
69             let id = eh_frame.id();
70             let section_id = product.add_debug_section(id, eh_frame.0.writer.into_vec());
71             let mut section_map = FxHashMap::default();
72             section_map.insert(id, section_id);
73
74             for reloc in &eh_frame.0.relocs {
75                 product.add_debug_reloc(&section_map, &section_id, reloc);
76             }
77         }
78     }
79
80     #[cfg(feature = "jit")]
81     pub(crate) unsafe fn register_jit(
82         self,
83         jit_module: &cranelift_simplejit::SimpleJITModule,
84     ) -> Option<UnwindRegistry> {
85         let mut eh_frame = EhFrame::from(super::emit::WriterRelocate::new(super::target_endian(
86             self.tcx,
87         )));
88         self.frame_table.write_eh_frame(&mut eh_frame).unwrap();
89
90         if eh_frame.0.writer.slice().is_empty() {
91             return None;
92         }
93
94         let mut eh_frame = eh_frame.0.relocate_for_jit(jit_module);
95
96         // GCC expects a terminating "empty" length, so write a 0 length at the end of the table.
97         eh_frame.extend(&[0, 0, 0, 0]);
98
99         let mut registrations = Vec::new();
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
107             let start = eh_frame.as_ptr();
108             let end = start.add(eh_frame.len());
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);
118                     registrations.push(current as usize);
119                 }
120
121                 // Move to the next table entry (+4 because the length itself is not inclusive)
122                 current = current.add(len + 4);
123             }
124         }
125         #[cfg(not(target_os = "macos"))]
126         {
127             // On other platforms, `__register_frame` will walk the FDEs until an entry of length 0
128             let ptr = eh_frame.as_ptr();
129             __register_frame(ptr);
130             registrations.push(ptr as usize);
131         }
132
133         Some(UnwindRegistry {
134             _frame_table: eh_frame,
135             registrations,
136         })
137     }
138 }
139
140 /// Represents a registry of function unwind information for System V ABI.
141 pub(crate) struct UnwindRegistry {
142     _frame_table: Vec<u8>,
143     registrations: Vec<usize>,
144 }
145
146 extern "C" {
147     // libunwind import
148     fn __register_frame(fde: *const u8);
149     fn __deregister_frame(fde: *const u8);
150 }
151
152 impl Drop for UnwindRegistry {
153     fn drop(&mut self) {
154         unsafe {
155             // libgcc stores the frame entries as a linked list in decreasing sort order
156             // based on the PC value of the registered entry.
157             //
158             // As we store the registrations in increasing order, it would be O(N^2) to
159             // deregister in that order.
160             //
161             // To ensure that we just pop off the first element in the list upon every
162             // deregistration, walk our list of registrations backwards.
163             for fde in self.registrations.iter().rev() {
164                 __deregister_frame(*fde as *const _);
165             }
166         }
167     }
168 }