compiler/rustc_codegen_cranelift/src/debuginfo/unwind.rs RUST 317 lines View on github.com → Search inside
1//! Unwind info generation (`.eh_frame`)23use cranelift_codegen::FinalizedMachExceptionHandler;4use cranelift_codegen::ir::Endianness;5use cranelift_codegen::isa::unwind::UnwindInfo;6use cranelift_module::DataId;7use cranelift_object::ObjectProduct;8use gimli::write::{Address, CieId, EhFrame, FrameTable, Section};9use gimli::{Encoding, Format, RunTimeEndian};1011use super::emit::{DebugRelocName, address_for_data, address_for_func};12use super::gcc_except_table::{13    Action, ActionKind, ActionTable, CallSite, CallSiteTable, GccExceptTable, TypeInfoTable,14};15use super::object::WriteDebugInfo;16use crate::prelude::*;1718pub(crate) const EXCEPTION_HANDLER_CLEANUP: u32 = 0;19pub(crate) const EXCEPTION_HANDLER_CATCH: u32 = 1;2021pub(crate) struct UnwindContext {22    endian: RunTimeEndian,23    frame_table: FrameTable,24    cie_id: Option<CieId>,25}2627impl UnwindContext {28    pub(crate) fn new(module: &mut dyn Module, pic_eh_frame: bool) -> Self {29        let endian = match module.isa().endianness() {30            Endianness::Little => RunTimeEndian::Little,31            Endianness::Big => RunTimeEndian::Big,32        };33        let mut frame_table = FrameTable::default();3435        let cie_id = if let Some(mut cie) = module.isa().create_systemv_cie() {36            let ptr_encoding = if pic_eh_frame {37                gimli::DwEhPe(gimli::DW_EH_PE_pcrel.0 | gimli::DW_EH_PE_sdata4.0)38            } else {39                gimli::DW_EH_PE_absptr40            };4142            cie.fde_address_encoding = ptr_encoding;4344            // FIXME only add personality function and lsda when necessary: https://github.com/rust-lang/rust/blob/1f76d219c906f0112bb1872f33aa977164c53fa6/compiler/rustc_codegen_ssa/src/mir/mod.rs#L200-L20445            if cfg!(feature = "unwinding") {46                let code_ptr_encoding = if pic_eh_frame {47                    if module.isa().triple().architecture == target_lexicon::Architecture::X86_64 {48                        gimli::DwEhPe(49                            gimli::DW_EH_PE_indirect.050                                | gimli::DW_EH_PE_pcrel.051                                | gimli::DW_EH_PE_sdata4.0,52                        )53                    } else if let target_lexicon::Architecture::Aarch64(_) =54                        module.isa().triple().architecture55                    {56                        gimli::DwEhPe(57                            gimli::DW_EH_PE_indirect.058                                | gimli::DW_EH_PE_pcrel.059                                | gimli::DW_EH_PE_sdata8.0,60                        )61                    } else {62                        todo!()63                    }64                } else {65                    gimli::DwEhPe(gimli::DW_EH_PE_indirect.0 | gimli::DW_EH_PE_absptr.0)66                };6768                cie.lsda_encoding = Some(ptr_encoding);6970                // FIXME use eh_personality lang item instead71                let personality = module72                    .declare_function(73                        "rust_eh_personality",74                        Linkage::Import,75                        &Signature {76                            params: vec![77                                AbiParam::new(types::I32),78                                AbiParam::new(types::I32),79                                AbiParam::new(types::I64),80                                AbiParam::new(module.target_config().pointer_type()),81                                AbiParam::new(module.target_config().pointer_type()),82                            ],83                            returns: vec![AbiParam::new(types::I32)],84                            call_conv: module.target_config().default_call_conv,85                        },86                    )87                    .unwrap();8889                // Use indirection here to support PIC the case where rust_eh_personality is defined in90                // another DSO.91                let personality_ref = module92                    .declare_data("DW.ref.rust_eh_personality", Linkage::Local, false, false)93                    .unwrap();9495                let mut personality_ref_data = DataDescription::new();96                // Note: Must not use define_zeroinit. The unwinder can't handle this being in the .bss97                // section.98                let pointer_bytes = usize::from(module.target_config().pointer_bytes());99                personality_ref_data.define(vec![0; pointer_bytes].into_boxed_slice());100                let personality_func_ref =101                    module.declare_func_in_data(personality, &mut personality_ref_data);102                personality_ref_data.write_function_addr(0, personality_func_ref);103104                module.define_data(personality_ref, &personality_ref_data).unwrap();105106                cie.personality = Some((code_ptr_encoding, address_for_data(personality_ref)));107            }108109            Some(frame_table.add_cie(cie))110        } else {111            None112        };113114        UnwindContext { endian, frame_table, cie_id }115    }116117    pub(crate) fn add_function(118        &mut self,119        module: &mut dyn Module,120        func_id: FuncId,121        context: &Context,122    ) {123        let triple = module.isa().triple();124        if matches!(triple.operating_system, target_lexicon::OperatingSystem::MacOSX { .. })125            && triple.architecture == target_lexicon::Architecture::X86_64126        {127            // The object crate doesn't currently support DW_GNU_EH_PE_absptr, which macOS128            // requires for unwinding tables. See gimli-rs/object#415.129            return;130        }131132        let Some(unwind_info) =133            context.compiled_code().unwrap().create_unwind_info(module.isa()).unwrap()134        else {135            return;136        };137138        match unwind_info {139            UnwindInfo::SystemV(unwind_info) => {140                let mut fde = unwind_info.to_fde(address_for_func(func_id));141142                // FIXME only add personality function and lsda when necessary: https://github.com/rust-lang/rust/blob/1f76d219c906f0112bb1872f33aa977164c53fa6/compiler/rustc_codegen_ssa/src/mir/mod.rs#L200-L204143                if cfg!(feature = "unwinding") {144                    // FIXME use unique symbol name derived from function name145                    let lsda = module.declare_anonymous_data(false, false).unwrap();146147                    let encoding = Encoding {148                        format: Format::Dwarf32,149                        version: 1,150                        address_size: module.isa().frontend_config().pointer_bytes(),151                    };152153                    let mut gcc_except_table_data = GccExceptTable {154                        call_sites: CallSiteTable(vec![]),155                        actions: ActionTable::new(),156                        type_info: TypeInfoTable::new(gimli::DW_EH_PE_udata4),157                    };158159                    let catch_type = gcc_except_table_data.type_info.add(Address::Constant(0));160                    let catch_action = gcc_except_table_data161                        .actions162                        .add(Action { kind: ActionKind::Catch(catch_type), next_action: None });163164                    for call_site in context.compiled_code().unwrap().buffer.call_sites() {165                        if call_site.exception_handlers.is_empty() {166                            gcc_except_table_data.call_sites.0.push(CallSite {167                                start: u64::from(call_site.ret_addr - 1),168                                length: 1,169                                landing_pad: 0,170                                action_entry: None,171                            });172                        }173                        for &handler in call_site.exception_handlers {174                            match handler {175                                FinalizedMachExceptionHandler::Tag(tag, landingpad) => {176                                    match tag.as_u32() {177                                        EXCEPTION_HANDLER_CLEANUP => {178                                            gcc_except_table_data.call_sites.0.push(CallSite {179                                                start: u64::from(call_site.ret_addr - 1),180                                                length: 1,181                                                landing_pad: u64::from(landingpad),182                                                action_entry: None,183                                            })184                                        }185                                        EXCEPTION_HANDLER_CATCH => {186                                            gcc_except_table_data.call_sites.0.push(CallSite {187                                                start: u64::from(call_site.ret_addr - 1),188                                                length: 1,189                                                landing_pad: u64::from(landingpad),190                                                action_entry: Some(catch_action),191                                            })192                                        }193                                        _ => unreachable!(),194                                    }195                                }196                                _ => unreachable!(),197                            }198                        }199                    }200201                    let mut gcc_except_table = super::emit::WriterRelocate::new(self.endian);202203                    gcc_except_table_data.write(&mut gcc_except_table, encoding).unwrap();204205                    let mut data = DataDescription::new();206                    data.define(gcc_except_table.writer.into_vec().into_boxed_slice());207                    data.set_segment_section("", ".gcc_except_table");208209                    for reloc in &gcc_except_table.relocs {210                        match reloc.name {211                            DebugRelocName::Section(_id) => unreachable!(),212                            DebugRelocName::Symbol(id) => {213                                let id = id.try_into().unwrap();214                                if id & 1 << 31 == 0 {215                                    let func_ref = module216                                        .declare_func_in_data(FuncId::from_u32(id), &mut data);217                                    data.write_function_addr(reloc.offset, func_ref);218                                } else {219                                    let gv = module.declare_data_in_data(220                                        DataId::from_u32(id & !(1 << 31)),221                                        &mut data,222                                    );223                                    data.write_data_addr(reloc.offset, gv, 0);224                                }225                            }226                        };227                    }228229                    module.define_data(lsda, &data).unwrap();230                    fde.lsda = Some(address_for_data(lsda));231                }232233                self.frame_table.add_fde(self.cie_id.unwrap(), fde);234            }235            UnwindInfo::WindowsX64(_) | UnwindInfo::WindowsArm64(_) => {236                // Windows does not have debug info for its unwind info.237            }238            unwind_info => unimplemented!("{:?}", unwind_info),239        }240    }241242    pub(crate) fn emit(self, product: &mut ObjectProduct) {243        let mut eh_frame = EhFrame::from(super::emit::WriterRelocate::new(self.endian));244        self.frame_table.write_eh_frame(&mut eh_frame).unwrap();245246        if !eh_frame.0.writer.slice().is_empty() {247            let id = eh_frame.id();248            let section_id = product.add_debug_section(id, eh_frame.0.writer.into_vec());249            let mut section_map = FxHashMap::default();250            section_map.insert(id, section_id);251252            let use_section_symbol = product.object.format() != object::BinaryFormat::MachO;253            for reloc in &eh_frame.0.relocs {254                product.add_debug_reloc(&section_map, &section_id, reloc, use_section_symbol);255            }256        }257    }258259    #[cfg(all(feature = "jit", windows))]260    pub(crate) unsafe fn register_jit(self, _jit_module: &cranelift_jit::JITModule) {}261262    #[cfg(all(feature = "jit", not(windows)))]263    pub(crate) unsafe fn register_jit(self, jit_module: &cranelift_jit::JITModule) {264        use std::mem::ManuallyDrop;265266        let mut eh_frame = EhFrame::from(super::emit::WriterRelocate::new(self.endian));267        self.frame_table.write_eh_frame(&mut eh_frame).unwrap();268269        if eh_frame.0.writer.slice().is_empty() {270            return;271        }272273        let mut eh_frame = eh_frame.0.relocate_for_jit(jit_module);274275        // GCC expects a terminating "empty" length, so write a 0 length at the end of the table.276        eh_frame.extend(&[0, 0, 0, 0]);277278        // FIXME support unregistering unwind tables once cranelift-jit supports deallocating279        // individual functions280        let eh_frame = ManuallyDrop::new(eh_frame);281282        // =======================================================================283        // Everything after this line up to the end of the file is loosely based on284        // https://github.com/bytecodealliance/wasmtime/blob/4471a82b0c540ff48960eca6757ccce5b1b5c3e4/crates/jit/src/unwind/systemv.rs285        #[cfg(target_os = "macos")]286        unsafe {287            // On macOS, `__register_frame` takes a pointer to a single FDE288            let start = eh_frame.as_ptr();289            let end = start.add(eh_frame.len());290            let mut current = start;291292            // Walk all of the entries in the frame table and register them293            while current < end {294                let len = std::ptr::read::<u32>(current as *const u32) as usize;295296                // Skip over the CIE297                if current != start {298                    __register_frame(current);299                }300301                // Move to the next table entry (+4 because the length itself is not inclusive)302                current = current.add(len + 4);303            }304        }305        #[cfg(not(target_os = "macos"))]306        {307            // On other platforms, `__register_frame` will walk the FDEs until an entry of length 0308            unsafe { __register_frame(eh_frame.as_ptr()) };309        }310    }311}312313unsafe extern "C" {314    // libunwind import315    fn __register_frame(fde: *const u8);316}

Code quality findings 23

Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
pub(crate) unsafe fn register_jit(self, _jit_module: &cranelift_jit::JITModule) {}
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
pub(crate) unsafe fn register_jit(self, jit_module: &cranelift_jit::JITModule) {
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
unsafe {
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
unsafe { __register_frame(eh_frame.as_ptr()) };
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
unsafe extern "C" {
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
.unwrap();
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
.unwrap();
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
module.define_data(personality_ref, &personality_ref_data).unwrap();
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
context.compiled_code().unwrap().create_unwind_info(module.isa()).unwrap()
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
let lsda = module.declare_anonymous_data(false, false).unwrap();
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
for call_site in context.compiled_code().unwrap().buffer.call_sites() {
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
gcc_except_table_data.write(&mut gcc_except_table, encoding).unwrap();
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
let id = id.try_into().unwrap();
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
module.define_data(lsda, &data).unwrap();
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
self.frame_table.add_fde(self.cie_id.unwrap(), fde);
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
self.frame_table.write_eh_frame(&mut eh_frame).unwrap();
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
self.frame_table.write_eh_frame(&mut eh_frame).unwrap();
Info: Wildcard imports (`use some::path::*;`) can obscure the origin of names and lead to conflicts. Prefer importing specific items explicitly.
info maintainability wildcard-import
use crate::prelude::*;
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
todo!()
Performance Info: Calling .push() repeatedly inside a loop without prior capacity reservation can lead to multiple reallocations. Consider using `Vec::with_capacity(n)` or `vec.reserve(n)` if the approximate number of elements is known.
info performance push-without-reserve
gcc_except_table_data.call_sites.0.push(CallSite {
Performance Info: Calling .push() repeatedly inside a loop without prior capacity reservation can lead to multiple reallocations. Consider using `Vec::with_capacity(n)` or `vec.reserve(n)` if the approximate number of elements is known.
info performance push-without-reserve
gcc_except_table_data.call_sites.0.push(CallSite {
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
unwind_info => unimplemented!("{:?}", unwind_info),
Info: Use of raw pointers (*const T, *mut T) typically requires 'unsafe' blocks for dereferencing. Ensure usage is justified (FFI, low-level optimizations) and memory safety is manually upheld.
info safety raw-pointer
let len = std::ptr::read::<u32>(current as *const u32) as usize;

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.