[v3,1/4] libdw: Handle split DWARF in dwarf_decl_file
Commit Message
From: Omar Sandoval <osandov@fb.com>
Calling dwarf_decl_file on a split DWARF DIE fails this assertion:
dwarf_decl_file.c:72: dwarf_decl_file: Assertion `cu->files != NULL && cu->files != (void *) -1l' failed.
This is because dwarf_decl_file calls dwarf_getsrclines to populate
cu->files. For normal units, cu->files is cached by dwarf_getsrclines
when it parses the line number information. However, for split units,
the line number information is parsed for the skeleton unit, then copied
to the split unit's cu->lines. Split units have their own file name
table, so cu->files is not copied.
The obvious solution is to use dwarf_getsrcfiles instead of relying on
implicit caching.
Also add a test case for dwarf_decl_file.
* libdw/dwarf_decl_file.c (dwarf_decl_file): Use
dwarf_getsrcfiles instead of dwarf_getsrclines.
* tests/Makefile.am (check_PROGRAMS): Add declfiles.
(TESTS): Add run-declfiles.sh.
(EXTRA_DIST): Add run-declfiles.sh.
(declfiles_LDADD): New variable.
* tests/declfiles.c: New test.
* tests/run-declfiles.sh: New test.
Signed-off-by: Omar Sandoval <osandov@fb.com>
---
libdw/dwarf_decl_file.c | 30 ++----
tests/.gitignore | 1 +
tests/Makefile.am | 8 +-
tests/declfiles.c | 90 ++++++++++++++++
tests/run-declfiles.sh | 233 ++++++++++++++++++++++++++++++++++++++++
5 files changed, 335 insertions(+), 27 deletions(-)
create mode 100644 tests/declfiles.c
create mode 100755 tests/run-declfiles.sh
Comments
Hi Omar,
On Mon, Feb 26, 2024 at 11:32:48AM -0800, Omar Sandoval wrote:
> Calling dwarf_decl_file on a split DWARF DIE fails this assertion:
>
> dwarf_decl_file.c:72: dwarf_decl_file: Assertion `cu->files != NULL && cu->files != (void *) -1l' failed.
>
> This is because dwarf_decl_file calls dwarf_getsrclines to populate
> cu->files. For normal units, cu->files is cached by dwarf_getsrclines
> when it parses the line number information. However, for split units,
> the line number information is parsed for the skeleton unit, then copied
> to the split unit's cu->lines. Split units have their own file name
> table, so cu->files is not copied.
>
> The obvious solution is to use dwarf_getsrcfiles instead of relying on
> implicit caching.
And it cleans up the code nicely.
> Also add a test case for dwarf_decl_file.
Thanks.
>
> * libdw/dwarf_decl_file.c (dwarf_decl_file): Use
> dwarf_getsrcfiles instead of dwarf_getsrclines.
> * tests/Makefile.am (check_PROGRAMS): Add declfiles.
> (TESTS): Add run-declfiles.sh.
> (EXTRA_DIST): Add run-declfiles.sh.
> (declfiles_LDADD): New variable.
> * tests/declfiles.c: New test.
> * tests/run-declfiles.sh: New test.
All looks good. Pushed.
Cheers,
Mark
@@ -48,35 +48,17 @@ dwarf_decl_file (Dwarf_Die *die)
&idx) != 0)
return NULL;
- /* Get the array of source files for the CU. */
- struct Dwarf_CU *cu = attr_mem.cu;
- if (cu->lines == NULL)
- {
- Dwarf_Lines *lines;
- size_t nlines;
+ Dwarf_Files *files;
+ size_t nfiles;
+ if (INTUSE(dwarf_getsrcfiles) (&CUDIE (attr_mem.cu), &files, &nfiles) != 0)
+ return NULL;
- /* Let the more generic function do the work. It'll create more
- data but that will be needed in an real program anyway. */
- (void) INTUSE(dwarf_getsrclines) (&CUDIE (cu), &lines, &nlines);
- assert (cu->lines != NULL);
- }
-
- if (cu->lines == (void *) -1l)
- {
- /* If DW_AT_decl_file was present, there should be file information
- available. */
- __libdw_seterrno (DWARF_E_INVALID_DWARF);
- return NULL;
- }
-
- assert (cu->files != NULL && cu->files != (void *) -1l);
-
- if (idx >= cu->files->nfiles)
+ if (idx >= nfiles)
{
__libdw_seterrno (DWARF_E_INVALID_DWARF);
return NULL;
}
- return cu->files->info[idx].name;
+ return files->info[idx].name;
}
OLD_VERSION (dwarf_decl_file, ELFUTILS_0.122)
@@ -32,6 +32,7 @@
/debugaltlink
/debuginfod_build_id_find
/debuglink
+/declfiles
/deleted
/dwarf-die-addr-die
/dwarf-getmacros
@@ -63,7 +63,7 @@ check_PROGRAMS = arextract arsymtest newfile saridx scnnames sectiondump \
getphdrnum leb128 read_unaligned \
msg_tst system-elf-libelf-test system-elf-gelf-test \
nvidia_extended_linemap_libdw elf-print-reloc-syms \
- cu-dwp-section-info \
+ cu-dwp-section-info declfiles \
$(asm_TESTS)
asm_TESTS = asm-tst1 asm-tst2 asm-tst3 asm-tst4 asm-tst5 \
@@ -213,7 +213,8 @@ TESTS = run-arextract.sh run-arsymtest.sh run-ar.sh newfile test-nlist \
$(asm_TESTS) run-disasm-bpf.sh run-low_high_pc-dw-form-indirect.sh \
run-nvidia-extended-linemap-libdw.sh run-nvidia-extended-linemap-readelf.sh \
run-readelf-dw-form-indirect.sh run-strip-largealign.sh \
- run-readelf-Dd.sh run-dwfl-core-noncontig.sh run-cu-dwp-section-info.sh
+ run-readelf-Dd.sh run-dwfl-core-noncontig.sh run-cu-dwp-section-info.sh \
+ run-declfiles.sh
if !BIARCH
export ELFUTILS_DISABLE_BIARCH = 1
@@ -640,7 +641,7 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh run-ar.sh \
testfile-dwp-4.bz2 testfile-dwp-4.dwp.bz2 \
testfile-dwp-4-strict.bz2 testfile-dwp-4-strict.dwp.bz2 \
testfile-dwp-5.bz2 testfile-dwp-5.dwp.bz2 testfile-dwp.source \
- run-cu-dwp-section-info.sh
+ run-cu-dwp-section-info.sh run-declfiles.sh
if USE_VALGRIND
@@ -818,6 +819,7 @@ read_unaligned_LDADD = $(libelf) $(libdw)
nvidia_extended_linemap_libdw_LDADD = $(libelf) $(libdw)
elf_print_reloc_syms_LDADD = $(libelf)
cu_dwp_section_info_LDADD = $(libdw)
+declfiles_LDADD = $(libdw)
# We want to test the libelf headers against the system elf.h header.
# Don't include any -I CPPFLAGS. Except when we install our own elf.h.
new file mode 100644
@@ -0,0 +1,90 @@
+/* Test program for dwarf_decl_file
+ Copyright (c) 2024 Meta Platforms, Inc. and affiliates.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <fcntl.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <dwarf.h>
+#include ELFUTILS_HEADER(dw)
+
+static void
+walk_tree (Dwarf_Die *dwarf_die, int indent)
+{
+ Dwarf_Die die = *dwarf_die;
+ do
+ {
+ int child_indent = indent;
+ const char *file = dwarf_decl_file (&die);
+ if (file != NULL)
+ {
+ printf("%*s", indent, "");
+ const char *name = dwarf_diename (&die) ?: "???";
+ int line, column;
+ if (dwarf_decl_line (&die, &line) == 0)
+ {
+ if (dwarf_decl_column (&die, &column) == 0)
+ printf ("%s@%s:%d:%d\n", name, file, line, column);
+ else
+ printf ("%s@%s:%d\n", name, file, line);
+ }
+ else
+ printf ("%s@%s\n", name, file);
+ child_indent++;
+ }
+
+ Dwarf_Die child;
+ if (dwarf_child (&die, &child) == 0)
+ walk_tree (&child, child_indent);
+ }
+ while (dwarf_siblingof (&die, &die) == 0);
+}
+
+int
+main (int argc, char *argv[])
+{
+ for (int i = 1; i < argc; i++)
+ {
+ printf ("file: %s\n", argv[i]);
+ int fd = open (argv[i], O_RDONLY);
+ Dwarf *dbg = dwarf_begin (fd, DWARF_C_READ);
+ if (dbg == NULL)
+ {
+ printf ("%s not usable: %s\n", argv[i], dwarf_errmsg (-1));
+ return -1;
+ }
+
+ Dwarf_CU *cu = NULL;
+ Dwarf_Die cudie, subdie;
+ uint8_t unit_type;
+ while (dwarf_get_units (dbg, cu, &cu, NULL, &unit_type, &cudie, &subdie)
+ == 0)
+ {
+ Dwarf_Die *die = unit_type == DW_UT_skeleton ? &subdie : &cudie;
+ printf (" cu: %s\n", dwarf_diename (die) ?: "???");
+ walk_tree (die, 2);
+ }
+
+ dwarf_end (dbg);
+ close (fd);
+ }
+
+ return 0;
+}
new file mode 100755
@@ -0,0 +1,233 @@
+#! /bin/sh
+# Copyright (c) 2024 Meta Platforms, Inc. and affiliates.
+# This file is part of elfutils.
+#
+# This file is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# elfutils is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+. $srcdir/test-subr.sh
+
+# see tests/testfile-dwarf-45.source
+testfiles testfile-dwarf-4 testfile-dwarf-5
+testfiles testfile-splitdwarf-4 testfile-hello4.dwo testfile-world4.dwo
+testfiles testfile-splitdwarf-5 testfile-hello5.dwo testfile-world5.dwo
+
+testrun_compare ${abs_builddir}/declfiles testfile-dwarf-4 << EOF
+file: testfile-dwarf-4
+ cu: hello.c
+ wchar_t@/opt/local/install/gcc/lib/gcc/x86_64-pc-linux-gnu/9.0.0/include/stddef.h:328:24
+ m@/var/tmp/hello/hello.h:1:12
+ m@/var/tmp/hello/hello.c:5:5
+ foo@/var/tmp/hello/hello.c:20:1
+ f@/var/tmp/hello/hello.c:20:14
+ baz@/var/tmp/hello/hello.c:8:5
+ x@/var/tmp/hello/hello.c:8:14
+ r@/var/tmp/hello/hello.c:10:7
+ frob@/var/tmp/hello/hello.h:5:1
+ a@/var/tmp/hello/hello.h:5:11
+ b@/var/tmp/hello/hello.h:5:18
+ c@/var/tmp/hello/hello.h:7:7
+ foo@/var/tmp/hello/hello.c:20:1
+ f@/var/tmp/hello/hello.c:20:14
+ frob@/var/tmp/hello/hello.h:5:1
+ b@/var/tmp/hello/hello.h:5:18
+ a@/var/tmp/hello/hello.h:5:11
+ c@/var/tmp/hello/hello.h:7:7
+ baz@/var/tmp/hello/hello.c:8:5
+ x@/var/tmp/hello/hello.c:8:14
+ r@/var/tmp/hello/hello.c:10:7
+ foo@/var/tmp/hello/hello.c:20:1
+ f@/var/tmp/hello/hello.c:20:14
+ main@/var/tmp/hello/hello.c:4:12
+ baz@/var/tmp/hello/hello.c:8:5
+ x@/var/tmp/hello/hello.c:8:14
+ r@/var/tmp/hello/hello.c:10:7
+ foo@/var/tmp/hello/hello.c:20:1
+ main@/var/tmp/hello/hello.c:4:12
+ cu: world.c
+ m@/var/tmp/hello/hello.h:1:12
+ main@/var/tmp/hello/world.c:14:1
+ argc@/var/tmp/hello/world.c:14:11
+ argv@/var/tmp/hello/world.c:14:30
+ n@/var/tmp/hello/world.c:16:15
+ calc@/var/tmp/hello/world.c:5:1
+ exit@/usr/include/stdlib.h:542:13
+ calc@/var/tmp/hello/world.c:5:1
+ word@/var/tmp/hello/world.c:5:19
+ frob@/var/tmp/hello/hello.h:5:1
+ b@/var/tmp/hello/hello.h:5:18
+ a@/var/tmp/hello/hello.h:5:11
+ c@/var/tmp/hello/hello.h:7:7
+ baz@/var/tmp/hello/hello.h:2:12
+ frob@/var/tmp/hello/hello.h:5:1
+ a@/var/tmp/hello/hello.h:5:11
+ b@/var/tmp/hello/hello.h:5:18
+ c@/var/tmp/hello/hello.h:7:7
+ exit@/usr/include/stdlib.h:542:13
+ baz@/var/tmp/hello/hello.h:2:12
+EOF
+
+testrun_compare ${abs_builddir}/declfiles testfile-dwarf-5 << EOF
+file: testfile-dwarf-5
+ cu: hello.c
+ wchar_t@/opt/local/install/gcc/lib/gcc/x86_64-pc-linux-gnu/9.0.0/include/stddef.h:328:24
+ m@/var/tmp/hello/hello.h:1:12
+ m@/var/tmp/hello/hello.c:5:5
+ foo@/var/tmp/hello/hello.c:20:1
+ f@/var/tmp/hello/hello.c:20:14
+ baz@/var/tmp/hello/hello.c:8:5
+ x@/var/tmp/hello/hello.c:8:14
+ r@/var/tmp/hello/hello.c:10:7
+ frob@/var/tmp/hello/hello.h:5:1
+ a@/var/tmp/hello/hello.h:5:11
+ b@/var/tmp/hello/hello.h:5:18
+ c@/var/tmp/hello/hello.h:7:7
+ foo@/var/tmp/hello/hello.c:20:1
+ f@/var/tmp/hello/hello.c:20:14
+ frob@/var/tmp/hello/hello.h:5:1
+ b@/var/tmp/hello/hello.h:5:18
+ a@/var/tmp/hello/hello.h:5:11
+ c@/var/tmp/hello/hello.h:7:7
+ baz@/var/tmp/hello/hello.c:8:5
+ x@/var/tmp/hello/hello.c:8:14
+ r@/var/tmp/hello/hello.c:10:7
+ foo@/var/tmp/hello/hello.c:20:1
+ f@/var/tmp/hello/hello.c:20:14
+ baz@/var/tmp/hello/hello.c:8:5
+ x@/var/tmp/hello/hello.c:8:14
+ r@/var/tmp/hello/hello.c:10:7
+ main@/var/tmp/hello/hello.c:4:12
+ cu: world.c
+ m@/var/tmp/hello/hello.h:1:12
+ main@/var/tmp/hello/world.c:14:1
+ argc@/var/tmp/hello/world.c:14:11
+ argv@/var/tmp/hello/world.c:14:30
+ n@/var/tmp/hello/world.c:16:15
+ calc@/var/tmp/hello/world.c:5:1
+ word@/var/tmp/hello/world.c:5:19
+ frob@/var/tmp/hello/hello.h:5:1
+ b@/var/tmp/hello/hello.h:5:18
+ a@/var/tmp/hello/hello.h:5:11
+ c@/var/tmp/hello/hello.h:7:7
+ frob@/var/tmp/hello/hello.h:5:1
+ a@/var/tmp/hello/hello.h:5:11
+ b@/var/tmp/hello/hello.h:5:18
+ c@/var/tmp/hello/hello.h:7:7
+ exit@/usr/include/stdlib.h:542:13
+ baz@/var/tmp/hello/hello.h:2:12
+EOF
+
+testrun_compare ${abs_builddir}/declfiles testfile-splitdwarf-4 << EOF
+file: testfile-splitdwarf-4
+ cu: hello.c
+ wchar_t@/opt/local/install/gcc/lib/gcc/x86_64-pc-linux-gnu/9.0.0/include/stddef.h:328:24
+ m@/home/mark/src/elfutils/tests/hello.h:1:12
+ m@/home/mark/src/elfutils/tests/hello.c:5:5
+ foo@/home/mark/src/elfutils/tests/hello.c:20:1
+ f@/home/mark/src/elfutils/tests/hello.c:20:14
+ baz@/home/mark/src/elfutils/tests/hello.c:8:5
+ x@/home/mark/src/elfutils/tests/hello.c:8:14
+ r@/home/mark/src/elfutils/tests/hello.c:10:7
+ frob@/home/mark/src/elfutils/tests/hello.h:5:1
+ a@/home/mark/src/elfutils/tests/hello.h:5:11
+ b@/home/mark/src/elfutils/tests/hello.h:5:18
+ c@/home/mark/src/elfutils/tests/hello.h:7:7
+ foo@/home/mark/src/elfutils/tests/hello.c:20:1
+ f@/home/mark/src/elfutils/tests/hello.c:20:14
+ frob@/home/mark/src/elfutils/tests/hello.h:5:1
+ b@/home/mark/src/elfutils/tests/hello.h:5:18
+ a@/home/mark/src/elfutils/tests/hello.h:5:11
+ c@/home/mark/src/elfutils/tests/hello.h:7:7
+ baz@/home/mark/src/elfutils/tests/hello.c:8:5
+ x@/home/mark/src/elfutils/tests/hello.c:8:14
+ r@/home/mark/src/elfutils/tests/hello.c:10:7
+ foo@/home/mark/src/elfutils/tests/hello.c:20:1
+ f@/home/mark/src/elfutils/tests/hello.c:20:14
+ main@/home/mark/src/elfutils/tests/hello.c:4:12
+ baz@/home/mark/src/elfutils/tests/hello.c:8:5
+ x@/home/mark/src/elfutils/tests/hello.c:8:14
+ r@/home/mark/src/elfutils/tests/hello.c:10:7
+ foo@/home/mark/src/elfutils/tests/hello.c:20:1
+ main@/home/mark/src/elfutils/tests/hello.c:4:12
+ cu: world.c
+ m@/home/mark/src/elfutils/tests/hello.h:1:12
+ main@/home/mark/src/elfutils/tests/world.c:14:1
+ argc@/home/mark/src/elfutils/tests/world.c:14:11
+ argv@/home/mark/src/elfutils/tests/world.c:14:30
+ n@/home/mark/src/elfutils/tests/world.c:16:15
+ calc@/home/mark/src/elfutils/tests/world.c:5:1
+ exit@/usr/include/stdlib.h:542:13
+ calc@/home/mark/src/elfutils/tests/world.c:5:1
+ word@/home/mark/src/elfutils/tests/world.c:5:19
+ frob@/home/mark/src/elfutils/tests/hello.h:5:1
+ b@/home/mark/src/elfutils/tests/hello.h:5:18
+ a@/home/mark/src/elfutils/tests/hello.h:5:11
+ c@/home/mark/src/elfutils/tests/hello.h:7:7
+ baz@/home/mark/src/elfutils/tests/hello.h:2:12
+ frob@/home/mark/src/elfutils/tests/hello.h:5:1
+ a@/home/mark/src/elfutils/tests/hello.h:5:11
+ b@/home/mark/src/elfutils/tests/hello.h:5:18
+ c@/home/mark/src/elfutils/tests/hello.h:7:7
+ exit@/usr/include/stdlib.h:542:13
+ baz@/home/mark/src/elfutils/tests/hello.h:2:12
+EOF
+
+testrun_compare ${abs_builddir}/declfiles testfile-splitdwarf-5 << EOF
+file: testfile-splitdwarf-5
+ cu: hello.c
+ wchar_t@/opt/local/install/gcc/lib/gcc/x86_64-pc-linux-gnu/9.0.0/include/stddef.h:328:24
+ m@/home/mark/src/elfutils/tests/hello.h:1:12
+ m@/home/mark/src/elfutils/tests/hello.c:5:5
+ foo@/home/mark/src/elfutils/tests/hello.c:20:1
+ f@/home/mark/src/elfutils/tests/hello.c:20:14
+ baz@/home/mark/src/elfutils/tests/hello.c:8:5
+ x@/home/mark/src/elfutils/tests/hello.c:8:14
+ r@/home/mark/src/elfutils/tests/hello.c:10:7
+ frob@/home/mark/src/elfutils/tests/hello.h:5:1
+ a@/home/mark/src/elfutils/tests/hello.h:5:11
+ b@/home/mark/src/elfutils/tests/hello.h:5:18
+ c@/home/mark/src/elfutils/tests/hello.h:7:7
+ foo@/home/mark/src/elfutils/tests/hello.c:20:1
+ f@/home/mark/src/elfutils/tests/hello.c:20:14
+ frob@/home/mark/src/elfutils/tests/hello.h:5:1
+ b@/home/mark/src/elfutils/tests/hello.h:5:18
+ a@/home/mark/src/elfutils/tests/hello.h:5:11
+ c@/home/mark/src/elfutils/tests/hello.h:7:7
+ baz@/home/mark/src/elfutils/tests/hello.c:8:5
+ x@/home/mark/src/elfutils/tests/hello.c:8:14
+ r@/home/mark/src/elfutils/tests/hello.c:10:7
+ foo@/home/mark/src/elfutils/tests/hello.c:20:1
+ f@/home/mark/src/elfutils/tests/hello.c:20:14
+ baz@/home/mark/src/elfutils/tests/hello.c:8:5
+ x@/home/mark/src/elfutils/tests/hello.c:8:14
+ r@/home/mark/src/elfutils/tests/hello.c:10:7
+ main@/home/mark/src/elfutils/tests/hello.c:4:12
+ cu: world.c
+ m@/home/mark/src/elfutils/tests/hello.h:1:12
+ main@/home/mark/src/elfutils/tests/world.c:14:1
+ argc@/home/mark/src/elfutils/tests/world.c:14:11
+ argv@/home/mark/src/elfutils/tests/world.c:14:30
+ n@/home/mark/src/elfutils/tests/world.c:16:15
+ calc@/home/mark/src/elfutils/tests/world.c:5:1
+ word@/home/mark/src/elfutils/tests/world.c:5:19
+ frob@/home/mark/src/elfutils/tests/hello.h:5:1
+ b@/home/mark/src/elfutils/tests/hello.h:5:18
+ a@/home/mark/src/elfutils/tests/hello.h:5:11
+ c@/home/mark/src/elfutils/tests/hello.h:7:7
+ frob@/home/mark/src/elfutils/tests/hello.h:5:1
+ a@/home/mark/src/elfutils/tests/hello.h:5:11
+ b@/home/mark/src/elfutils/tests/hello.h:5:18
+ c@/home/mark/src/elfutils/tests/hello.h:7:7
+ exit@/usr/include/stdlib.h:542:13
+ baz@/home/mark/src/elfutils/tests/hello.h:2:12
+EOF