]>
Commit | Line | Data |
---|---|---|
29967ef6 XL |
1 | //! Handling of everything related to debuginfo. |
2 | ||
3 | mod emit; | |
4 | mod line_info; | |
5 | mod unwind; | |
6 | ||
7 | use crate::prelude::*; | |
8 | ||
9 | use rustc_index::vec::IndexVec; | |
10 | ||
11 | use cranelift_codegen::entity::EntityRef; | |
6a06907d | 12 | use cranelift_codegen::ir::{LabelValueLoc, StackSlots, ValueLabel, ValueLoc}; |
29967ef6 XL |
13 | use cranelift_codegen::isa::TargetIsa; |
14 | use cranelift_codegen::ValueLocRange; | |
15 | ||
16 | use gimli::write::{ | |
17 | Address, AttributeValue, DwarfUnit, Expression, LineProgram, LineString, Location, | |
18 | LocationList, Range, RangeList, UnitEntryId, | |
19 | }; | |
20 | use gimli::{Encoding, Format, LineEncoding, RunTimeEndian, X86_64}; | |
21 | ||
22 | pub(crate) use emit::{DebugReloc, DebugRelocName}; | |
23 | pub(crate) use unwind::UnwindContext; | |
24 | ||
25 | fn target_endian(tcx: TyCtxt<'_>) -> RunTimeEndian { | |
26 | use rustc_target::abi::Endian; | |
27 | ||
28 | match tcx.data_layout.endian { | |
29 | Endian::Big => RunTimeEndian::Big, | |
30 | Endian::Little => RunTimeEndian::Little, | |
31 | } | |
32 | } | |
33 | ||
34 | pub(crate) struct DebugContext<'tcx> { | |
35 | tcx: TyCtxt<'tcx>, | |
36 | ||
37 | endian: RunTimeEndian, | |
38 | ||
39 | dwarf: DwarfUnit, | |
40 | unit_range_list: RangeList, | |
41 | ||
29967ef6 XL |
42 | types: FxHashMap<Ty<'tcx>, UnitEntryId>, |
43 | } | |
44 | ||
45 | impl<'tcx> DebugContext<'tcx> { | |
46 | pub(crate) fn new(tcx: TyCtxt<'tcx>, isa: &dyn TargetIsa) -> Self { | |
47 | let encoding = Encoding { | |
48 | format: Format::Dwarf32, | |
49 | // TODO: this should be configurable | |
50 | // macOS doesn't seem to support DWARF > 3 | |
51 | // 5 version is required for md5 file hash | |
52 | version: if tcx.sess.target.is_like_osx { | |
53 | 3 | |
54 | } else { | |
55 | // FIXME change to version 5 once the gdb and lldb shipping with the latest debian | |
56 | // support it. | |
57 | 4 | |
58 | }, | |
59 | address_size: isa.frontend_config().pointer_bytes(), | |
60 | }; | |
61 | ||
62 | let mut dwarf = DwarfUnit::new(encoding); | |
63 | ||
64 | // FIXME: how to get version when building out of tree? | |
65 | // Normally this would use option_env!("CFG_VERSION"). | |
66 | let producer = format!("cg_clif (rustc {})", "unknown version"); | |
67 | let comp_dir = tcx.sess.working_dir.0.to_string_lossy().into_owned(); | |
68 | let (name, file_info) = match tcx.sess.local_crate_source_file.clone() { | |
69 | Some(path) => { | |
70 | let name = path.to_string_lossy().into_owned(); | |
71 | (name, None) | |
72 | } | |
73 | None => (tcx.crate_name(LOCAL_CRATE).to_string(), None), | |
74 | }; | |
75 | ||
76 | let mut line_program = LineProgram::new( | |
77 | encoding, | |
78 | LineEncoding::default(), | |
79 | LineString::new(comp_dir.as_bytes(), encoding, &mut dwarf.line_strings), | |
80 | LineString::new(name.as_bytes(), encoding, &mut dwarf.line_strings), | |
81 | file_info, | |
82 | ); | |
83 | line_program.file_has_md5 = file_info.is_some(); | |
84 | ||
85 | dwarf.unit.line_program = line_program; | |
86 | ||
87 | { | |
88 | let name = dwarf.strings.add(name); | |
89 | let comp_dir = dwarf.strings.add(comp_dir); | |
90 | ||
91 | let root = dwarf.unit.root(); | |
92 | let root = dwarf.unit.get_mut(root); | |
6a06907d XL |
93 | root.set(gimli::DW_AT_producer, AttributeValue::StringRef(dwarf.strings.add(producer))); |
94 | root.set(gimli::DW_AT_language, AttributeValue::Language(gimli::DW_LANG_Rust)); | |
29967ef6 XL |
95 | root.set(gimli::DW_AT_name, AttributeValue::StringRef(name)); |
96 | root.set(gimli::DW_AT_comp_dir, AttributeValue::StringRef(comp_dir)); | |
6a06907d | 97 | root.set(gimli::DW_AT_low_pc, AttributeValue::Address(Address::Constant(0))); |
29967ef6 XL |
98 | } |
99 | ||
100 | DebugContext { | |
101 | tcx, | |
102 | ||
103 | endian: target_endian(tcx), | |
104 | ||
105 | dwarf, | |
106 | unit_range_list: RangeList(Vec::new()), | |
107 | ||
29967ef6 XL |
108 | types: FxHashMap::default(), |
109 | } | |
110 | } | |
111 | ||
29967ef6 XL |
112 | fn dwarf_ty(&mut self, ty: Ty<'tcx>) -> UnitEntryId { |
113 | if let Some(type_id) = self.types.get(ty) { | |
114 | return *type_id; | |
115 | } | |
116 | ||
117 | let new_entry = |dwarf: &mut DwarfUnit, tag| dwarf.unit.add(dwarf.unit.root(), tag); | |
118 | ||
119 | let primitive = |dwarf: &mut DwarfUnit, ate| { | |
120 | let type_id = new_entry(dwarf, gimli::DW_TAG_base_type); | |
121 | let type_entry = dwarf.unit.get_mut(type_id); | |
122 | type_entry.set(gimli::DW_AT_encoding, AttributeValue::Encoding(ate)); | |
123 | type_id | |
124 | }; | |
125 | ||
126 | let name = format!("{}", ty); | |
127 | let layout = self.tcx.layout_of(ParamEnv::reveal_all().and(ty)).unwrap(); | |
128 | ||
129 | let type_id = match ty.kind() { | |
130 | ty::Bool => primitive(&mut self.dwarf, gimli::DW_ATE_boolean), | |
131 | ty::Char => primitive(&mut self.dwarf, gimli::DW_ATE_UTF), | |
132 | ty::Uint(_) => primitive(&mut self.dwarf, gimli::DW_ATE_unsigned), | |
133 | ty::Int(_) => primitive(&mut self.dwarf, gimli::DW_ATE_signed), | |
134 | ty::Float(_) => primitive(&mut self.dwarf, gimli::DW_ATE_float), | |
135 | ty::Ref(_, pointee_ty, _mutbl) | |
6a06907d | 136 | | ty::RawPtr(ty::TypeAndMut { ty: pointee_ty, mutbl: _mutbl }) => { |
29967ef6 XL |
137 | let type_id = new_entry(&mut self.dwarf, gimli::DW_TAG_pointer_type); |
138 | ||
139 | // Ensure that type is inserted before recursing to avoid duplicates | |
140 | self.types.insert(ty, type_id); | |
141 | ||
142 | let pointee = self.dwarf_ty(pointee_ty); | |
143 | ||
144 | let type_entry = self.dwarf.unit.get_mut(type_id); | |
145 | ||
146 | //type_entry.set(gimli::DW_AT_mutable, AttributeValue::Flag(mutbl == rustc_hir::Mutability::Mut)); | |
147 | type_entry.set(gimli::DW_AT_type, AttributeValue::UnitRef(pointee)); | |
148 | ||
149 | type_id | |
150 | } | |
151 | ty::Adt(adt_def, _substs) if adt_def.is_struct() && !layout.is_unsized() => { | |
152 | let type_id = new_entry(&mut self.dwarf, gimli::DW_TAG_structure_type); | |
153 | ||
154 | // Ensure that type is inserted before recursing to avoid duplicates | |
155 | self.types.insert(ty, type_id); | |
156 | ||
157 | let variant = adt_def.non_enum_variant(); | |
158 | ||
159 | for (field_idx, field_def) in variant.fields.iter().enumerate() { | |
160 | let field_offset = layout.fields.offset(field_idx); | |
161 | let field_layout = layout | |
162 | .field( | |
6a06907d | 163 | &layout::LayoutCx { tcx: self.tcx, param_env: ParamEnv::reveal_all() }, |
29967ef6 XL |
164 | field_idx, |
165 | ) | |
166 | .unwrap(); | |
167 | ||
168 | let field_type = self.dwarf_ty(field_layout.ty); | |
169 | ||
170 | let field_id = self.dwarf.unit.add(type_id, gimli::DW_TAG_member); | |
171 | let field_entry = self.dwarf.unit.get_mut(field_id); | |
172 | ||
173 | field_entry.set( | |
174 | gimli::DW_AT_name, | |
175 | AttributeValue::String(field_def.ident.as_str().to_string().into_bytes()), | |
176 | ); | |
177 | field_entry.set( | |
178 | gimli::DW_AT_data_member_location, | |
179 | AttributeValue::Udata(field_offset.bytes()), | |
180 | ); | |
181 | field_entry.set(gimli::DW_AT_type, AttributeValue::UnitRef(field_type)); | |
182 | } | |
183 | ||
184 | type_id | |
185 | } | |
186 | _ => new_entry(&mut self.dwarf, gimli::DW_TAG_structure_type), | |
187 | }; | |
188 | ||
189 | let type_entry = self.dwarf.unit.get_mut(type_id); | |
190 | ||
191 | type_entry.set(gimli::DW_AT_name, AttributeValue::String(name.into_bytes())); | |
6a06907d | 192 | type_entry.set(gimli::DW_AT_byte_size, AttributeValue::Udata(layout.size.bytes())); |
29967ef6 XL |
193 | |
194 | self.types.insert(ty, type_id); | |
195 | ||
196 | type_id | |
197 | } | |
198 | ||
199 | fn define_local(&mut self, scope: UnitEntryId, name: String, ty: Ty<'tcx>) -> UnitEntryId { | |
200 | let dw_ty = self.dwarf_ty(ty); | |
201 | ||
202 | let var_id = self.dwarf.unit.add(scope, gimli::DW_TAG_variable); | |
203 | let var_entry = self.dwarf.unit.get_mut(var_id); | |
204 | ||
205 | var_entry.set(gimli::DW_AT_name, AttributeValue::String(name.into_bytes())); | |
206 | var_entry.set(gimli::DW_AT_type, AttributeValue::UnitRef(dw_ty)); | |
207 | ||
208 | var_id | |
209 | } | |
210 | ||
211 | pub(crate) fn define_function( | |
212 | &mut self, | |
213 | instance: Instance<'tcx>, | |
214 | func_id: FuncId, | |
215 | name: &str, | |
216 | isa: &dyn TargetIsa, | |
217 | context: &Context, | |
218 | source_info_set: &indexmap::IndexSet<SourceInfo>, | |
219 | local_map: IndexVec<mir::Local, CPlace<'tcx>>, | |
220 | ) { | |
221 | let symbol = func_id.as_u32() as usize; | |
222 | let mir = self.tcx.instance_mir(instance.def); | |
223 | ||
224 | // FIXME: add to appropriate scope instead of root | |
225 | let scope = self.dwarf.unit.root(); | |
226 | ||
227 | let entry_id = self.dwarf.unit.add(scope, gimli::DW_TAG_subprogram); | |
228 | let entry = self.dwarf.unit.get_mut(entry_id); | |
229 | let name_id = self.dwarf.strings.add(name); | |
230 | // Gdb requires DW_AT_name. Otherwise the DW_TAG_subprogram is skipped. | |
231 | entry.set(gimli::DW_AT_name, AttributeValue::StringRef(name_id)); | |
6a06907d | 232 | entry.set(gimli::DW_AT_linkage_name, AttributeValue::StringRef(name_id)); |
29967ef6 | 233 | |
6a06907d | 234 | let end = self.create_debug_lines(symbol, entry_id, context, mir.span, source_info_set); |
29967ef6 XL |
235 | |
236 | self.unit_range_list.0.push(Range::StartLength { | |
237 | begin: Address::Symbol { symbol, addend: 0 }, | |
238 | length: u64::from(end), | |
239 | }); | |
240 | ||
29967ef6 XL |
241 | let func_entry = self.dwarf.unit.get_mut(entry_id); |
242 | // Gdb requires both DW_AT_low_pc and DW_AT_high_pc. Otherwise the DW_TAG_subprogram is skipped. | |
243 | func_entry.set( | |
244 | gimli::DW_AT_low_pc, | |
245 | AttributeValue::Address(Address::Symbol { symbol, addend: 0 }), | |
246 | ); | |
247 | // Using Udata for DW_AT_high_pc requires at least DWARF4 | |
248 | func_entry.set(gimli::DW_AT_high_pc, AttributeValue::Udata(u64::from(end))); | |
249 | ||
29967ef6 XL |
250 | // FIXME make it more reliable and implement scopes before re-enabling this. |
251 | if false { | |
252 | let value_labels_ranges = context.build_value_labels_ranges(isa).unwrap(); | |
253 | ||
254 | for (local, _local_decl) in mir.local_decls.iter_enumerated() { | |
255 | let ty = self.tcx.subst_and_normalize_erasing_regions( | |
256 | instance.substs, | |
257 | ty::ParamEnv::reveal_all(), | |
fc512014 | 258 | mir.local_decls[local].ty, |
29967ef6 XL |
259 | ); |
260 | let var_id = self.define_local(entry_id, format!("{:?}", local), ty); | |
261 | ||
262 | let location = place_location( | |
263 | self, | |
264 | isa, | |
265 | symbol, | |
266 | context, | |
267 | &local_map, | |
268 | &value_labels_ranges, | |
6a06907d | 269 | Place { local, projection: ty::List::empty() }, |
29967ef6 XL |
270 | ); |
271 | ||
272 | let var_entry = self.dwarf.unit.get_mut(var_id); | |
273 | var_entry.set(gimli::DW_AT_location, location); | |
274 | } | |
275 | } | |
276 | ||
277 | // FIXME create locals for all entries in mir.var_debug_info | |
278 | } | |
279 | } | |
280 | ||
281 | fn place_location<'tcx>( | |
282 | debug_context: &mut DebugContext<'tcx>, | |
283 | isa: &dyn TargetIsa, | |
284 | symbol: usize, | |
285 | context: &Context, | |
286 | local_map: &IndexVec<mir::Local, CPlace<'tcx>>, | |
287 | #[allow(rustc::default_hash_types)] value_labels_ranges: &std::collections::HashMap< | |
288 | ValueLabel, | |
289 | Vec<ValueLocRange>, | |
290 | >, | |
291 | place: Place<'tcx>, | |
292 | ) -> AttributeValue { | |
293 | assert!(place.projection.is_empty()); // FIXME implement them | |
294 | ||
295 | match local_map[place.local].inner() { | |
296 | CPlaceInner::Var(_local, var) => { | |
297 | let value_label = cranelift_codegen::ir::ValueLabel::new(var.index()); | |
298 | if let Some(value_loc_ranges) = value_labels_ranges.get(&value_label) { | |
299 | let loc_list = LocationList( | |
300 | value_loc_ranges | |
301 | .iter() | |
302 | .map(|value_loc_range| Location::StartEnd { | |
303 | begin: Address::Symbol { | |
304 | symbol, | |
305 | addend: i64::from(value_loc_range.start), | |
306 | }, | |
6a06907d | 307 | end: Address::Symbol { symbol, addend: i64::from(value_loc_range.end) }, |
29967ef6 XL |
308 | data: translate_loc( |
309 | isa, | |
310 | value_loc_range.loc, | |
311 | &context.func.stack_slots, | |
312 | ) | |
313 | .unwrap(), | |
314 | }) | |
315 | .collect(), | |
316 | ); | |
317 | let loc_list_id = debug_context.dwarf.unit.locations.add(loc_list); | |
318 | ||
319 | AttributeValue::LocationListRef(loc_list_id) | |
320 | } else { | |
321 | // FIXME set value labels for unused locals | |
322 | ||
323 | AttributeValue::Exprloc(Expression::new()) | |
324 | } | |
325 | } | |
326 | CPlaceInner::VarPair(_, _, _) => { | |
327 | // FIXME implement this | |
328 | ||
329 | AttributeValue::Exprloc(Expression::new()) | |
330 | } | |
331 | CPlaceInner::VarLane(_, _, _) => { | |
332 | // FIXME implement this | |
333 | ||
334 | AttributeValue::Exprloc(Expression::new()) | |
335 | } | |
336 | CPlaceInner::Addr(_, _) => { | |
337 | // FIXME implement this (used by arguments and returns) | |
338 | ||
339 | AttributeValue::Exprloc(Expression::new()) | |
340 | ||
341 | // For PointerBase::Stack: | |
342 | //AttributeValue::Exprloc(translate_loc(ValueLoc::Stack(*stack_slot), &context.func.stack_slots).unwrap()) | |
343 | } | |
344 | } | |
345 | } | |
346 | ||
347 | // Adapted from https://github.com/CraneStation/wasmtime/blob/5a1845b4caf7a5dba8eda1fef05213a532ed4259/crates/debug/src/transform/expression.rs#L59-L137 | |
348 | fn translate_loc( | |
349 | isa: &dyn TargetIsa, | |
6a06907d | 350 | loc: LabelValueLoc, |
29967ef6 XL |
351 | stack_slots: &StackSlots, |
352 | ) -> Option<Expression> { | |
353 | match loc { | |
6a06907d | 354 | LabelValueLoc::ValueLoc(ValueLoc::Reg(reg)) => { |
29967ef6 XL |
355 | let machine_reg = isa.map_dwarf_register(reg).unwrap(); |
356 | let mut expr = Expression::new(); | |
357 | expr.op_reg(gimli::Register(machine_reg)); | |
358 | Some(expr) | |
359 | } | |
6a06907d | 360 | LabelValueLoc::ValueLoc(ValueLoc::Stack(ss)) => { |
29967ef6 XL |
361 | if let Some(ss_offset) = stack_slots[ss].offset { |
362 | let mut expr = Expression::new(); | |
363 | expr.op_breg(X86_64::RBP, i64::from(ss_offset) + 16); | |
364 | Some(expr) | |
365 | } else { | |
366 | None | |
367 | } | |
368 | } | |
6a06907d XL |
369 | LabelValueLoc::ValueLoc(ValueLoc::Unassigned) => unreachable!(), |
370 | LabelValueLoc::Reg(reg) => { | |
371 | let machine_reg = isa.map_regalloc_reg_to_dwarf(reg).unwrap(); | |
372 | let mut expr = Expression::new(); | |
373 | expr.op_reg(gimli::Register(machine_reg)); | |
374 | Some(expr) | |
375 | } | |
376 | LabelValueLoc::SPOffset(offset) => { | |
377 | let mut expr = Expression::new(); | |
378 | expr.op_breg(X86_64::RSP, offset); | |
379 | Some(expr) | |
380 | } | |
29967ef6 XL |
381 | } |
382 | } |