summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs
blob: 28fbcb15b2b5893ab9ea6912d5047a05a6f293a7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
//! Handling of everything related to debuginfo.

mod emit;
mod line_info;
mod object;
mod unwind;

use crate::prelude::*;

use cranelift_codegen::ir::Endianness;
use cranelift_codegen::isa::TargetIsa;

use gimli::write::{
    Address, AttributeValue, DwarfUnit, FileId, LineProgram, LineString, Range, RangeList,
    UnitEntryId,
};
use gimli::{Encoding, Format, LineEncoding, RunTimeEndian};
use indexmap::IndexSet;

pub(crate) use emit::{DebugReloc, DebugRelocName};
pub(crate) use unwind::UnwindContext;

pub(crate) struct DebugContext {
    endian: RunTimeEndian,

    dwarf: DwarfUnit,
    unit_range_list: RangeList,
}

pub(crate) struct FunctionDebugContext {
    entry_id: UnitEntryId,
    function_source_loc: (FileId, u64, u64),
    source_loc_set: indexmap::IndexSet<(FileId, u64, u64)>,
}

impl DebugContext {
    pub(crate) fn new(tcx: TyCtxt<'_>, isa: &dyn TargetIsa) -> Self {
        let encoding = Encoding {
            format: Format::Dwarf32,
            // FIXME this should be configurable
            // macOS doesn't seem to support DWARF > 3
            // 5 version is required for md5 file hash
            version: if tcx.sess.target.is_like_osx {
                3
            } else {
                // FIXME change to version 5 once the gdb and lldb shipping with the latest debian
                // support it.
                4
            },
            address_size: isa.frontend_config().pointer_bytes(),
        };

        let endian = match isa.endianness() {
            Endianness::Little => RunTimeEndian::Little,
            Endianness::Big => RunTimeEndian::Big,
        };

        let mut dwarf = DwarfUnit::new(encoding);

        let producer = format!(
            "cg_clif (rustc {}, cranelift {})",
            rustc_interface::util::rustc_version_str().unwrap_or("unknown version"),
            cranelift_codegen::VERSION,
        );
        let comp_dir = tcx
            .sess
            .opts
            .working_dir
            .to_string_lossy(FileNameDisplayPreference::Remapped)
            .into_owned();
        let (name, file_info) = match tcx.sess.local_crate_source_file() {
            Some(path) => {
                let name = path.to_string_lossy().into_owned();
                (name, None)
            }
            None => (tcx.crate_name(LOCAL_CRATE).to_string(), None),
        };

        let mut line_program = LineProgram::new(
            encoding,
            LineEncoding::default(),
            LineString::new(comp_dir.as_bytes(), encoding, &mut dwarf.line_strings),
            LineString::new(name.as_bytes(), encoding, &mut dwarf.line_strings),
            file_info,
        );
        line_program.file_has_md5 = file_info.is_some();

        dwarf.unit.line_program = line_program;

        {
            let name = dwarf.strings.add(name);
            let comp_dir = dwarf.strings.add(comp_dir);

            let root = dwarf.unit.root();
            let root = dwarf.unit.get_mut(root);
            root.set(gimli::DW_AT_producer, AttributeValue::StringRef(dwarf.strings.add(producer)));
            root.set(gimli::DW_AT_language, AttributeValue::Language(gimli::DW_LANG_Rust));
            root.set(gimli::DW_AT_name, AttributeValue::StringRef(name));
            root.set(gimli::DW_AT_comp_dir, AttributeValue::StringRef(comp_dir));
            root.set(gimli::DW_AT_low_pc, AttributeValue::Address(Address::Constant(0)));
        }

        DebugContext { endian, dwarf, unit_range_list: RangeList(Vec::new()) }
    }

    pub(crate) fn define_function(
        &mut self,
        tcx: TyCtxt<'_>,
        name: &str,
        function_span: Span,
    ) -> FunctionDebugContext {
        let (file, line, column) = DebugContext::get_span_loc(tcx, function_span, function_span);

        let file_id = self.add_source_file(&file);

        // FIXME: add to appropriate scope instead of root
        let scope = self.dwarf.unit.root();

        let entry_id = self.dwarf.unit.add(scope, gimli::DW_TAG_subprogram);
        let entry = self.dwarf.unit.get_mut(entry_id);
        let name_id = self.dwarf.strings.add(name);
        // Gdb requires DW_AT_name. Otherwise the DW_TAG_subprogram is skipped.
        entry.set(gimli::DW_AT_name, AttributeValue::StringRef(name_id));
        entry.set(gimli::DW_AT_linkage_name, AttributeValue::StringRef(name_id));

        entry.set(gimli::DW_AT_decl_file, AttributeValue::FileIndex(Some(file_id)));
        entry.set(gimli::DW_AT_decl_line, AttributeValue::Udata(line));
        entry.set(gimli::DW_AT_decl_column, AttributeValue::Udata(column));

        FunctionDebugContext {
            entry_id,
            function_source_loc: (file_id, line, column),
            source_loc_set: IndexSet::new(),
        }
    }
}

impl FunctionDebugContext {
    pub(crate) fn finalize(
        mut self,
        debug_context: &mut DebugContext,
        func_id: FuncId,
        context: &Context,
    ) {
        let symbol = func_id.as_u32() as usize;

        let end = self.create_debug_lines(debug_context, symbol, context);

        debug_context.unit_range_list.0.push(Range::StartLength {
            begin: Address::Symbol { symbol, addend: 0 },
            length: u64::from(end),
        });

        let func_entry = debug_context.dwarf.unit.get_mut(self.entry_id);
        // Gdb requires both DW_AT_low_pc and DW_AT_high_pc. Otherwise the DW_TAG_subprogram is skipped.
        func_entry.set(
            gimli::DW_AT_low_pc,
            AttributeValue::Address(Address::Symbol { symbol, addend: 0 }),
        );
        // Using Udata for DW_AT_high_pc requires at least DWARF4
        func_entry.set(gimli::DW_AT_high_pc, AttributeValue::Udata(u64::from(end)));
    }
}