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
) -> Self {
19 let mut frame_table
= FrameTable
::default();
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
);
26 Some(frame_table
.add_cie(cie
))
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() {
46 UnwindInfo
::SystemV(unwind_info
) => {
47 self.frame_table
.add_fde(
49 unwind_info
.to_fde(Address
::Symbol
{
50 symbol
: func_id
.as_u32() as usize,
55 UnwindInfo
::WindowsX64(_
) => {
56 // FIXME implement this
58 unwind_info
=> unimplemented
!("{:?}", unwind_info
),
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(
66 self.frame_table
.write_eh_frame(&mut eh_frame
).unwrap();
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
);
74 for reloc
in &eh_frame
.0.relocs
{
75 product
.add_debug_reloc(§ion_map
, §ion_id
, reloc
);
80 #[cfg(feature = "jit")]
81 pub(crate) unsafe fn register_jit(
83 jit_module
: &cranelift_simplejit
::SimpleJITModule
,
84 ) -> Option
<UnwindRegistry
> {
85 let mut eh_frame
= EhFrame
::from(super::emit
::WriterRelocate
::new(super::target_endian(
88 self.frame_table
.write_eh_frame(&mut eh_frame
).unwrap();
90 if eh_frame
.0.writer
.slice().is_empty() {
94 let mut eh_frame
= eh_frame
.0.relocate_for_jit(jit_module
);
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]);
99 let mut registrations
= Vec
::new();
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")]
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
;
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;
116 if current
!= start
{
117 __register_frame(current
);
118 registrations
.push(current
as usize);
121 // Move to the next table entry (+4 because the length itself is not inclusive)
122 current
= current
.add(len
+ 4);
125 #[cfg(not(target_os = "macos"))]
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);
133 Some(UnwindRegistry
{
134 _frame_table
: eh_frame
,
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>,
148 fn __register_frame(fde
: *const u8);
149 fn __deregister_frame(fde
: *const u8);
152 impl Drop
for UnwindRegistry
{
155 // libgcc stores the frame entries as a linked list in decreasing sort order
156 // based on the PC value of the registered entry.
158 // As we store the registrations in increasing order, it would be O(N^2) to
159 // deregister in that order.
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 _
);