Skip to content

Commit 85c3538

Browse files
d-e-s-odanielocfb
authored andcommitted
Implement iteration over DWARF symbols
Add support for iterating over DWARF symbols in an ELF file. So far only iteration over ELF symbols was supported. Unfortunately that subtly changes the existing behavior: it was entirely allowed for users to set debug_syms, but we would only iterate over ELF symbols. With this change we will consult *only* DWARF symbols instead. Signed-off-by: Daniel Müller <[email protected]>
1 parent abf4e65 commit 85c3538

File tree

7 files changed

+112
-29
lines changed

7 files changed

+112
-29
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
Unreleased
22
----------
3+
- Added support for iteration over DWARF symbols to `inspect::Inspector`
34
- Adjusted normalization logic to use "symbolic path" for reading build
45
IDs when normalizing with `NormalizeOpts::map_files` equal to `false`
56
- Adjusted `inspect::Inspector::for_each` to accept callback returning

src/dwarf/resolver.rs

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use std::fmt::Formatter;
77
use std::fmt::Result as FmtResult;
88
use std::mem;
99
use std::mem::swap;
10+
use std::ops::ControlFlow;
1011
use std::ops::Deref as _;
1112
use std::path::Path;
1213
use std::path::PathBuf;
@@ -329,11 +330,24 @@ impl Inspect for DwarfResolver {
329330
}
330331
}
331332

332-
fn for_each(&self, _opts: &FindAddrOpts, _f: &mut ForEachFn<'_>) -> Result<()> {
333-
// TODO: Implement this functionality.
334-
Err(Error::with_unsupported(
335-
"DWARF logic does not currently support symbol iteration",
336-
))
333+
fn for_each(&self, opts: &FindAddrOpts, f: &mut ForEachFn<'_>) -> Result<()> {
334+
if let SymType::Variable = opts.sym_type {
335+
return Err(Error::with_unsupported("not implemented"))
336+
}
337+
338+
let mut overall_result = Ok(());
339+
let () = self.units.for_each_function(|func| {
340+
let result = self.function_to_sym_info(func, opts.offset_in_file);
341+
match result {
342+
Ok(Some(sym_info)) => f(&sym_info),
343+
Ok(None) => ControlFlow::Continue(()),
344+
Err(err) => {
345+
overall_result = Err(err);
346+
ControlFlow::Break(())
347+
}
348+
}
349+
})?;
350+
overall_result
337351
}
338352
}
339353

@@ -349,7 +363,7 @@ impl Debug for DwarfResolver {
349363
// directly.
350364
impl<'dwarf> Units<'dwarf> {
351365
/// Fill in source code information for an address to the provided
352-
/// `IntSym`.
366+
/// `ResolvedSym`.
353367
///
354368
/// `addr` is a normalized address.
355369
fn fill_code_info<'slf>(

src/dwarf/unit.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,6 @@ impl<'dwarf> Unit<'dwarf> {
6767
}
6868
}
6969

70-
#[cfg(test)]
71-
#[cfg(feature = "nightly")]
7270
pub(super) fn parse_functions<'unit>(
7371
&'unit self,
7472
units: &Units<'dwarf>,

src/dwarf/units.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
// > IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
2626
// > DEALINGS IN THE SOFTWARE.
2727

28+
use std::ops::ControlFlow;
29+
2830
use crate::log::warn;
2931
use crate::once::OnceCell;
3032
use crate::ErrorExt as _;
@@ -408,6 +410,22 @@ impl<'dwarf> Units<'dwarf> {
408410
.filter_map(move |unit| unit.find_name(name, self).transpose())
409411
}
410412

413+
pub(crate) fn for_each_function<F>(&self, mut f: F) -> Result<(), gimli::Error>
414+
where
415+
F: FnMut(&Function<'dwarf>) -> ControlFlow<()>,
416+
{
417+
for unit in self.units.iter() {
418+
let functions = unit.parse_functions(self)?;
419+
420+
for function in functions.functions.iter() {
421+
if let ControlFlow::Break(()) = f(function) {
422+
return Ok(())
423+
}
424+
}
425+
}
426+
Ok(())
427+
}
428+
411429
/// Retrieve a [`gimli::UnitRef`] for the provided `unit`.
412430
#[inline]
413431
pub(crate) fn unit_ref<'unit>(

src/elf/resolver.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
use std::fmt::Debug;
22
use std::fmt::Formatter;
33
use std::fmt::Result as FmtResult;
4-
use std::ops::Deref as _;
54
use std::path::Path;
65
use std::path::PathBuf;
76
use std::rc::Rc;
@@ -203,8 +202,11 @@ impl Inspect for ElfResolver {
203202
}
204203

205204
fn for_each(&self, opts: &FindAddrOpts, f: &mut ForEachFn<'_>) -> Result<()> {
206-
let parser = self.parser();
207-
parser.deref().for_each(opts, f)
205+
match &self.backend {
206+
#[cfg(feature = "dwarf")]
207+
ElfBackend::Dwarf(dwarf) => dwarf.for_each(opts, f),
208+
ElfBackend::Elf(parser) => parser.for_each(opts, f),
209+
}
208210
}
209211
}
210212

src/inspect/inspector.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -154,13 +154,13 @@ impl Inspector {
154154
/// callback function.
155155
///
156156
/// # Notes
157-
/// - no symbol name demangling is performed currently
158-
/// - currently only function symbols (as opposed to variables) are reported
157+
/// - no symbol name demangling is performed and data is reported as it
158+
/// appears in the symbol source
159159
/// - undefined symbols (such as ones referencing a different shared object)
160160
/// are not reported
161-
/// - for the [`Elf`](Source::Elf) source, at present DWARF symbols are
162-
/// ignored (irrespective of the [`debug_syms`][Elf::debug_syms]
163-
/// configuration)
161+
/// - for the [`Elf`](Source::Elf) source:
162+
/// - if `debug_syms` is set, *only* debug symbols are consulted and no
163+
/// support for variables is present
164164
/// - for the [`Breakpad`](Source::Breakpad) source:
165165
/// - no variable support is present
166166
/// - file offsets won't be reported

tests/blazesym.rs

Lines changed: 63 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1121,11 +1121,18 @@ fn inspect_elf_file_offset() {
11211121
}
11221122

11231123

1124-
/// Check that we can iterate over all symbols in an ELF file.
1124+
/// Check that we can iterate over all symbols in a symbolization source.
11251125
#[test]
1126-
fn inspect_elf_breakpad_all_symbols() {
1126+
fn inspect_elf_dwarf_breakpad_all_symbols() {
11271127
fn test(src: &inspect::Source) {
11281128
let breakpad = matches!(src, inspect::Source::Breakpad(..));
1129+
let dwarf = matches!(
1130+
src,
1131+
inspect::Source::Elf(inspect::Elf {
1132+
debug_syms: true,
1133+
..
1134+
})
1135+
);
11291136
let inspector = Inspector::new();
11301137
let mut syms = HashMap::<String, inspect::SymInfo>::new();
11311138
let () = inspector
@@ -1135,8 +1142,8 @@ fn inspect_elf_breakpad_all_symbols() {
11351142
})
11361143
.unwrap();
11371144

1138-
// Breakpad doesn't contain any or any reasonable information for
1139-
// some symbols.
1145+
// Breakpad and DWARF don't contain any or any reasonable information
1146+
// for some symbols.
11401147
if !breakpad {
11411148
let sym = syms.get("main").unwrap();
11421149
assert_eq!(sym.sym_type, SymType::Function);
@@ -1151,7 +1158,7 @@ fn inspect_elf_breakpad_all_symbols() {
11511158
let sym = syms.get("factorial_inline_test").unwrap();
11521159
assert_eq!(sym.sym_type, SymType::Function);
11531160

1154-
if !breakpad {
1161+
if !breakpad && !dwarf {
11551162
let sym = syms.get("indirect_func").unwrap();
11561163
assert_eq!(sym.sym_type, SymType::Function);
11571164
}
@@ -1162,16 +1169,24 @@ fn inspect_elf_breakpad_all_symbols() {
11621169
let sym = syms.get("resolve_indirect_func").unwrap();
11631170
assert_eq!(sym.sym_type, SymType::Function);
11641171

1165-
if !breakpad {
1172+
if !breakpad && !dwarf {
11661173
let sym = syms.get("a_variable").unwrap();
11671174
assert_eq!(sym.sym_type, SymType::Variable);
11681175
}
11691176
}
11701177

1171-
let test_elf = Path::new(&env!("CARGO_MANIFEST_DIR"))
1178+
let path = Path::new(&env!("CARGO_MANIFEST_DIR"))
11721179
.join("data")
11731180
.join("test-stable-addrs-no-dwarf.bin");
1174-
let elf = inspect::Elf::new(test_elf);
1181+
let mut elf = inspect::Elf::new(path);
1182+
elf.debug_syms = false;
1183+
let src = inspect::Source::Elf(elf);
1184+
test(&src);
1185+
1186+
let path = Path::new(&env!("CARGO_MANIFEST_DIR"))
1187+
.join("data")
1188+
.join("test-stable-addrs-stripped-elf-with-dwarf.bin");
1189+
let elf = inspect::Elf::new(path);
11751190
let src = inspect::Source::Elf(elf);
11761191
test(&src);
11771192

@@ -1186,7 +1201,7 @@ fn inspect_elf_breakpad_all_symbols() {
11861201

11871202
/// Check that early stopping of symbol iteration works as expected.
11881203
#[test]
1189-
fn inspect_elf_breakpad_early_iter_stop() {
1204+
fn inspect_elf_dwarf_breakpad_early_iter_stop() {
11901205
fn test(src: &inspect::Source) {
11911206
let mut i = 0;
11921207
let inspector = Inspector::new();
@@ -1202,10 +1217,18 @@ fn inspect_elf_breakpad_early_iter_stop() {
12021217
.unwrap();
12031218
}
12041219

1205-
let test_elf = Path::new(&env!("CARGO_MANIFEST_DIR"))
1220+
let path = Path::new(&env!("CARGO_MANIFEST_DIR"))
12061221
.join("data")
12071222
.join("test-stable-addrs-no-dwarf.bin");
1208-
let elf = inspect::Elf::new(test_elf);
1223+
let mut elf = inspect::Elf::new(path);
1224+
elf.debug_syms = false;
1225+
let src = inspect::Source::Elf(elf);
1226+
test(&src);
1227+
1228+
let path = Path::new(&env!("CARGO_MANIFEST_DIR"))
1229+
.join("data")
1230+
.join("test-stable-addrs-stripped-elf-with-dwarf.bin");
1231+
let elf = inspect::Elf::new(path);
12091232
let src = inspect::Source::Elf(elf);
12101233
test(&src);
12111234

@@ -1218,14 +1241,41 @@ fn inspect_elf_breakpad_early_iter_stop() {
12181241
}
12191242

12201243

1244+
/// Make sure that the `debug_syms` flag is honored.
1245+
#[test]
1246+
fn inspect_debug_syms_flag() {
1247+
let path = Path::new(&env!("CARGO_MANIFEST_DIR"))
1248+
.join("data")
1249+
.join("test-stable-addrs-no-dwarf.bin");
1250+
let mut elf = inspect::Elf::new(path);
1251+
elf.debug_syms = true;
1252+
let src = inspect::Source::Elf(elf);
1253+
let inspector = Inspector::new();
1254+
// There aren't any debug symbols in the source (although there are ELF
1255+
// symbols).
1256+
let () = inspector.for_each(&src, |_sym| panic!()).unwrap();
1257+
1258+
let path = Path::new(&env!("CARGO_MANIFEST_DIR"))
1259+
.join("data")
1260+
.join("test-stable-addrs-stripped-elf-with-dwarf.bin");
1261+
let mut elf = inspect::Elf::new(path);
1262+
elf.debug_syms = false;
1263+
let src = inspect::Source::Elf(elf);
1264+
// There aren't any ELF symbols in the source (although there are DWARF
1265+
// symbols).
1266+
let () = inspector.for_each(&src, |_sym| panic!()).unwrap();
1267+
}
1268+
1269+
12211270
/// Check that we can iterate over all symbols in an ELF file, without
12221271
/// encountering duplicates caused by dynamic/static symbol overlap.
12231272
#[test]
12241273
fn inspect_elf_all_symbols_without_duplicates() {
1225-
let test_elf = Path::new(&env!("CARGO_MANIFEST_DIR"))
1274+
let path = Path::new(&env!("CARGO_MANIFEST_DIR"))
12261275
.join("data")
12271276
.join("libtest-so.so");
1228-
let elf = inspect::Elf::new(test_elf);
1277+
let mut elf = inspect::Elf::new(path);
1278+
elf.debug_syms = false;
12291279
let src = inspect::Source::Elf(elf);
12301280

12311281
let inspector = Inspector::new();

0 commit comments

Comments
 (0)