libdwelf: Add dwelf_elf_gnu_build_id.

Move internal function __libdwfl_find_build_id to libdwelf and use it to
add a public dwelf_elf_gnu_build_id function to extract the NT_GNU_BUILD_ID
from an ELF file using either the shdrs or phdrs. Adjust internal callers
and add a testcase.

Signed-off-by: Mark Wielaard <mjw@redhat.com>
This commit is contained in:
Mark Wielaard
2014-04-30 23:00:40 +02:00
parent 920f03dcd3
commit d81d32d2a4
16 changed files with 333 additions and 126 deletions
+4
View File
@@ -1,3 +1,7 @@
2014-04-30 Mark Wielaard <mjw@redhat.com>
* libdw.map (ELFUTILS_0.159): Add dwelf_elf_gnu_build_id.
2014-04-15 Florian Weimer <fweimer@redhat.com>
* dwarf_begin_elf.c (__check_build_id, try_debugaltlink)
+1
View File
@@ -299,4 +299,5 @@ ELFUTILS_0.159 {
dwarf_setalt;
dwelf_dwarf_gnu_debugaltlink;
dwelf_elf_gnu_debuglink;
dwelf_elf_gnu_build_id;
} ELFUTILS_0.158;
+9
View File
@@ -1,3 +1,12 @@
2014-04-30 Mark Wielaard <mjw@redhat.com>
* Makefile.am (AM_CPPFLAGS): Add libdwfl and libebl include dirs.
(libdwelf_a_SOURCES): Add dwelf_elf_gnu_build_id.c
* dwelf_elf_gnu_build_id.c: New file. Moved libdwfl function
__libdwfl_find_elf_build_id here.
* libdwelf.h (dwelf_elf_gnu_build_id): Declare new function.
* libdwelfP.h (dwelf_elf_gnu_build_id): Add internal declaration.
2014-04-24 Florian Weimer <fweimer@redhat.com>
* dwelf_dwarf_gnu_debugaltlink.c: New file.
+4 -2
View File
@@ -30,7 +30,8 @@
## not, see <http://www.gnu.org/licenses/>.
##
include $(top_srcdir)/config/eu.am
AM_CPPFLAGS += -I$(srcdir)/../libelf -I$(srcdir)/../libdw
AM_CPPFLAGS += -I$(srcdir)/../libelf -I$(srcdir)/../libdw \
-I$(srcdir)/../libdwfl -I$(srcdir)/../libebl
VERSION = 1
noinst_LIBRARIES = libdwelf.a libdwelf_pic.a
@@ -38,7 +39,8 @@ noinst_LIBRARIES = libdwelf.a libdwelf_pic.a
pkginclude_HEADERS = libdwelf.h
noinst_HEADERS = libdwelfP.h
libdwelf_a_SOURCES = dwelf_elf_gnu_debuglink.c dwelf_dwarf_gnu_debugaltlink.c
libdwelf_a_SOURCES = dwelf_elf_gnu_debuglink.c dwelf_dwarf_gnu_debugaltlink.c \
dwelf_elf_gnu_build_id.c
libdwelf = $(libdw)
+147
View File
@@ -0,0 +1,147 @@
/* Returns the build id if found in a NT_GNU_BUILD_ID note.
Copyright (C) 2014 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 "libdwelfP.h"
#include "libdwflP.h"
#define NO_VADDR ((GElf_Addr) -1l)
/* Defined here for reuse. The dwelf interface doesn't care about the
address of the note, but libdwfl does. */
static int
find_elf_build_id (Dwfl_Module *mod, int e_type, Elf *elf,
const void **build_id_bits, GElf_Addr *build_id_elfaddr,
int *build_id_len)
{
int check_notes (Elf_Data *data, GElf_Addr data_elfaddr)
{
size_t pos = 0;
GElf_Nhdr nhdr;
size_t name_pos;
size_t desc_pos;
while ((pos = gelf_getnote (data, pos, &nhdr, &name_pos, &desc_pos)) > 0)
if (nhdr.n_type == NT_GNU_BUILD_ID
&& nhdr.n_namesz == sizeof "GNU" && !memcmp (data->d_buf + name_pos,
"GNU", sizeof "GNU"))
{
*build_id_bits = data->d_buf + desc_pos;
*build_id_elfaddr = (data_elfaddr == NO_VADDR
? 0 : data_elfaddr + desc_pos);
*build_id_len = nhdr.n_descsz;
return 1;
}
return 0;
}
size_t shstrndx = SHN_UNDEF;
int result = 0;
Elf_Scn *scn = elf_nextscn (elf, NULL);
if (scn == NULL)
{
/* No sections, have to look for phdrs. */
size_t phnum;
if (unlikely (elf_getphdrnum (elf, &phnum) != 0))
{
if (mod != NULL)
__libdwfl_seterrno (DWFL_E_LIBELF);
return -1;
}
for (size_t i = 0; result == 0 && i < phnum; ++i)
{
GElf_Phdr phdr_mem;
GElf_Phdr *phdr = gelf_getphdr (elf, i, &phdr_mem);
if (likely (phdr != NULL) && phdr->p_type == PT_NOTE)
result = check_notes (elf_getdata_rawchunk (elf,
phdr->p_offset,
phdr->p_filesz,
ELF_T_NHDR),
phdr->p_vaddr);
}
}
else
do
{
GElf_Shdr shdr_mem;
GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
if (likely (shdr != NULL) && shdr->sh_type == SHT_NOTE)
{
/* Determine the right sh_addr in this module. */
GElf_Addr vaddr = 0;
if (!(shdr->sh_flags & SHF_ALLOC))
vaddr = NO_VADDR;
else if (mod == NULL || e_type != ET_REL)
vaddr = shdr->sh_addr;
else if (__libdwfl_relocate_value (mod, elf, &shstrndx,
elf_ndxscn (scn), &vaddr))
vaddr = NO_VADDR;
result = check_notes (elf_getdata (scn, NULL), vaddr);
}
}
while (result == 0 && (scn = elf_nextscn (elf, scn)) != NULL);
return result;
}
int
internal_function
__libdwfl_find_elf_build_id (Dwfl_Module *mod, Elf *elf,
const void **build_id_bits,
GElf_Addr *build_id_elfaddr, int *build_id_len)
{
GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr (elf, &ehdr_mem);
if (unlikely (ehdr == NULL))
{
__libdwfl_seterrno (DWFL_E_LIBELF);
return -1;
}
// MOD->E_TYPE is zero here.
assert (ehdr->e_type != ET_REL || mod != NULL);
return find_elf_build_id (mod, ehdr->e_type, elf,
build_id_bits, build_id_elfaddr, build_id_len);
}
ssize_t
dwelf_elf_gnu_build_id (Elf *elf, const void **build_idp)
{
GElf_Addr build_id_elfaddr;
int build_id_len;
int result = find_elf_build_id (NULL, ET_NONE, elf, build_idp,
&build_id_elfaddr, &build_id_len);
if (result > 0)
return build_id_len;
return result;
}
INTDEF(dwelf_elf_gnu_build_id)
+8
View File
@@ -57,6 +57,14 @@ extern ssize_t dwelf_dwarf_gnu_debugaltlink (Dwarf *dwarf,
const char **namep,
const void **build_idp);
/* Returns the build ID as found in a NT_GNU_BUILD_ID note from either
a SHT_NOTE section or from a PT_NOTE segment if the ELF file
doesn't contain any section headers. On success a pointer to the
build ID is written to *BUILDID_P, and the positive length of the
build ID is returned. Returns 0 if the ELF lacks a NT_GNU_BUILD_ID
note. Returns -1 in case of malformed data or other errors. */
extern ssize_t dwelf_elf_gnu_build_id (Elf *elf, const void **build_idp);
#ifdef __cplusplus
}
#endif
+1
View File
@@ -37,5 +37,6 @@
/* Avoid PLT entries. */
INTDECL (dwelf_elf_gnu_debuglink)
INTDECL (dwelf_dwarf_gnu_debugaltlink)
INTDECL (dwelf_elf_gnu_build_id)
#endif /* libdwelfP.h */
+10
View File
@@ -1,3 +1,13 @@
2014-04-30 Mark Wielaard <mjw@redhat.com>
* dwfl_module_build_id.c (__libdwfl_find_elf_build_id): Moved to
dwelf_elf_gnu_build_id.c.
(__libdwfl_find_build_id): Add assert to make sure mod is never NULL.
* dwfl_segment_report_module.c (dwfl_segment_report_module): Call
dwelf_elf_gnu_build_id directly instead of __libdwfl_find_build_id.
* dwfl_module_getdwarf.c (__check_build_id): Implement using
dwelf_elf_gnu_build_id.
2014-04-15 Florian Weimer <fweimer@redhat.com>
* dwfl_module_getdwarf.c (__check_build_id): Moved from libdw.
+4 -88
View File
@@ -1,5 +1,5 @@
/* Return build ID information for a module.
Copyright (C) 2007-2010 Red Hat, Inc.
Copyright (C) 2007-2010, 2014 Red Hat, Inc.
This file is part of elfutils.
This file is free software; you can redistribute it and/or modify
@@ -52,93 +52,6 @@ found_build_id (Dwfl_Module *mod, bool set,
return len;
}
#define NO_VADDR ((GElf_Addr) -1l)
int
internal_function
__libdwfl_find_elf_build_id (Dwfl_Module *mod, Elf *elf,
const void **build_id_bits,
GElf_Addr *build_id_elfaddr, int *build_id_len)
{
GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr (elf, &ehdr_mem);
if (unlikely (ehdr == NULL))
{
__libdwfl_seterrno (DWFL_E_LIBELF);
return -1;
}
// MOD->E_TYPE is zero here.
assert (ehdr->e_type != ET_REL || mod != NULL);
int check_notes (Elf_Data *data, GElf_Addr data_elfaddr)
{
size_t pos = 0;
GElf_Nhdr nhdr;
size_t name_pos;
size_t desc_pos;
while ((pos = gelf_getnote (data, pos, &nhdr, &name_pos, &desc_pos)) > 0)
if (nhdr.n_type == NT_GNU_BUILD_ID
&& nhdr.n_namesz == sizeof "GNU" && !memcmp (data->d_buf + name_pos,
"GNU", sizeof "GNU"))
{
*build_id_bits = data->d_buf + desc_pos;
*build_id_elfaddr = (data_elfaddr == NO_VADDR
? 0 : data_elfaddr + desc_pos);
*build_id_len = nhdr.n_descsz;
return 1;
}
return 0;
}
size_t shstrndx = SHN_UNDEF;
int result = 0;
Elf_Scn *scn = elf_nextscn (elf, NULL);
if (scn == NULL)
{
/* No sections, have to look for phdrs. */
size_t phnum;
if (unlikely (elf_getphdrnum (elf, &phnum) != 0))
{
__libdwfl_seterrno (DWFL_E_LIBELF);
return -1;
}
for (size_t i = 0; result == 0 && i < phnum; ++i)
{
GElf_Phdr phdr_mem;
GElf_Phdr *phdr = gelf_getphdr (elf, i, &phdr_mem);
if (likely (phdr != NULL) && phdr->p_type == PT_NOTE)
result = check_notes (elf_getdata_rawchunk (elf,
phdr->p_offset,
phdr->p_filesz,
ELF_T_NHDR),
phdr->p_vaddr);
}
}
else
do
{
GElf_Shdr shdr_mem;
GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
if (likely (shdr != NULL) && shdr->sh_type == SHT_NOTE)
{
/* Determine the right sh_addr in this module. */
GElf_Addr vaddr = 0;
if (!(shdr->sh_flags & SHF_ALLOC))
vaddr = NO_VADDR;
else if (mod == NULL || ehdr->e_type != ET_REL)
vaddr = shdr->sh_addr;
else if (__libdwfl_relocate_value (mod, elf, &shstrndx,
elf_ndxscn (scn), &vaddr))
vaddr = NO_VADDR;
result = check_notes (elf_getdata (scn, NULL), vaddr);
}
}
while (result == 0 && (scn = elf_nextscn (elf, scn)) != NULL);
return result;
}
int
internal_function
__libdwfl_find_build_id (Dwfl_Module *mod, bool set, Elf *elf)
@@ -147,6 +60,9 @@ __libdwfl_find_build_id (Dwfl_Module *mod, bool set, Elf *elf)
GElf_Addr build_id_elfaddr;
int build_id_len;
/* For mod == NULL use dwelf_elf_gnu_build_id directly. */
assert (mod != NULL);
int result = __libdwfl_find_elf_build_id (mod, elf, &build_id_bits,
&build_id_elfaddr, &build_id_len);
if (result <= 0)
+5 -26
View File
@@ -42,34 +42,13 @@ __check_build_id (Dwarf *dw, const uint8_t *build_id, const size_t id_len)
return -1;
Elf *elf = dw->elf;
Elf_Scn *scn = elf_nextscn (elf, NULL);
if (scn == NULL)
const void *elf_build_id;
ssize_t elf_id_len = INTUSE(dwelf_elf_gnu_build_id) (elf, &elf_build_id);
if (elf_id_len < 0)
return -1;
do
{
GElf_Shdr shdr_mem;
GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
if (likely (shdr != NULL) && shdr->sh_type == SHT_NOTE)
{
size_t pos = 0;
GElf_Nhdr nhdr;
size_t name_pos;
size_t desc_pos;
Elf_Data *data = elf_getdata (scn, NULL);
while ((pos = gelf_getnote (data, pos, &nhdr, &name_pos,
&desc_pos)) > 0)
if (nhdr.n_type == NT_GNU_BUILD_ID
&& nhdr.n_namesz == sizeof "GNU"
&& ! memcmp (data->d_buf + name_pos, "GNU", sizeof "GNU"))
return (nhdr.n_descsz == id_len
&& ! memcmp (data->d_buf + desc_pos,
build_id, id_len)) ? 0 : 1;
}
}
while ((scn = elf_nextscn (elf, scn)) != NULL);
return -1;
return (id_len == (size_t) elf_id_len
&& memcmp (build_id, elf_build_id, id_len) == 0) ? 0 : 1;
}
/* Try to open an debug alt link by name, checking build_id.
+5 -6
View File
@@ -494,13 +494,12 @@ dwfl_segment_report_module (Dwfl *dwfl, int ndx, const char *name,
&& module->disk_file_has_build_id && build_id_len > 0)
{
const void *elf_build_id;
GElf_Addr elf_build_id_elfaddr;
int elf_build_id_len;
ssize_t elf_build_id_len;
if (__libdwfl_find_elf_build_id (NULL, module->elf,
&elf_build_id,
&elf_build_id_elfaddr,
&elf_build_id_len) > 0)
/* If there is a build id in the elf file, check it. */
elf_build_id_len = INTUSE(dwelf_elf_gnu_build_id) (module->elf,
&elf_build_id);
if (elf_build_id_len > 0)
{
if (build_id_len != (size_t) elf_build_id_len
|| memcmp (build_id, elf_build_id, build_id_len) != 0)
+8
View File
@@ -1,3 +1,11 @@
2014-04-30 Mark Wielaard <mjw@redhat.com>
* buildid.c, buildid.sh, testfile42_noshdrs.bz2: New files.
* Makefile.am (check_PROGRAMS): Add buildid.
(TESTS): Add run-buildid.sh.
(EXTRA_DISTS): Add run-buildid.sh and testfile42_noshdrs.bz2.
(buildid_LDADD): New variable.
2014-04-24 Florian Weimer <fweimer@redhat.com>
* allfcts.c (setup_alt): New function.
+8 -4
View File
@@ -49,7 +49,9 @@ check_PROGRAMS = arextract arsymtest newfile saridx scnnames sectiondump \
alldts md5-sha1-test typeiter typeiter2 low_high_pc \
test-elf_cntl_gelf_getshdr dwflsyms dwfllines \
dwfl-report-elf-align varlocs backtrace backtrace-child \
backtrace-data backtrace-dwarf debuglink debugaltlink
backtrace-data backtrace-dwarf debuglink debugaltlink \
buildid
asm_TESTS = asm-tst1 asm-tst2 asm-tst3 asm-tst4 asm-tst5 \
asm-tst6 asm-tst7 asm-tst8 asm-tst9
@@ -86,7 +88,7 @@ TESTS = run-arextract.sh run-arsymtest.sh newfile test-nlist \
run-readelf-macro.sh run-readelf-loc.sh \
run-readelf-aranges.sh run-readelf-line.sh \
run-native-test.sh run-bug1-test.sh \
run-debuglink.sh run-debugaltlink.sh \
run-debuglink.sh run-debugaltlink.sh run-buildid.sh \
dwfl-bug-addr-overflow run-addrname-test.sh \
dwfl-bug-fd-leak dwfl-bug-report \
run-dwfl-bug-offline-rel.sh run-dwfl-addr-sect.sh \
@@ -176,14 +178,15 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh \
run-readelf-test1.sh run-readelf-test2.sh run-readelf-test3.sh \
run-readelf-test4.sh run-readelf-twofiles.sh \
run-bug1-test.sh testfile28.bz2 testfile28.rdwr.bz2 \
run-debuglink.sh run-debugaltlink.sh \
run-debuglink.sh run-debugaltlink.sh run-buildid.sh \
testfile29.bz2 testfile29.rdwr.bz2 \
testfile30.bz2 testfile31.bz2 testfile32.bz2 testfile33.bz2 \
testfile34.bz2 testfile35.bz2 testfile35.debug.bz2 \
testfile36.bz2 testfile36.debug.bz2 \
testfile37.bz2 testfile37.debug.bz2 \
testfile38.bz2 testfile39.bz2 testfile40.bz2 testfile40.debug.bz2 \
testfile41.bz2 testfile42.bz2 testfile43.bz2 \
testfile41.bz2 testfile42.bz2 testfile42_noshdrs.bz2 \
testfile43.bz2 \
testfile44.S.bz2 testfile44.expect.bz2 run-disasm-x86.sh \
testfile45.S.bz2 testfile45.expect.bz2 run-disasm-x86-64.sh \
testfile46.bz2 testfile47.bz2 testfile48.bz2 testfile48.debug.bz2 \
@@ -406,6 +409,7 @@ backtrace_dwarf_CFLAGS = -Wno-unused-parameter
backtrace_dwarf_LDADD = $(libdw) $(libelf)
debuglink_LDADD = $(libdw) $(libelf)
debugaltlink_LDADD = $(libdw) $(libelf)
buildid_LDADD = $(libdw) $(libelf)
if GCOV
check: check-am coverage
+81
View File
@@ -0,0 +1,81 @@
/* Test program for dwelf_elf_gnu_build_id, print build ID.
Copyright (C) 2014 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/>. */
#include <config.h>
#include <assert.h>
#include <inttypes.h>
#include <err.h>
#include <errno.h>
#include ELFUTILS_HEADER(elf)
#include ELFUTILS_HEADER(dwelf)
#include <stdio.h>
#include <error.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int
main (int argc, char *argv[])
{
if (argc < 2)
error (EXIT_FAILURE, 0, "No input file given");
elf_version (EV_CURRENT);
for (int i = 1; i < argc; i++)
{
const char *file = argv[i];
int fd = open (file, O_RDONLY);
if (fd < 0)
error (EXIT_FAILURE, errno, "couldn't open file '%s'", file);
Elf *elf = elf_begin (fd, ELF_C_READ, NULL);
if (elf == NULL)
{
printf("%s: elf_begin failed: %s\n", file, elf_errmsg (-1));
close (fd);
continue;
}
const void *build_id;
ssize_t len = dwelf_elf_gnu_build_id (elf, &build_id);
switch (len)
{
case 0:
printf ("%s: <no NT_GNU_BUILD_ID note>\n", file);
break;
case -1:
errx (1, "dwelf_elf_gnu_build_id (%s): %s",
file, elf_errmsg (-1));
default:
printf ("%s: build ID: ", file);
const unsigned char *p = build_id;
const unsigned char *end = p + len;
while (p < end)
printf("%02x", (unsigned)*p++);
putchar('\n');
}
elf_end (elf);
close (fd);
}
return 0;
}
+38
View File
@@ -0,0 +1,38 @@
#! /bin/sh
# Copyright (C) 2014 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
# Just some random testfiles, four with, one without build-id,
# and one without shdrs forcing reading the notes through phdrs.
# eu-strip --strip-sections -g --output=testfile42_noshdrs testfile42
# See also run-debugaltlink.sh.
testfiles testfile42 testfile_multi.dwz testfile-dwzstr.multi \
test-offset-loop.alt testfile14 testfile42_noshdrs
testrun_compare ${abs_builddir}/buildid testfile42 testfile42_noshdrs \
testfile_multi.dwz testfile-dwzstr.multi \
test-offset-loop.alt testfile14 <<\EOF
testfile42: build ID: d826d96c4d097bdc5c254b1f7344a907e36b0439
testfile42_noshdrs: build ID: d826d96c4d097bdc5c254b1f7344a907e36b0439
testfile_multi.dwz: build ID: a0d6c06e0d912d74033b6fe2808753cae8f6f594
testfile-dwzstr.multi: build ID: 6da22627dae55c1d62cf9122827c665e240a056b
test-offset-loop.alt: build ID: 066bbf1a7bc5676f5015ee1966a088f23bdb83ae
testfile14: <no NT_GNU_BUILD_ID note>
EOF
exit 0
Binary file not shown.