libdw: Add new dwarf_cu_info function.

This allows getting a (split) subdie lazily, only when needed.
All arguments to dwarf_get_units are optional. When not given
then unit DIE and sub DIE are not looked up. This new function
allows them to be looked up when not immediately retrieved with
dwarf_get_units, or for a Dwarf_CU gotten through some other way.

Add a new testcase to make sure the results of calling dwarf_cu_info
and dwarf_get_units are consistent.

Signed-off-by: Mark Wielaard <mark@klomp.org>
This commit is contained in:
Mark Wielaard
2018-05-20 10:35:34 +02:00
parent c2d14cc492
commit ac6960d6b8
9 changed files with 542 additions and 5 deletions
+7
View File
@@ -1,3 +1,10 @@
2018-05-20 Mark Wielaard <mark@klomp.org>
* dwarf_cu_info.c: New file.
* Makefile.am (libdw_a_SOURCES): Add dwarf_cu_info.c.
* libdw.h (dwarf_cu_info): New function declaration.
* libdw.map (ELFUTILS_0.171): Add dwarf_cu_info.
2018-05-24 Mark Wielaard <mark@klomp.org>
* dwarf_ranges.c (dwarf_ranges): Check for NULL cu.
+1 -1
View File
@@ -91,7 +91,7 @@ libdw_a_SOURCES = dwarf_begin.c dwarf_begin_elf.c dwarf_end.c dwarf_getelf.c \
dwarf_getalt.c dwarf_setalt.c dwarf_cu_getdwarf.c \
dwarf_cu_die.c dwarf_peel_type.c dwarf_default_lower_bound.c \
dwarf_die_addr_die.c dwarf_get_units.c \
libdw_find_split_unit.c
libdw_find_split_unit.c dwarf_cu_info.c
if MAINTAINER_MODE
BUILT_SOURCES = $(srcdir)/known-dwarf.h
+103
View File
@@ -0,0 +1,103 @@
/* Provides information and DIEs associated with the Dwarf_CU unit.
Copyright (C) 2018 Red Hat, Inc.
This file is part of elfutils.
This file is free software; you can redistribute it and/or modify
it under the terms of either
* the GNU Lesser General Public License as published by the Free
Software Foundation; either version 3 of the License, or (at
your option) any later version
or
* the GNU General Public License as published by the Free
Software Foundation; either version 2 of the License, or (at
your option) any later version
or both in parallel, as here.
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 copies of the GNU General Public License and
the GNU Lesser General Public License along with this program. If
not, see <http://www.gnu.org/licenses/>. */
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <string.h>
#include "libdwP.h"
int
dwarf_cu_info (Dwarf_CU *cu,
Dwarf_Half *version, uint8_t *unit_type,
Dwarf_Die *cudie, Dwarf_Die *subdie,
uint64_t *unit_id,
uint8_t *address_size, uint8_t *offset_size)
{
if (cu == NULL)
return -1;
if (version != NULL)
*version = cu->version;
if (unit_type != NULL)
*unit_type = cu->unit_type;
if (cudie != NULL)
{
if (cu->version >= 2 && cu->version <= 5
&& cu->unit_type >= DW_UT_compile
&& cu->unit_type <= DW_UT_split_type)
*cudie = CUDIE (cu);
else
{
invalid:
__libdw_seterrno (DWARF_E_INVALID_DWARF);
return -1;
}
}
if (subdie != NULL)
{
if (cu->version >= 2 && cu->version <= 5)
{
/* For types, return the actual type DIE. For skeletons,
find the associated split compile unit and return its
DIE. */
if (cu->unit_type == DW_UT_type
|| cu->unit_type == DW_UT_split_type)
*subdie = SUBDIE(cu);
else if (cu->unit_type == DW_UT_skeleton)
{
Dwarf_CU *split_cu = __libdw_find_split_unit (cu);
if (split_cu != NULL)
*subdie = CUDIE(split_cu);
else
memset (subdie, '\0', sizeof (Dwarf_Die));
}
else
memset (subdie, '\0', sizeof (Dwarf_Die));
}
else
goto invalid;
}
if (unit_id != NULL)
*unit_id = cu->unit_id8;
if (address_size != NULL)
*address_size = cu->address_size;
if (offset_size != NULL)
*offset_size = cu->offset_size;
return 0;
}
+13 -1
View File
@@ -297,13 +297,25 @@ extern int dwarf_next_unit (Dwarf *dwarf, Dwarf_Off off, Dwarf_Off *next_off,
DW_UT_type for DW_TAG_type_unit or DW_UT_partial for
DW_TAG_partial_unit), otherwise it is set to zero. If unavailable
(the version or unit type is unknown) the CU DIE is cleared.
Likewise ff the sub DIE isn't isn't available (the unit type is not
Likewise if the sub DIE isn't isn't available (the unit type is not
DW_UT_type or DW_UT_split_type) the sub DIE tag is cleared. */
extern int dwarf_get_units (Dwarf *dwarf, Dwarf_CU *cu, Dwarf_CU **next_cu,
Dwarf_Half *version, uint8_t *unit_type,
Dwarf_Die *cudie, Dwarf_Die *subdie)
__nonnull_attribute__ (3);
/* Provides information and DIEs associated with the given Dwarf_CU
unit. Returns -1 on error, zero on success. Arguments not needed
may be NULL. If they are NULL and aren't known yet, they won't be
looked up. If the subdie doesn't exist for this unit_type it will
be cleared. If there is no unit_id for this unit type it will be
set to zero. */
extern int dwarf_cu_info (Dwarf_CU *cu,
Dwarf_Half *version, uint8_t *unit_type,
Dwarf_Die *cudie, Dwarf_Die *subdie,
uint64_t *unit_id,
uint8_t *address_size, uint8_t *offset_size);
/* Decode one DWARF CFI entry (CIE or FDE) from the raw section data.
The E_IDENT from the originating ELF file indicates the address
size and byte order used in the CFI section contained in DATA;
+1
View File
@@ -350,4 +350,5 @@ ELFUTILS_0.171 {
dwarf_die_addr_die;
dwarf_get_units;
dwarf_getabbrevattr_data;
dwarf_cu_info;
} ELFUTILS_0.170;
+9
View File
@@ -1,3 +1,12 @@
2018-05-20 Mark Wielaard <mark@klomp.org>
* unit-info.c: New test.
* run-unit-info.sh: New test runner.
* Makefile.am (check_PROGRAMS): Add unit-info.
(TESTS): Add run-unit-info.sh
(EXTRA_INFO): Likewise.
(unit_info_LDADD): New variable.
2018-05-24 Mark Wielaard <mark@klomp.org>
* get-units-invalid.c (main): Add check for invalid dwarf_ranges.
+5 -3
View File
@@ -57,7 +57,7 @@ check_PROGRAMS = arextract arsymtest newfile saridx scnnames sectiondump \
elfgetzdata elfputzdata zstrptr emptyfile vendorelf \
fillfile dwarf_default_lower_bound dwarf-die-addr-die \
get-units-invalid get-units-split attr-integrate-skel \
all-dwarf-ranges
all-dwarf-ranges unit-info
asm_TESTS = asm-tst1 asm-tst2 asm-tst3 asm-tst4 asm-tst5 \
asm-tst6 asm-tst7 asm-tst8 asm-tst9
@@ -143,7 +143,7 @@ TESTS = run-arextract.sh run-arsymtest.sh run-ar.sh newfile test-nlist \
run-dwarf-die-addr-die.sh \
run-get-units-invalid.sh run-get-units-split.sh \
run-attr-integrate-skel.sh \
run-all-dwarf-ranges.sh
run-all-dwarf-ranges.sh run-unit-info.sh
if !BIARCH
export ELFUTILS_DISABLE_BIARCH = 1
@@ -370,7 +370,8 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh run-ar.sh \
testfile-world5.dwo.bz2 testfile-world4.dwo.bz2 \
run-attr-integrate-skel.sh \
run-all-dwarf-ranges.sh testfilesplitranges4.debug.bz2 \
testfile-ranges-hello.dwo.bz2 testfile-ranges-world.dwo.bz2
testfile-ranges-hello.dwo.bz2 testfile-ranges-world.dwo.bz2 \
run-unit-info.sh
if USE_VALGRIND
valgrind_cmd='valgrind -q --leak-check=full --error-exitcode=1'
@@ -533,6 +534,7 @@ get_units_invalid_LDADD = $(libdw)
get_units_split_LDADD = $(libdw)
attr_integrate_skel_LDADD = $(libdw)
all_dwarf_ranges_LDADD = $(libdw)
unit_info_LDADD = $(libdw)
# We want to test the libelf header against the system elf.h header.
# Don't include any -I CPPFLAGS.
+80
View File
@@ -0,0 +1,80 @@
#! /bin/sh
# Copyright (C) 2018 Red Hat, Inc.
# 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 run-typeiter.sh
testfiles testfile-debug-types
testrun ${abs_builddir}/unit-info testfile-debug-types
# see run-readelf-dwz-multi.sh
testfiles testfile_multi_main testfile_multi.dwz
testrun ${abs_builddir}/unit-info testfile_multi_main
# see tests/run-dwflsyms.sh
testfiles testfilebazdbgppc64.debug
testrun ${abs_builddir}/unit-info testfilebazdbgppc64.debug
# see tests/testfile-dwarf-45.source
testfiles testfile-dwarf-4 testfile-dwarf-5
testfiles testfile-splitdwarf-4 testfile-splitdwarf-5
testfiles testfile-hello4.dwo testfile-hello5.dwo
testfiles testfile-world4.dwo testfile-world5.dwo
testrun ${abs_builddir}/unit-info testfile-dwarf-4
testrun ${abs_builddir}/unit-info testfile-dwarf-5
# The consistency checks should find most issue, but make sure the
# output is also what we expect in case we break dwarf_get_units and
# dwarf_cu_info at the same time.
testrun_compare ${abs_builddir}/unit-info \
testfile-splitdwarf-4 testfile-splitdwarf-5 <<\EOF
file: testfile-splitdwarf-4
Iterate getting all info, compare with dwarf_cu_info.
0 cu dietag: 11, subtag: 11, version 4, unit_type 4
0 subdietag: 11, subtag: 0, version 4, unit_type 5
1 cu dietag: 11, subtag: 11, version 4, unit_type 4
1 subdietag: 11, subtag: 0, version 4, unit_type 5
rechecking: testfile-splitdwarf-4
Iterate no info, compare recorded info with dwarf_cu_info.
0 re dietag: 11, subtag: 11, version 4, unit_type 4
0 subdietag: 11, subtag: 0, version 4, unit_type 5
1 re dietag: 11, subtag: 11, version 4, unit_type 4
1 subdietag: 11, subtag: 0, version 4, unit_type 5
file: testfile-splitdwarf-5
Iterate getting all info, compare with dwarf_cu_info.
0 cu dietag: 4a, subtag: 11, version 5, unit_type 4
0 subdietag: 11, subtag: 0, version 5, unit_type 5
1 cu dietag: 4a, subtag: 11, version 5, unit_type 4
1 subdietag: 11, subtag: 0, version 5, unit_type 5
rechecking: testfile-splitdwarf-5
Iterate no info, compare recorded info with dwarf_cu_info.
0 re dietag: 4a, subtag: 11, version 5, unit_type 4
0 subdietag: 11, subtag: 0, version 5, unit_type 5
1 re dietag: 4a, subtag: 11, version 5, unit_type 4
1 subdietag: 11, subtag: 0, version 5, unit_type 5
EOF
# Self test
testrun_on_self ${abs_builddir}/unit-info
exit 0
+323
View File
@@ -0,0 +1,323 @@
/* Test dwarf_cu_info properties.
Copyright (C) 2018 Red Hat, Inc.
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 <dwarf.h>
#include ELFUTILS_HEADER(dw)
#include <stdio.h>
#include <inttypes.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
/* Yeah, lazy, 16K CUs should be enough for everybody... */
#define MAX_UNITS 16384
struct info
{
int dietag;
int subtag;
Dwarf_Half version;
uint8_t unit_type;
uint64_t id;
uint8_t addr_size;
uint8_t off_size;
};
static struct info unit_info[MAX_UNITS];
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_Half version;
Dwarf_Die cudie, subdie;
uint8_t unit_type;
size_t u, units;
u = units = 0;
printf ("Iterate getting all info, compare with dwarf_cu_info.\n");
while (dwarf_get_units (dbg, cu, &cu, &version,
&unit_type, &cudie, &subdie) == 0)
{
int dietag = dwarf_tag (&cudie);
int subtag = dwarf_tag (&subdie);
unit_info[u].dietag = dietag;
unit_info[u].subtag = subtag;
unit_info[u].version = version;
unit_info[u].unit_type = unit_type;
printf ("%zu cu dietag: %x, subtag: %x, version %" PRIx32
", unit_type %" PRIx8 "\n",
u, dietag, subtag, version, unit_type);
uint64_t unit_id;
uint8_t addr_size, off_size;
if (dwarf_cu_info (cu,
&version, &unit_type, &cudie, &subdie,
&unit_id, &addr_size, &off_size) != 0)
{
printf ("Invalid dwarf_cu_info: %s\n", dwarf_errmsg (-1));
return -1;
}
dietag = dwarf_tag (&cudie);
subtag = dwarf_tag (&subdie);
if (unit_info[u].dietag != dietag)
{
printf("Unequal dietags\n");
return -1;
}
if (unit_info[u].subtag != subtag)
{
printf("Unequal subtags\n");
return -1;
}
if (unit_info[u].version != version)
{
printf("Unequal versions\n");
return -1;
}
if (unit_info[u].unit_type != unit_type)
{
printf("Unequal unit_types\n");
return -1;
}
unit_info[u].id = unit_id;
unit_info[u].addr_size = addr_size;
unit_info[u].off_size = off_size;
if (unit_type == DW_UT_skeleton)
{
if (dwarf_cu_info (subdie.cu,
&version, &unit_type, &cudie, &subdie,
&unit_id, &addr_size, &off_size) != 0)
{
printf ("Invalid subdie dwarf_cu_info: %s\n",
dwarf_errmsg (-1));
return -1;
}
dietag = dwarf_tag (&cudie);
subtag = dwarf_tag (&subdie);
printf ("%zu subdietag: %x, subtag: %x, version %" PRIx32
", unit_type %" PRIx8 "\n",
u, dietag, subtag, version, unit_type);
/* subdie is now cudie. */
if (unit_info[u].subtag != dietag)
{
printf ("Inconsistent subdie tag\n");
return -1;
}
if (unit_info[u].id != unit_id)
{
printf ("Unequal subdie ids\n");
return -1;
}
if (unit_info[u].addr_size != addr_size)
{
printf ("Unequal subdie addr_size\n");
return -1;
}
if (unit_info[u].off_size != off_size)
{
printf ("Unequal subdie off_size\n");
return -1;
}
}
if (u >= MAX_UNITS)
{
printf ("Oops, more than 16K units...\n");
return -1;
}
u = ++units;
}
dwarf_end (dbg);
close (fd);
/* And again... */
printf ("rechecking: %s\n", argv[i]);
fd = open (argv[i], O_RDONLY);
dbg = dwarf_begin (fd, DWARF_C_READ);
if (dbg == NULL)
{
printf ("%s not usable: %s\n", argv[i], dwarf_errmsg (-1));
return -1;
}
cu = NULL;
u = 0;
printf ("Iterate no info, compare recorded info with dwarf_cu_info.\n");
while (dwarf_get_units (dbg, cu, &cu, NULL, NULL, NULL, NULL) == 0)
{
if (u > units)
{
printf ("Got too many units???\n");
return -1;
}
uint64_t unit_id;
uint8_t addr_size, off_size;
if (dwarf_cu_info (cu,
&version, &unit_type, &cudie, &subdie,
&unit_id, &addr_size, &off_size) != 0)
{
printf ("Invalid dwarf_cu_info: %s\n", dwarf_errmsg (-1));
return -1;
}
int dietag = dwarf_tag (&cudie);
int subtag = dwarf_tag (&subdie);
printf ("%zu re dietag: %x, subtag: %x, version %" PRIx32
", unit_type %" PRIx8 "\n",
u, dietag, subtag, version, unit_type);
if (unit_info[u].dietag != dietag)
{
printf("Unequal dietags %x != %x\n", unit_info[u].dietag, dietag);
return -1;
}
if (unit_info[u].subtag != subtag)
{
printf("Unequal subtags\n");
return -1;
}
if (unit_info[u].version != version)
{
printf("Unequal versions\n");
return -1;
}
if (unit_info[u].unit_type != unit_type)
{
printf("Unequal unit_types\n");
return -1;
}
if (unit_info[u].id != unit_id)
{
printf ("Unequal subdie ids\n");
return -1;
}
if (unit_info[u].addr_size != addr_size)
{
printf ("Unequal subdie addr_size\n");
return -1;
}
if (unit_info[u].off_size != off_size)
{
printf ("Unequal subdie off_size\n");
return -1;
}
if (unit_type == DW_UT_skeleton)
{
if (dwarf_cu_info (subdie.cu,
&version, &unit_type, &cudie, &subdie,
&unit_id, &addr_size, &off_size) != 0)
{
printf ("Invalid subdie dwarf_cu_info: %s\n",
dwarf_errmsg (-1));
return -1;
}
dietag = dwarf_tag (&cudie);
subtag = dwarf_tag (&subdie);
printf ("%zu subdietag: %x, subtag: %x, version %" PRIx32
", unit_type %" PRIx8 "\n",
u, dietag, subtag, version, unit_type);
/* subdie is now cudie. */
subtag = dwarf_tag (&cudie);
if (unit_info[u].subtag != subtag)
{
printf ("Inconsistent subdie tag\n");
return -1;
}
if (unit_info[u].id != unit_id)
{
printf ("Unequal subdie ids\n");
return -1;
}
if (unit_info[u].addr_size != addr_size)
{
printf ("Unequal subdie addr_size\n");
return -1;
}
if (unit_info[u].off_size != off_size)
{
printf ("Unequal subdie off_size\n");
return -1;
}
}
if (u >= MAX_UNITS)
{
printf ("Oops, more than 16K units...\n");
return -1;
}
u++;
}
if (u != units)
{
printf ("Got not enough units???\n");
return -1;
}
dwarf_end (dbg);
close (fd);
printf ("\n");
}
return 0;
}