Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 14c32bf

Browse files
authored
Merge pull request rust-lang#978 from bjorn3/simple_debuginfo_for_arguments
Generate simple debuginfo for arguments
2 parents 1691405 + c3180f3 commit 14c32bf

File tree

2 files changed

+213
-7
lines changed

2 files changed

+213
-7
lines changed

docs/dwarf.md

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,135 @@ debugger won't find the line number information. On macOS the debuginfo relocati
1919
section relative and not symbol relative.
2020
See [#303 (comment)](https://github.com/bjorn3/rustc_codegen_cranelift/issues/303#issuecomment-457825535)
2121
for more information.
22+
23+
# Function debuginfo
24+
25+
## Tips
26+
27+
`DW_TAG_subprogram` requires `DW_AT_name`, `DW_AT_low_pc` and `DW_AT_high_pc` **or** `DW_AT_ranges`.
28+
Otherwise gdb will silently skip it. When `DW_AT_high_pc` is a length instead of an address, the
29+
DWARF version must be at least 4.
30+
31+
<details>
32+
<summary>IRC log of #gdb on irc.freenode.org at 2020-04-23</summary>
33+
34+
```
35+
(13:46:11) bjorn3: i am writing a backend for a compiler that uses DWARF for debuginfo. for some reason gdb seems to completely ignore all DW_TAG_subprogram, while lldb works fine. any idea what the problem could be?
36+
(13:47:49) bjorn3: this is the output of llvm-dwarfdump: https://gist.github.com/bjorn3/8a34e333c80f13cb048381e94b4a3756
37+
(13:47:50) osa1: luispm: why is that problem not exists in 'commands'? (the target vs. host)
38+
(13:52:16) luispm: osa1, commands is a bit more high level. It executes isolated commands. Breakpoint conditions need to be evaluated in the context of a valid expression. That expression may involve variables, symbols etc.
39+
(13:52:36) luispm: osa1, Oh, i see your point now. Commands is only executed on the host.
40+
(13:53:18) luispm: osa1, The commands are not tied to the execution context of the debugged program. The breakpoint conditions determine if execution must stop or continue etc.
41+
(13:55:00) luispm: bjorn3, Likely something GDB thinks is wrong. Does enabling "set debug dwarf*" show anything?
42+
(13:56:01) bjorn3: luispm: no
43+
(13:56:12) bjorn3: for more context: https://github.com/bjorn3/rustc_codegen_cranelift/pull/978
44+
(13:58:16) osa1 verliet de ruimte (quit: Quit: osa1).
45+
(13:58:28) bjorn3: luispm: wait, for b m<TAB> it shows nothing, but when stepping into a new function it does
46+
(13:58:45) bjorn3: it still doesn't show anything for `info args` though
47+
(13:58:50) bjorn3: No symbol table info available.
48+
(14:00:50) luispm: bjorn3, Is that expected given the nature of the binary?
49+
(14:01:17) bjorn3: b main<TAB> may show nothing as I only set DW_AT_linkage_name and not DW_AT_name
50+
(14:01:24) bjorn3: info args should work though
51+
(14:03:26) luispm: Sorry, I'm not sure what's up. There may be a genuine bug there.
52+
(14:03:41) luispm: tromey (not currently in the channel, but maybe later today) may have more input.
53+
(14:04:08) bjorn3: okay, thanks luispm!
54+
(14:04:27) luispm: In the worst case, reporting a bug may prompt someone to look into that as well.
55+
(14:04:48) luispm: Or send an e-mail to the [email protected] mailing list.
56+
(14:05:11) bjorn3: I don't know if it is a bug in gdb, or just me producing (slightly) wrong DWARF
57+
(14:39:40) irker749: gdb: tom binutils-gdb.git:master * 740480b88af / gdb/ChangeLog gdb/darwin-nat.c gdb/inferior.c gdb/inferior.h: Remove iterate_over_inferiors
58+
(15:22:45) irker749: gdb: tromey binutils-gdb.git:master * ecc6c6066b5 / gdb/ChangeLog gdb/dwarf2/read.c gdb/unittests/lookup_name_info-selftests.c: Fix Ada crash with .debug_names
59+
(15:23:13) bjorn3: tromey: ping
60+
(15:23:29) tromey: bjorn3: hey
61+
(15:24:16) bjorn3: I am writing a backend for a compiler which uses DWARF for debuginfo. I unfortunately can't get gdb to show arguments. lldb works fine.
62+
(15:25:13) bjorn3: it just says: No symbol table info available.
63+
(15:25:21) bjorn3: any idea what it could be?
64+
(15:25:34) bjorn3: dwarfdump output: https://gist.github.com/bjorn3/8a34e333c80f13cb048381e94b4a3756
65+
(15:26:48) bjorn3: more context: https://github.com/bjorn3/rustc_codegen_cranelift/pull/978
66+
(15:28:05) tromey: offhand I don't know, but if you can send me an executable I can look
67+
(15:28:17) bjorn3: how should I send it?
68+
(15:29:26) tromey: good question
69+
(15:29:41) tromey: you could try emailing it to tromey at adacore.com
70+
(15:29:47) tromey: dunno if that will work or not
71+
(15:30:26) bjorn3: i will try
72+
(15:37:27) bjorn3: tromey: i sent an email with the subject "gdb args not showing"
73+
(15:38:29) tromey: will check now
74+
(15:38:40) bjorn3: thanks!
75+
(15:42:51) irker749: gdb: tdevries binutils-gdb.git:master * de82891ce5b / gdb/ChangeLog gdb/block.c gdb/block.h gdb/symtab.c gdb/testsuite/ChangeLog gdb/testsuite/gdb.base/decl-before-def-decl.c gdb/testsuite/gdb.base/decl-before-def-def.c gdb/testsuite/gdb.base/decl-before-def.exp: [gdb/symtab] Prefer def over decl (inter-CU case)
76+
(15:42:52) irker749: gdb: tdevries binutils-gdb.git:master * 70bc38f5138 / gdb/ChangeLog gdb/symtab.c gdb/testsuite/ChangeLog gdb/testsuite/gdb.base/decl-before-def.exp: [gdb/symtab] Prefer def over decl (inter-CU case, with context)
77+
(15:43:36) tromey: bjorn3: sorry, got distracted. I have the file now
78+
(15:45:35) tromey: my first thing when investigating was to enable complaints
79+
(15:45:37) tromey: so I did
80+
(15:45:40) tromey: set complaints 1000
81+
(15:45:42) tromey: then
82+
(15:45:51) tromey: file -readnow mini_core_hello_world
83+
(15:46:00) tromey: gdb printed just one style of complaint
84+
(15:46:07) tromey: During symbol reading: missing name for subprogram DIE at 0x3f7
85+
(15:46:18) tromey: (which is really pretty good, most compilers manage to generate a bunch)
86+
(15:46:29) tromey: and then the gdb DWARF reader says
87+
(15:46:34) tromey: /* Ignore functions with missing or empty names. These are actually
88+
(15:46:34) tromey: illegal according to the DWARF standard. */
89+
(15:46:34) tromey: if (name == NULL)
90+
(15:46:34) tromey: {
91+
(15:46:37) tromey: complaint (_("missing name for subprogram DIE at %s"),
92+
(15:46:40) tromey: sect_offset_str (die->sect_off));
93+
(15:46:47) tromey: I wonder if that comment is correct though
94+
(15:47:34) tromey: I guess pedantically maybe it is, DWARF 5 3.3.1 says
95+
(15:47:43) tromey: The subroutine or entry point entry has a DW_AT_name attribute whose value is
96+
(15:47:43) tromey: a null-terminated string containing the subroutine or entry point name.
97+
(15:48:14) bjorn3: i tried set complaints, but it returned complaints for system files. i didn't know about file -readnow.
98+
(15:48:21) tromey: cool
99+
(15:48:26) bjorn3: i will try adding DW_AT_name
100+
(15:48:45) tromey: without readnow unfortunately you get less stuff, because for whatever reason gdb has 2 separate DWARF scanners
101+
(15:49:02) tromey: sort of anyway
102+
(15:49:43) tromey: this seems kind of pedantic of gdb, like if there's a linkage name but no DW_AT_name, then why bail?
103+
(15:50:01) tromey: also what about anonymous functions
104+
(15:50:17) tromey: but anyway this explains the current situation and if you don't mind adding DW_AT_name, then that's probably simplest
105+
(15:51:47) bjorn3: i added DW_AT_name.
106+
(15:51:54) bjorn3: now it says cannot get low and high bounds for subprogram DIE at ...
107+
(15:52:01) tromey: ugh
108+
(15:52:10) bjorn3: i will add DW_AT_low_pc and DW_AT_high_pc
109+
(15:52:15) tromey: /* Ignore functions with missing or invalid low and high pc attributes. */
110+
(15:52:37) tromey: you can also use DW_AT_ranges
111+
(15:52:55) tromey: if you'd prefer
112+
(15:53:08) bjorn3: already using DW_AT_ranges for DW_TAG_compilation_unit
113+
(15:53:19) bjorn3: for individual functions, there are no gaps
114+
(15:57:07) bjorn3: still the same error with DW_AT_low_pc and DW_AT_high_pc
115+
(15:57:24) bjorn3: tromey: ^
116+
(15:58:08) tromey: hmmm
117+
(15:58:30) bjorn3: should i send the new executable?
118+
(15:58:31) tromey: send me another executable & I will debug
119+
(15:58:33) tromey: yep
120+
(15:59:23) bjorn3: sent as repy of the previous mail
121+
(16:03:23) tromey: the low PC has DW_FORM_addr, but the high PC has DW_FORM_udata, which seems weird
122+
(16:03:50) mjw: no
123+
(16:03:54) tromey: no?
124+
(16:04:00) mjw: I suggested that for the DWARF standard...
125+
(16:04:05) mjw: sorry
126+
(16:04:58) mjw: The idea was that instead of two relocations and two address wide fields, you have one address and a constant offset.
127+
(16:05:05) tromey: ahh, I see the code now
128+
(16:05:07) tromey: I forgot about this
129+
(16:05:18) tromey: if (cu->header.version >= 4 && attr_high->form_is_constant ())
130+
(16:05:18) tromey: high += low;
131+
(16:05:36) mjw: that second offset doesn't need a relocation and can often be packed in something small, like an uleb128
132+
(16:05:51) mjw: using udata might not be ideal though, but is allowed
133+
(16:05:51) tromey: bjorn3: the problem is that this CU claims to be DWARF 3 but is using a DWARF 4 feature
134+
(16:05:58) mjw: aha
135+
(16:05:59) bjorn3: which one?
136+
(16:06:03) ryoshu: hi
137+
(16:06:08) tromey: high_pc (udata) 107 (+0x00000000000011b0 <_ZN21mini_core_hello_world5start17hec55b7ca64fc434eE>)
138+
(16:06:08) tromey:
139+
(16:06:12) ryoshu: just soft ping, I have a queue of patches :)
140+
(16:06:22) tromey: using this as a length requires DWARF 4
141+
(16:06:36) tromey: for gdb at least it's fine to always emit DWARF 4
142+
(16:06:44) bjorn3: trying dwarf 4 now
143+
(16:06:48) tromey: I think there are some DWARF 5 features still in the works but DWARF 4 should be solid AFAIK
144+
(16:07:03) tromey: fini
145+
(16:07:08) tromey: lol wrong window
146+
(16:07:56) mjw: Maybe you can accept it for DWARF < 4. But if I remember correctly it might be that people might have been using udata as if it was an address...
147+
(16:08:13) tromey: yeah, I vaguely recall this as well, though I'd expect there to be a comment
148+
(16:08:21) mjw: Cannot really remember why it needed version >= 4. Maybe there was no good reason?
149+
(16:08:32) bjorn3: tromey: it works!!!! thanks for all the help!
150+
(16:08:41) tromey: my pleasure bjorn3
151+
```
152+
153+
</details>

src/debuginfo/mod.rs

Lines changed: 81 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ pub(crate) struct DebugContext<'tcx> {
3535
dwarf: DwarfUnit,
3636
unit_range_list: RangeList,
3737

38+
clif_types: FxHashMap<Type, UnitEntryId>,
3839
types: FxHashMap<Ty<'tcx>, UnitEntryId>,
3940
}
4041

@@ -116,10 +117,45 @@ impl<'tcx> DebugContext<'tcx> {
116117
dwarf,
117118
unit_range_list: RangeList(Vec::new()),
118119

120+
clif_types: FxHashMap::default(),
119121
types: FxHashMap::default(),
120122
}
121123
}
122124

125+
fn dwarf_ty_for_clif_ty(&mut self, ty: Type) -> UnitEntryId {
126+
if let Some(type_id) = self.clif_types.get(&ty) {
127+
return *type_id;
128+
}
129+
130+
let new_entry = |dwarf: &mut DwarfUnit, tag| dwarf.unit.add(dwarf.unit.root(), tag);
131+
132+
let primitive = |dwarf: &mut DwarfUnit, ate| {
133+
let type_id = new_entry(dwarf, gimli::DW_TAG_base_type);
134+
let type_entry = dwarf.unit.get_mut(type_id);
135+
type_entry.set(gimli::DW_AT_encoding, AttributeValue::Encoding(ate));
136+
type_id
137+
};
138+
139+
let type_id = if ty.is_bool() {
140+
primitive(&mut self.dwarf, gimli::DW_ATE_boolean)
141+
} else if ty.is_int() {
142+
primitive(&mut self.dwarf, gimli::DW_ATE_address)
143+
} else if ty.is_float() {
144+
primitive(&mut self.dwarf, gimli::DW_ATE_float)
145+
} else {
146+
new_entry(&mut self.dwarf, gimli::DW_TAG_structure_type)
147+
};
148+
149+
let type_entry = self.dwarf.unit.get_mut(type_id);
150+
type_entry.set(gimli::DW_AT_name, AttributeValue::String(format!("{}", ty).replace('i', "u").into_bytes()));
151+
type_entry.set(
152+
gimli::DW_AT_byte_size,
153+
AttributeValue::Udata(u64::from(ty.bytes())),
154+
);
155+
156+
type_id
157+
}
158+
123159
fn dwarf_ty(&mut self, ty: Ty<'tcx>) -> UnitEntryId {
124160
if let Some(type_id) = self.types.get(ty) {
125161
return *type_id;
@@ -234,6 +270,11 @@ impl<'a, 'tcx> FunctionDebugContext<'a, 'tcx> {
234270
.add(scope, gimli::DW_TAG_subprogram);
235271
let entry = debug_context.dwarf.unit.get_mut(entry_id);
236272
let name_id = debug_context.dwarf.strings.add(name);
273+
// Gdb requires DW_AT_name. Otherwise the DW_TAG_subprogram is skipped.
274+
entry.set(
275+
gimli::DW_AT_name,
276+
AttributeValue::StringRef(name_id),
277+
);
237278
entry.set(
238279
gimli::DW_AT_linkage_name,
239280
AttributeValue::StringRef(name_id),
@@ -249,11 +290,6 @@ impl<'a, 'tcx> FunctionDebugContext<'a, 'tcx> {
249290
}
250291

251292
fn define_local(&mut self, name: String, ty: Ty<'tcx>) -> UnitEntryId {
252-
let ty = self.debug_context.tcx.subst_and_normalize_erasing_regions(
253-
self.instance.substs,
254-
ty::ParamEnv::reveal_all(),
255-
&ty,
256-
);
257293
let dw_ty = self.debug_context.dwarf_ty(ty);
258294

259295
let var_id = self
@@ -290,15 +326,53 @@ impl<'a, 'tcx> FunctionDebugContext<'a, 'tcx> {
290326
symbol: self.symbol,
291327
addend: 0,
292328
},
293-
length: end as u64,
329+
length: u64::from(end),
294330
});
295331

332+
let func_entry = self.debug_context.dwarf.unit.get_mut(self.entry_id);
333+
// Gdb requires both DW_AT_low_pc and DW_AT_high_pc. Otherwise the DW_TAG_subprogram is skipped.
334+
func_entry.set(gimli::DW_AT_low_pc, AttributeValue::Address(Address::Symbol {
335+
symbol: self.symbol,
336+
addend: 0,
337+
}));
338+
// Using Udata for DW_AT_high_pc requires at least DWARF4
339+
func_entry.set(gimli::DW_AT_high_pc, AttributeValue::Udata(u64::from(end)));
340+
341+
// FIXME Remove once actual debuginfo for locals works.
342+
for (i, (param, &val)) in context.func.signature.params.iter().zip(context.func.dfg.block_params(context.func.layout.entry_block().unwrap())).enumerate() {
343+
use cranelift_codegen::ir::ArgumentPurpose;
344+
let base_name = match param.purpose {
345+
ArgumentPurpose::Normal => "arg",
346+
ArgumentPurpose::StructReturn => "sret",
347+
ArgumentPurpose::Link | ArgumentPurpose::FramePointer | ArgumentPurpose::CalleeSaved => continue,
348+
ArgumentPurpose::VMContext | ArgumentPurpose::SignatureId | ArgumentPurpose::StackLimit => unreachable!(),
349+
};
350+
let name = format!("{}{}", base_name, i);
351+
352+
let dw_ty = self.debug_context.dwarf_ty_for_clif_ty(param.value_type);
353+
let loc = Expression(
354+
translate_loc(isa, context.func.locations[val], &context.func.stack_slots).unwrap(),
355+
);
356+
357+
let arg_id = self.debug_context.dwarf.unit.add(self.entry_id, gimli::DW_TAG_formal_parameter);
358+
let var_entry = self.debug_context.dwarf.unit.get_mut(arg_id);
359+
360+
var_entry.set(gimli::DW_AT_name, AttributeValue::String(name.into_bytes()));
361+
var_entry.set(gimli::DW_AT_type, AttributeValue::ThisUnitEntryRef(dw_ty));
362+
var_entry.set(gimli::DW_AT_location, AttributeValue::Exprloc(loc));
363+
}
364+
296365
// FIXME make it more reliable and implement scopes before re-enabling this.
297366
if false {
298367
let value_labels_ranges = context.build_value_labels_ranges(isa).unwrap();
299368

300369
for (local, _local_decl) in self.mir.local_decls.iter_enumerated() {
301-
let var_id = self.define_local(format!("{:?}", local), &self.mir.local_decls[local].ty);
370+
let ty = self.debug_context.tcx.subst_and_normalize_erasing_regions(
371+
self.instance.substs,
372+
ty::ParamEnv::reveal_all(),
373+
&self.mir.local_decls[local].ty,
374+
);
375+
let var_id = self.define_local(format!("{:?}", local), ty);
302376

303377
let location = place_location(
304378
self,

0 commit comments

Comments
 (0)