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