1 //! Unwind info generation (`.eh_frame`)
5 use cranelift_codegen
::isa
::{unwind::UnwindInfo, TargetIsa}
;
7 use gimli
::write
::{Address, CieId, EhFrame, FrameTable, Section}
;
9 use crate::backend
::WriteDebugInfo
;
11 pub(crate) struct UnwindContext
<'tcx
> {
13 frame_table
: FrameTable
,
14 cie_id
: Option
<CieId
>,
17 impl<'tcx
> UnwindContext
<'tcx
> {
18 pub(crate) fn new(tcx
: TyCtxt
<'tcx
>, isa
: &dyn TargetIsa
, pic_eh_frame
: bool
) -> Self {
19 let mut frame_table
= FrameTable
::default();
21 let cie_id
= if let Some(mut cie
) = isa
.create_systemv_cie() {
23 cie
.fde_address_encoding
=
24 gimli
::DwEhPe(gimli
::DW_EH_PE_pcrel
.0 | gimli
::DW_EH_PE_sdata4
.0
);
26 Some(frame_table
.add_cie(cie
))
31 UnwindContext { tcx, frame_table, cie_id }
34 pub(crate) fn add_function(&mut self, func_id
: FuncId
, context
: &Context
, isa
: &dyn TargetIsa
) {
35 let unwind_info
= if let Some(unwind_info
) = context
.create_unwind_info(isa
).unwrap() {
42 UnwindInfo
::SystemV(unwind_info
) => {
43 self.frame_table
.add_fde(
46 .to_fde(Address
::Symbol { symbol: func_id.as_u32() as usize, addend: 0 }
),
49 UnwindInfo
::WindowsX64(_
) => {
50 // FIXME implement this
52 unwind_info
=> unimplemented
!("{:?}", unwind_info
),
56 pub(crate) fn emit
<P
: WriteDebugInfo
>(self, product
: &mut P
) {
58 EhFrame
::from(super::emit
::WriterRelocate
::new(super::target_endian(self.tcx
)));
59 self.frame_table
.write_eh_frame(&mut eh_frame
).unwrap();
61 if !eh_frame
.0.writer
.slice().is_empty() {
62 let id
= eh_frame
.id();
63 let section_id
= product
.add_debug_section(id
, eh_frame
.0.writer
.into_vec());
64 let mut section_map
= FxHashMap
::default();
65 section_map
.insert(id
, section_id
);
67 for reloc
in &eh_frame
.0.relocs
{
68 product
.add_debug_reloc(§ion_map
, §ion_id
, reloc
);
73 #[cfg(feature = "jit")]
74 pub(crate) unsafe fn register_jit(
76 jit_module
: &cranelift_jit
::JITModule
,
77 ) -> Option
<UnwindRegistry
> {
79 EhFrame
::from(super::emit
::WriterRelocate
::new(super::target_endian(self.tcx
)));
80 self.frame_table
.write_eh_frame(&mut eh_frame
).unwrap();
82 if eh_frame
.0.writer
.slice().is_empty() {
86 let mut eh_frame
= eh_frame
.0.relocate_for_jit(jit_module
);
88 // GCC expects a terminating "empty" length, so write a 0 length at the end of the table.
89 eh_frame
.extend(&[0, 0, 0, 0]);
91 let mut registrations
= Vec
::new();
93 // =======================================================================
94 // Everything after this line up to the end of the file is loosly based on
95 // https://github.com/bytecodealliance/wasmtime/blob/4471a82b0c540ff48960eca6757ccce5b1b5c3e4/crates/jit/src/unwind/systemv.rs
96 #[cfg(target_os = "macos")]
98 // On macOS, `__register_frame` takes a pointer to a single FDE
99 let start
= eh_frame
.as_ptr();
100 let end
= start
.add(eh_frame
.len());
101 let mut current
= start
;
103 // Walk all of the entries in the frame table and register them
104 while current
< end
{
105 let len
= std
::ptr
::read
::<u32>(current
as *const u32) as usize;
108 if current
!= start
{
109 __register_frame(current
);
110 registrations
.push(current
as usize);
113 // Move to the next table entry (+4 because the length itself is not inclusive)
114 current
= current
.add(len
+ 4);
117 #[cfg(not(target_os = "macos"))]
119 // On other platforms, `__register_frame` will walk the FDEs until an entry of length 0
120 let ptr
= eh_frame
.as_ptr();
121 __register_frame(ptr
);
122 registrations
.push(ptr
as usize);
125 Some(UnwindRegistry { _frame_table: eh_frame, registrations }
)
129 /// Represents a registry of function unwind information for System V ABI.
130 pub(crate) struct UnwindRegistry
{
131 _frame_table
: Vec
<u8>,
132 registrations
: Vec
<usize>,
137 fn __register_frame(fde
: *const u8);
138 fn __deregister_frame(fde
: *const u8);
141 impl Drop
for UnwindRegistry
{
144 // libgcc stores the frame entries as a linked list in decreasing sort order
145 // based on the PC value of the registered entry.
147 // As we store the registrations in increasing order, it would be O(N^2) to
148 // deregister in that order.
150 // To ensure that we just pop off the first element in the list upon every
151 // deregistration, walk our list of registrations backwards.
152 for fde
in self.registrations
.iter().rev() {
153 __deregister_frame(*fde
as *const _
);