]> git.lizzy.rs Git - rust.git/blob - src/debuginfo/mod.rs
Implement local reading for locals on stack
[rust.git] / src / debuginfo / mod.rs
1 mod emit;
2 mod line_info;
3
4 use crate::prelude::*;
5
6 use cranelift::codegen::ir::{StackSlots, ValueLoc};
7 use cranelift::codegen::isa::RegUnit;
8
9 use gimli::write::{
10     self, Address, AttributeValue, DwarfUnit, Expression, LineProgram, LineString, Location,
11     LocationList, Range, RangeList, UnitEntryId, Writer,
12 };
13 use gimli::{Encoding, Format, LineEncoding, Register, RunTimeEndian, X86_64};
14
15 pub use emit::{DebugReloc, DebugRelocName};
16
17 fn target_endian(tcx: TyCtxt) -> RunTimeEndian {
18     use rustc::ty::layout::Endian;
19
20     match tcx.data_layout.endian {
21         Endian::Big => RunTimeEndian::Big,
22         Endian::Little => RunTimeEndian::Little,
23     }
24 }
25
26 pub struct DebugContext<'tcx> {
27     tcx: TyCtxt<'tcx>,
28
29     endian: RunTimeEndian,
30     symbols: indexmap::IndexMap<FuncId, String>,
31
32     dwarf: DwarfUnit,
33     unit_range_list: RangeList,
34
35     types: HashMap<Ty<'tcx>, UnitEntryId>,
36 }
37
38 impl<'tcx> DebugContext<'tcx> {
39     pub fn new(tcx: TyCtxt<'tcx>, address_size: u8) -> Self {
40         let encoding = Encoding {
41             format: Format::Dwarf32,
42             // TODO: this should be configurable
43             // macOS doesn't seem to support DWARF > 3
44             version: 3,
45             address_size,
46         };
47
48         let mut dwarf = DwarfUnit::new(encoding);
49
50         // FIXME: how to get version when building out of tree?
51         // Normally this would use option_env!("CFG_VERSION").
52         let producer = format!("cranelift fn (rustc version {})", "unknown version");
53         let comp_dir = tcx.sess.working_dir.0.to_string_lossy().into_owned();
54         let name = match tcx.sess.local_crate_source_file {
55             Some(ref path) => path.to_string_lossy().into_owned(),
56             None => tcx.crate_name(LOCAL_CRATE).to_string(),
57         };
58
59         let line_program = LineProgram::new(
60             encoding,
61             LineEncoding::default(),
62             LineString::new(comp_dir.as_bytes(), encoding, &mut dwarf.line_strings),
63             LineString::new(name.as_bytes(), encoding, &mut dwarf.line_strings),
64             None,
65         );
66         dwarf.unit.line_program = line_program;
67
68         {
69             let name = dwarf.strings.add(&*name);
70             let comp_dir = dwarf.strings.add(&*comp_dir);
71
72             let root = dwarf.unit.root();
73             let root = dwarf.unit.get_mut(root);
74             root.set(
75                 gimli::DW_AT_producer,
76                 AttributeValue::StringRef(dwarf.strings.add(producer)),
77             );
78             root.set(
79                 gimli::DW_AT_language,
80                 AttributeValue::Language(gimli::DW_LANG_Rust),
81             );
82             root.set(gimli::DW_AT_name, AttributeValue::StringRef(name));
83             root.set(gimli::DW_AT_comp_dir, AttributeValue::StringRef(comp_dir));
84             root.set(
85                 gimli::DW_AT_low_pc,
86                 AttributeValue::Address(Address::Constant(0)),
87             );
88         }
89
90         DebugContext {
91             tcx,
92
93             endian: target_endian(tcx),
94             symbols: indexmap::IndexMap::new(),
95
96             dwarf,
97             unit_range_list: RangeList(Vec::new()),
98
99             types: HashMap::new(),
100         }
101     }
102
103     fn dwarf_ty(&mut self, ty: Ty<'tcx>) -> UnitEntryId {
104         if let Some(type_id) = self.types.get(ty) {
105             return *type_id;
106         }
107
108         let new_entry = |dwarf: &mut DwarfUnit, tag| dwarf.unit.add(dwarf.unit.root(), tag);
109
110         let primtive = |dwarf: &mut DwarfUnit, ate| {
111             let type_id = new_entry(dwarf, gimli::DW_TAG_base_type);
112             let type_entry = dwarf.unit.get_mut(type_id);
113             type_entry.set(gimli::DW_AT_encoding, AttributeValue::Encoding(ate));
114             type_id
115         };
116
117         let type_id = match ty.kind {
118             ty::Bool => primtive(&mut self.dwarf, gimli::DW_ATE_boolean),
119             ty::Char => primtive(&mut self.dwarf, gimli::DW_ATE_UTF),
120             ty::Uint(_) => primtive(&mut self.dwarf, gimli::DW_ATE_unsigned),
121             ty::Int(_) => primtive(&mut self.dwarf, gimli::DW_ATE_signed),
122             ty::Float(_) => primtive(&mut self.dwarf, gimli::DW_ATE_float),
123             ty::Ref(_, pointee_ty, mutbl)
124             | ty::RawPtr(ty::TypeAndMut {
125                 ty: pointee_ty,
126                 mutbl,
127             }) => {
128                 let type_id = new_entry(&mut self.dwarf, gimli::DW_TAG_pointer_type);
129
130                 // Ensure that type is inserted before recursing to avoid duplicates
131                 self.types.insert(ty, type_id);
132
133                 let pointee = self.dwarf_ty(pointee_ty);
134
135                 let type_entry = self.dwarf.unit.get_mut(type_id);
136
137                 //type_entry.set(gimli::DW_AT_mutable, AttributeValue::Flag(mutbl == rustc::hir::Mutability::MutMutable));
138                 type_entry.set(gimli::DW_AT_type, AttributeValue::ThisUnitEntryRef(pointee));
139
140                 type_id
141             }
142             _ => new_entry(&mut self.dwarf, gimli::DW_TAG_structure_type),
143         };
144         let name = format!("{}", ty);
145         let layout = self.tcx.layout_of(ParamEnv::reveal_all().and(ty)).unwrap();
146
147         let type_entry = self.dwarf.unit.get_mut(type_id);
148
149         type_entry.set(gimli::DW_AT_name, AttributeValue::String(name.into_bytes()));
150         type_entry.set(
151             gimli::DW_AT_byte_size,
152             AttributeValue::Udata(layout.size.bytes()),
153         );
154
155         self.types.insert(ty, type_id);
156
157         type_id
158     }
159 }
160
161 pub struct FunctionDebugContext<'a, 'tcx> {
162     debug_context: &'a mut DebugContext<'tcx>,
163     entry_id: UnitEntryId,
164     symbol: usize,
165     instance: Instance<'tcx>,
166     mir: &'tcx mir::Body<'tcx>,
167 }
168
169 impl<'a, 'tcx> FunctionDebugContext<'a, 'tcx> {
170     pub fn new(
171         debug_context: &'a mut DebugContext<'tcx>,
172         instance: Instance<'tcx>,
173         func_id: FuncId,
174         name: &str,
175     ) -> Self {
176         let mir = debug_context.tcx.instance_mir(instance.def);
177
178         let (symbol, _) = debug_context.symbols.insert_full(func_id, name.to_string());
179
180         // FIXME: add to appropriate scope intead of root
181         let scope = debug_context.dwarf.unit.root();
182
183         let entry_id = debug_context
184             .dwarf
185             .unit
186             .add(scope, gimli::DW_TAG_subprogram);
187         let entry = debug_context.dwarf.unit.get_mut(entry_id);
188         let name_id = debug_context.dwarf.strings.add(name);
189         entry.set(
190             gimli::DW_AT_linkage_name,
191             AttributeValue::StringRef(name_id),
192         );
193
194         FunctionDebugContext {
195             debug_context,
196             entry_id,
197             symbol,
198             instance,
199             mir,
200         }
201     }
202
203     fn define_local(&mut self, local: mir::Local) -> UnitEntryId {
204         let local_decl = &self.mir.local_decls[local];
205
206         let ty = self.debug_context.tcx.subst_and_normalize_erasing_regions(
207             self.instance.substs,
208             ty::ParamEnv::reveal_all(),
209             &local_decl.ty,
210         );
211         let dw_ty = self.debug_context.dwarf_ty(ty);
212
213         let name = if let Some(name) = local_decl.name {
214             format!("{}{:?}", name.as_str(), local)
215         } else {
216             format!("{:?}", local)
217         };
218
219         let var_id = self
220             .debug_context
221             .dwarf
222             .unit
223             .add(self.entry_id, gimli::DW_TAG_variable);
224         let var_entry = self.debug_context.dwarf.unit.get_mut(var_id);
225
226         var_entry.set(gimli::DW_AT_name, AttributeValue::String(name.into_bytes()));
227         var_entry.set(gimli::DW_AT_type, AttributeValue::ThisUnitEntryRef(dw_ty));
228
229         var_id
230     }
231
232     pub fn define(
233         &mut self,
234         context: &Context,
235         isa: &dyn cranelift::codegen::isa::TargetIsa,
236         source_info_set: &indexmap::IndexSet<(Span, mir::SourceScope)>,
237         local_map: HashMap<mir::Local, CPlace<'tcx>>,
238     ) {
239         let end = self.create_debug_lines(context, isa, source_info_set);
240
241         self.debug_context
242             .unit_range_list
243             .0
244             .push(Range::StartLength {
245                 begin: Address::Symbol {
246                     symbol: self.symbol,
247                     addend: 0,
248                 },
249                 length: end as u64,
250             });
251
252         let value_labels_ranges = context.build_value_labels_ranges(isa).unwrap();
253
254         for (local, _local_decl) in self.mir.local_decls.iter_enumerated() {
255             let var_id = self.define_local(local);
256             let value_label = cranelift::codegen::ir::ValueLabel::from_u32(local.as_u32());
257
258             let location = match local_map[&local].inner() {
259                 CPlaceInner::Var(_) => {
260                     if let Some(value_loc_ranges) = value_labels_ranges.get(&value_label) {
261                         let loc_list = LocationList(
262                             value_loc_ranges
263                                 .iter()
264                                 .map(|value_loc_range| Location::StartEnd {
265                                     begin: Address::Symbol {
266                                         symbol: self.symbol,
267                                         addend: i64::from(value_loc_range.start),
268                                     },
269                                     end: Address::Symbol {
270                                         symbol: self.symbol,
271                                         addend: i64::from(value_loc_range.end),
272                                     },
273                                     data: Expression(
274                                         translate_loc(value_loc_range.loc, &context.func.stack_slots).unwrap(),
275                                     ),
276                                 })
277                                 .collect(),
278                         );
279                         let loc_list_id = self.debug_context.dwarf.unit.locations.add(loc_list);
280
281                         AttributeValue::LocationListRef(loc_list_id)
282                     } else {
283                         // FIXME set value labels for unused locals
284
285                         AttributeValue::Exprloc(Expression(vec![]))
286                     }
287                 }
288                 CPlaceInner::Addr(_, _) => {
289                     // FIXME implement this (used by arguments and returns)
290
291                     AttributeValue::Exprloc(Expression(vec![]))
292                 }
293                 CPlaceInner::Stack(stack_slot) => {
294                     AttributeValue::Exprloc(Expression(translate_loc(ValueLoc::Stack(*stack_slot), &context.func.stack_slots).unwrap()))
295                 }
296                 CPlaceInner::NoPlace => AttributeValue::Exprloc(Expression(vec![])),
297             };
298
299             let var_entry = self.debug_context.dwarf.unit.get_mut(var_id);
300             var_entry.set(gimli::DW_AT_location, location);
301         }
302     }
303 }
304
305
306
307
308
309
310
311 // Adapted from https://github.com/CraneStation/wasmtime/blob/5a1845b4caf7a5dba8eda1fef05213a532ed4259/crates/debug/src/transform/expression.rs#L59-L137
312
313 fn map_reg(reg: RegUnit) -> Register {
314     static mut REG_X86_MAP: Option<HashMap<RegUnit, Register>> = None;
315     // FIXME lazy initialization?
316     unsafe {
317         if REG_X86_MAP.is_none() {
318             REG_X86_MAP = Some(HashMap::new());
319         }
320         if let Some(val) = REG_X86_MAP.as_mut().unwrap().get(&reg) {
321             return *val;
322         }
323         let result = match reg {
324             0 => X86_64::RAX,
325             1 => X86_64::RCX,
326             2 => X86_64::RDX,
327             3 => X86_64::RBX,
328             4 => X86_64::RSP,
329             5 => X86_64::RBP,
330             6 => X86_64::RSI,
331             7 => X86_64::RDI,
332             8 => X86_64::R8,
333             9 => X86_64::R9,
334             10 => X86_64::R10,
335             11 => X86_64::R11,
336             12 => X86_64::R12,
337             13 => X86_64::R13,
338             14 => X86_64::R14,
339             15 => X86_64::R15,
340             16 => X86_64::XMM0,
341             17 => X86_64::XMM1,
342             18 => X86_64::XMM2,
343             19 => X86_64::XMM3,
344             20 => X86_64::XMM4,
345             21 => X86_64::XMM5,
346             22 => X86_64::XMM6,
347             23 => X86_64::XMM7,
348             24 => X86_64::XMM8,
349             25 => X86_64::XMM9,
350             26 => X86_64::XMM10,
351             27 => X86_64::XMM11,
352             28 => X86_64::XMM12,
353             29 => X86_64::XMM13,
354             30 => X86_64::XMM14,
355             31 => X86_64::XMM15,
356             _ => panic!("unknown x86_64 register {}", reg),
357         };
358         REG_X86_MAP.as_mut().unwrap().insert(reg, result);
359         result
360     }
361 }
362
363 fn translate_loc(loc: ValueLoc, stack_slots: &StackSlots) -> Option<Vec<u8>> {
364     match loc {
365         ValueLoc::Reg(reg) => {
366             let machine_reg = map_reg(reg).0 as u8;
367             assert!(machine_reg <= 32); // FIXME
368             Some(vec![gimli::constants::DW_OP_reg0.0 + machine_reg])
369         }
370         ValueLoc::Stack(ss) => {
371             if let Some(ss_offset) = stack_slots[ss].offset {
372                 let endian = gimli::RunTimeEndian::Little;
373                 let mut writer = write::EndianVec::new(endian);
374                 writer
375                     .write_u8(gimli::constants::DW_OP_breg0.0 + X86_64::RBP.0 as u8)
376                     .expect("bp wr");
377                 writer.write_sleb128(ss_offset as i64 + 16).expect("ss wr");
378                 writer
379                     .write_u8(gimli::constants::DW_OP_deref.0 as u8)
380                     .expect("bp wr");
381                 let buf = writer.into_vec();
382                 return Some(buf);
383             }
384             None
385         }
386         _ => None,
387     }
388 }