1 //! Handling of everything related to debuginfo.
10 use cranelift_codegen::ir::Endianness;
11 use cranelift_codegen::isa::TargetIsa;
13 use gimli::write::{Address, AttributeValue, DwarfUnit, LineProgram, LineString, Range, RangeList};
14 use gimli::{Encoding, Format, LineEncoding, RunTimeEndian};
16 pub(crate) use emit::{DebugReloc, DebugRelocName};
17 pub(crate) use unwind::UnwindContext;
19 pub(crate) struct DebugContext<'tcx> {
22 endian: RunTimeEndian,
25 unit_range_list: RangeList,
28 impl<'tcx> DebugContext<'tcx> {
29 pub(crate) fn new(tcx: TyCtxt<'tcx>, isa: &dyn TargetIsa) -> Self {
30 let encoding = Encoding {
31 format: Format::Dwarf32,
32 // FIXME this should be configurable
33 // macOS doesn't seem to support DWARF > 3
34 // 5 version is required for md5 file hash
35 version: if tcx.sess.target.is_like_osx {
38 // FIXME change to version 5 once the gdb and lldb shipping with the latest debian
42 address_size: isa.frontend_config().pointer_bytes(),
45 let endian = match isa.endianness() {
46 Endianness::Little => RunTimeEndian::Little,
47 Endianness::Big => RunTimeEndian::Big,
50 let mut dwarf = DwarfUnit::new(encoding);
52 let producer = format!(
53 "cg_clif (rustc {}, cranelift {})",
54 rustc_interface::util::version_str().unwrap_or("unknown version"),
55 cranelift_codegen::VERSION,
61 .to_string_lossy(FileNameDisplayPreference::Remapped)
63 let (name, file_info) = match tcx.sess.local_crate_source_file.clone() {
65 let name = path.to_string_lossy().into_owned();
68 None => (tcx.crate_name(LOCAL_CRATE).to_string(), None),
71 let mut line_program = LineProgram::new(
73 LineEncoding::default(),
74 LineString::new(comp_dir.as_bytes(), encoding, &mut dwarf.line_strings),
75 LineString::new(name.as_bytes(), encoding, &mut dwarf.line_strings),
78 line_program.file_has_md5 = file_info.is_some();
80 dwarf.unit.line_program = line_program;
83 let name = dwarf.strings.add(name);
84 let comp_dir = dwarf.strings.add(comp_dir);
86 let root = dwarf.unit.root();
87 let root = dwarf.unit.get_mut(root);
88 root.set(gimli::DW_AT_producer, AttributeValue::StringRef(dwarf.strings.add(producer)));
89 root.set(gimli::DW_AT_language, AttributeValue::Language(gimli::DW_LANG_Rust));
90 root.set(gimli::DW_AT_name, AttributeValue::StringRef(name));
91 root.set(gimli::DW_AT_comp_dir, AttributeValue::StringRef(comp_dir));
92 root.set(gimli::DW_AT_low_pc, AttributeValue::Address(Address::Constant(0)));
95 DebugContext { tcx, endian, dwarf, unit_range_list: RangeList(Vec::new()) }
98 pub(crate) fn define_function(
104 source_info_set: &indexmap::IndexSet<SourceInfo>,
106 let symbol = func_id.as_u32() as usize;
108 // FIXME: add to appropriate scope instead of root
109 let scope = self.dwarf.unit.root();
111 let entry_id = self.dwarf.unit.add(scope, gimli::DW_TAG_subprogram);
112 let entry = self.dwarf.unit.get_mut(entry_id);
113 let name_id = self.dwarf.strings.add(name);
114 // Gdb requires DW_AT_name. Otherwise the DW_TAG_subprogram is skipped.
115 entry.set(gimli::DW_AT_name, AttributeValue::StringRef(name_id));
116 entry.set(gimli::DW_AT_linkage_name, AttributeValue::StringRef(name_id));
119 self.create_debug_lines(symbol, entry_id, context, function_span, source_info_set);
121 self.unit_range_list.0.push(Range::StartLength {
122 begin: Address::Symbol { symbol, addend: 0 },
123 length: u64::from(end),
126 let func_entry = self.dwarf.unit.get_mut(entry_id);
127 // Gdb requires both DW_AT_low_pc and DW_AT_high_pc. Otherwise the DW_TAG_subprogram is skipped.
130 AttributeValue::Address(Address::Symbol { symbol, addend: 0 }),
132 // Using Udata for DW_AT_high_pc requires at least DWARF4
133 func_entry.set(gimli::DW_AT_high_pc, AttributeValue::Udata(u64::from(end)));