1 //! Unwind info generation (`.eh_frame`)
5 use cranelift_codegen
::isa
::{unwind::UnwindInfo, TargetIsa}
;
7 use cranelift_object
::ObjectProduct
;
8 use gimli
::write
::{Address, CieId, EhFrame, FrameTable, Section}
;
9 use gimli
::RunTimeEndian
;
11 use super::object
::WriteDebugInfo
;
13 pub(crate) struct UnwindContext
{
14 endian
: RunTimeEndian
,
15 frame_table
: FrameTable
,
16 cie_id
: Option
<CieId
>,
20 pub(crate) fn new(tcx
: TyCtxt
<'_
>, isa
: &dyn TargetIsa
, pic_eh_frame
: bool
) -> Self {
21 let endian
= super::target_endian(tcx
);
22 let mut frame_table
= FrameTable
::default();
24 let cie_id
= if let Some(mut cie
) = isa
.create_systemv_cie() {
26 cie
.fde_address_encoding
=
27 gimli
::DwEhPe(gimli
::DW_EH_PE_pcrel
.0 | gimli
::DW_EH_PE_sdata4
.0
);
29 Some(frame_table
.add_cie(cie
))
34 UnwindContext { endian, frame_table, cie_id }
37 pub(crate) fn add_function(&mut self, func_id
: FuncId
, context
: &Context
, isa
: &dyn TargetIsa
) {
38 let unwind_info
= if let Some(unwind_info
) = context
.create_unwind_info(isa
).unwrap() {
45 UnwindInfo
::SystemV(unwind_info
) => {
46 self.frame_table
.add_fde(
49 .to_fde(Address
::Symbol { symbol: func_id.as_u32() as usize, addend: 0 }
),
52 UnwindInfo
::WindowsX64(_
) => {
53 // FIXME implement this
55 unwind_info
=> unimplemented
!("{:?}", unwind_info
),
59 pub(crate) fn emit(self, product
: &mut ObjectProduct
) {
60 let mut eh_frame
= EhFrame
::from(super::emit
::WriterRelocate
::new(self.endian
));
61 self.frame_table
.write_eh_frame(&mut eh_frame
).unwrap();
63 if !eh_frame
.0.writer
.slice().is_empty() {
64 let id
= eh_frame
.id();
65 let section_id
= product
.add_debug_section(id
, eh_frame
.0.writer
.into_vec());
66 let mut section_map
= FxHashMap
::default();
67 section_map
.insert(id
, section_id
);
69 for reloc
in &eh_frame
.0.relocs
{
70 product
.add_debug_reloc(§ion_map
, §ion_id
, reloc
);
75 #[cfg(all(feature = "jit", windows))]
76 pub(crate) unsafe fn register_jit(self, _jit_module
: &cranelift_jit
::JITModule
) {}
78 #[cfg(all(feature = "jit", not(windows)))]
79 pub(crate) unsafe fn register_jit(self, jit_module
: &cranelift_jit
::JITModule
) {
80 let mut eh_frame
= EhFrame
::from(super::emit
::WriterRelocate
::new(self.endian
));
81 self.frame_table
.write_eh_frame(&mut eh_frame
).unwrap();
83 if eh_frame
.0.writer
.slice().is_empty() {
87 let mut eh_frame
= eh_frame
.0.relocate_for_jit(jit_module
);
89 // GCC expects a terminating "empty" length, so write a 0 length at the end of the table.
90 eh_frame
.extend(&[0, 0, 0, 0]);
92 // FIXME support unregistering unwind tables once cranelift-jit supports deallocating
93 // individual functions
94 #[allow(unused_variables)]
95 let (eh_frame
, eh_frame_len
, _
) = Vec
::into_raw_parts(eh_frame
);
97 // =======================================================================
98 // Everything after this line up to the end of the file is loosly based on
99 // https://github.com/bytecodealliance/wasmtime/blob/4471a82b0c540ff48960eca6757ccce5b1b5c3e4/crates/jit/src/unwind/systemv.rs
100 #[cfg(target_os = "macos")]
102 // On macOS, `__register_frame` takes a pointer to a single FDE
103 let start
= eh_frame
;
104 let end
= start
.add(eh_frame_len
);
105 let mut current
= start
;
107 // Walk all of the entries in the frame table and register them
108 while current
< end
{
109 let len
= std
::ptr
::read
::<u32>(current
as *const u32) as usize;
112 if current
!= start
{
113 __register_frame(current
);
116 // Move to the next table entry (+4 because the length itself is not inclusive)
117 current
= current
.add(len
+ 4);
120 #[cfg(not(target_os = "macos"))]
122 // On other platforms, `__register_frame` will walk the FDEs until an entry of length 0
123 __register_frame(eh_frame
);
130 fn __register_frame(fde
: *const u8);