fix PR symtab/15597
This patch fixes gdb PR symtab/15597. The bug is that the .gnu_debugaltlink section includes the build-id of the alt file, but gdb does not use it. This patch fixes the problem by changing gdb to do what it ought to always have done: verify the build id of the file found using the filename in .gnu_debugaltlink; and if that does not match, try to find the correct debug file using the build-id and debug-file-directory. This patch touches BFD. Previously, gdb had its own code for parsing .gnu_debugaltlink; I changed it to use the BFD functions after those were introduced. However, the BFD functions are incorrect -- they assume that .gnu_debugaltlink is formatted like .gnu_debuglink. However, it it is not. Instead, it consists of a file name followed by the build-id -- no alignment, and the build-id is not a CRC. Fixing this properly is a bit of a pain. But, because separate_alt_debug_file_exists just has a FIXME for the build-id case, I did not fix it properly. Instead I introduced a hack. This leaves BFD working just as well as it did before my patch. I'm willing to do something better here but I could use some guidance as to what. It seems that the build-id code in BFD is largely punted on. FWIW gdb is the only user of bfd_get_alt_debug_link_info outside of BFD itself. I moved the build-id logic out of elfread.c and into a new file. This seemed cleanest to me. Writing a test case was a bit of a pain. I added a couple new features to the DWARF assembler to handle this. Built and regtested on x86-64 Fedora 18. * bfd-in2.h: Rebuild. * opncls.c (bfd_get_alt_debug_link_info): Add buildid_len parameter. Change type of buildid_out. Update. (get_alt_debug_link_info_shim): New function. (bfd_follow_gnu_debuglink): Use it. * Makefile.in (SFILES): Add build-id.c. (HFILES_NO_SRCDIR): Add build-id.h. * build-id.c: New file, largely from elfread.c. Modified most functions. * build-id.h: New file. * dwarf2read.c (dwarf2_get_dwz_file): Update for change to bfd_get_alt_debug_link_info. Verify dwz file's build-id. Search for dwz file using build-id. * elfread.c (build_id_bfd_get, build_id_verify) (build_id_to_debug_filename, find_separate_debug_file): Remove. * gdb.dwarf2/dwzbuildid.exp: New file. * lib/dwarf.exp (Dwarf::_section): Add "flags" and "type" parameters. (Dwarf::_defer_output): Change "section" parameter to "section_spec"; update. (Dwarf::gnu_debugaltlink, Dwarf::_note, Dwarf::build_id): New procs.
This commit is contained in:
parent
5bb7df2edf
commit
dc294be54c
12 changed files with 530 additions and 155 deletions
|
@ -1,3 +1,11 @@
|
|||
2013-10-08 Tom Tromey <tromey@redhat.com>
|
||||
|
||||
* bfd-in2.h: Rebuild.
|
||||
* opncls.c (bfd_get_alt_debug_link_info): Add buildid_len
|
||||
parameter. Change type of buildid_out. Update.
|
||||
(get_alt_debug_link_info_shim): New function.
|
||||
(bfd_follow_gnu_debuglink): Use it.
|
||||
|
||||
2013-10-08 Andreas Schwab <schwab@suse.de>
|
||||
|
||||
* elf32-m68k.c (elf_m68k_size_dynamic_sections): Add DT_DEBUG also
|
||||
|
|
|
@ -1067,7 +1067,8 @@ unsigned long bfd_calc_gnu_debuglink_crc32
|
|||
|
||||
char *bfd_get_debug_link_info (bfd *abfd, unsigned long *crc32_out);
|
||||
|
||||
char *bfd_get_alt_debug_link_info (bfd *abfd, unsigned long *crc32_out);
|
||||
char *bfd_get_alt_debug_link_info (bfd * abfd, size_t *buildid_len,
|
||||
bfd_byte **buildid_out);
|
||||
|
||||
char *bfd_follow_gnu_debuglink (bfd *abfd, const char *dir);
|
||||
|
||||
|
|
41
bfd/opncls.c
41
bfd/opncls.c
|
@ -1194,18 +1194,21 @@ FUNCTION
|
|||
bfd_get_alt_debug_link_info
|
||||
|
||||
SYNOPSIS
|
||||
char *bfd_get_alt_debug_link_info (bfd *abfd, unsigned long *crc32_out);
|
||||
char *bfd_get_alt_debug_link_info (bfd * abfd, size_t *buildid_len,
|
||||
bfd_byte **buildid_out);
|
||||
|
||||
DESCRIPTION
|
||||
Fetch the filename and BuildID value for any alternate debuginfo
|
||||
associated with @var{abfd}. Return NULL if no such info found,
|
||||
otherwise return filename and update @var{buildid_out}. The
|
||||
returned filename is allocated with @code{malloc}; freeing it
|
||||
is the responsibility of the caller.
|
||||
otherwise return filename and update @var{buildid_len} and
|
||||
@var{buildid_out}. The returned filename and build_id are
|
||||
allocated with @code{malloc}; freeing them is the
|
||||
responsibility of the caller.
|
||||
*/
|
||||
|
||||
char *
|
||||
bfd_get_alt_debug_link_info (bfd * abfd, unsigned long * buildid_out)
|
||||
bfd_get_alt_debug_link_info (bfd * abfd, size_t *buildid_len,
|
||||
bfd_byte **buildid_out)
|
||||
{
|
||||
asection *sect;
|
||||
bfd_byte *contents;
|
||||
|
@ -1213,6 +1216,7 @@ bfd_get_alt_debug_link_info (bfd * abfd, unsigned long * buildid_out)
|
|||
char *name;
|
||||
|
||||
BFD_ASSERT (abfd);
|
||||
BFD_ASSERT (buildid_len);
|
||||
BFD_ASSERT (buildid_out);
|
||||
|
||||
sect = bfd_get_section_by_name (abfd, GNU_DEBUGALTLINK);
|
||||
|
@ -1227,12 +1231,13 @@ bfd_get_alt_debug_link_info (bfd * abfd, unsigned long * buildid_out)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
/* BuildID value is stored after the filename, aligned up to 4 bytes. */
|
||||
/* BuildID value is stored after the filename. */
|
||||
name = (char *) contents;
|
||||
buildid_offset = strlen (name) + 1;
|
||||
buildid_offset = (buildid_offset + 3) & ~3;
|
||||
|
||||
* buildid_out = bfd_get_32 (abfd, contents + buildid_offset);
|
||||
*buildid_len = bfd_get_section_size (sect) - buildid_offset;
|
||||
*buildid_out = bfd_malloc (*buildid_len);
|
||||
memcpy (*buildid_out, contents + buildid_offset, *buildid_len);
|
||||
|
||||
return name;
|
||||
}
|
||||
|
@ -1466,6 +1471,24 @@ bfd_follow_gnu_debuglink (bfd *abfd, const char *dir)
|
|||
separate_debug_file_exists);
|
||||
}
|
||||
|
||||
/* Helper for bfd_follow_gnu_debugaltlink. It just pretends to return
|
||||
a CRC. .gnu_debugaltlink supplies a build-id, which is different,
|
||||
but this is ok because separate_alt_debug_file_exists ignores the
|
||||
CRC anyway. */
|
||||
|
||||
static char *
|
||||
get_alt_debug_link_info_shim (bfd * abfd, unsigned long *crc32_out)
|
||||
{
|
||||
size_t len;
|
||||
bfd_byte *buildid = NULL;
|
||||
char *result = bfd_get_alt_debug_link_info (abfd, &len, &buildid);
|
||||
|
||||
*crc32_out = 0;
|
||||
free (buildid);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
FUNCTION
|
||||
bfd_follow_gnu_debugaltlink
|
||||
|
@ -1496,7 +1519,7 @@ char *
|
|||
bfd_follow_gnu_debugaltlink (bfd *abfd, const char *dir)
|
||||
{
|
||||
return find_separate_debug_file (abfd, dir,
|
||||
bfd_get_alt_debug_link_info,
|
||||
get_alt_debug_link_info_shim,
|
||||
separate_alt_debug_file_exists);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,16 @@
|
|||
2013-10-08 Tom Tromey <tromey@redhat.com>
|
||||
|
||||
* Makefile.in (SFILES): Add build-id.c.
|
||||
(HFILES_NO_SRCDIR): Add build-id.h.
|
||||
* build-id.c: New file, largely from elfread.c. Modified
|
||||
most functions.
|
||||
* build-id.h: New file.
|
||||
* dwarf2read.c (dwarf2_get_dwz_file): Update for change to
|
||||
bfd_get_alt_debug_link_info. Verify dwz file's build-id.
|
||||
Search for dwz file using build-id.
|
||||
* elfread.c (build_id_bfd_get, build_id_verify)
|
||||
(build_id_to_debug_filename, find_separate_debug_file): Remove.
|
||||
|
||||
2013-10-08 Joel Brobecker <brobecker@adacore.com>
|
||||
|
||||
* ada-lang.c (compare_names_with_case): Renamed from
|
||||
|
|
|
@ -717,7 +717,7 @@ SFILES = ada-exp.y ada-lang.c ada-typeprint.c ada-valprint.c ada-tasks.c \
|
|||
bfd-target.c \
|
||||
block.c blockframe.c \
|
||||
breakpoint.c break-catch-sig.c break-catch-throw.c \
|
||||
buildsym.c \
|
||||
build-id.c buildsym.c \
|
||||
c-exp.y c-lang.c c-typeprint.c c-valprint.c \
|
||||
charset.c cleanups.c cli-out.c coffread.c coff-pe-read.c \
|
||||
complaints.c completer.c continuations.c corefile.c corelow.c \
|
||||
|
@ -814,7 +814,8 @@ tui/tui-file.h tui/tui-command.h tui/tui-disasm.h tui/tui-wingeneral.h \
|
|||
tui/tui-windata.h tui/tui-data.h tui/tui-win.h tui/tui-stack.h \
|
||||
tui/tui-winsource.h tui/tui-regs.h tui/tui-io.h tui/tui-layout.h \
|
||||
tui/tui-source.h sol2-tdep.h gregset.h sh-tdep.h sh64-tdep.h \
|
||||
expression.h score-tdep.h gdb_select.h ser-tcp.h buildsym.h valprint.h \
|
||||
expression.h score-tdep.h gdb_select.h ser-tcp.h \
|
||||
build-id.h buildsym.h valprint.h \
|
||||
typeprint.h mi/mi-getopt.h mi/mi-parse.h mi/mi-console.h \
|
||||
mi/mi-out.h mi/mi-main.h mi/mi-common.h mi/mi-cmds.h linux-nat.h \
|
||||
complaints.h gdb_proc_service.h gdb_regex.h xtensa-tdep.h inf-loop.h \
|
||||
|
@ -911,7 +912,8 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \
|
|||
event-loop.o event-top.o inf-loop.o completer.o \
|
||||
gdbarch.o arch-utils.o gdbtypes.o gdb_bfd.o gdb_obstack.o \
|
||||
osabi.o copying.o \
|
||||
memattr.o mem-break.o target.o parse.o language.o buildsym.o \
|
||||
memattr.o mem-break.o target.o parse.o language.o \
|
||||
build-id.o buildsym.o \
|
||||
findcmd.o \
|
||||
std-regs.o \
|
||||
signals.o \
|
||||
|
|
168
gdb/build-id.c
Normal file
168
gdb/build-id.c
Normal file
|
@ -0,0 +1,168 @@
|
|||
/* build-id-related functions.
|
||||
|
||||
Copyright (C) 1991-2013 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GDB.
|
||||
|
||||
This program 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.
|
||||
|
||||
This program 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 "defs.h"
|
||||
#include "bfd.h"
|
||||
#include "elf-bfd.h"
|
||||
#include "gdb_bfd.h"
|
||||
#include "build-id.h"
|
||||
#include "gdb_string.h"
|
||||
#include "gdb_vecs.h"
|
||||
#include "symfile.h"
|
||||
#include "objfiles.h"
|
||||
#include "filenames.h"
|
||||
|
||||
/* Locate NT_GNU_BUILD_ID from ABFD and return its content. */
|
||||
|
||||
static const struct elf_build_id *
|
||||
build_id_bfd_get (bfd *abfd)
|
||||
{
|
||||
if (!bfd_check_format (abfd, bfd_object)
|
||||
|| bfd_get_flavour (abfd) != bfd_target_elf_flavour
|
||||
/* Although this is ELF_specific, it is safe to do in generic
|
||||
code because it does not rely on any ELF-specific symbols at
|
||||
link time, and if the ELF code is not available in BFD, then
|
||||
ABFD will not have the ELF flavour. */
|
||||
|| elf_tdata (abfd)->build_id == NULL)
|
||||
return NULL;
|
||||
|
||||
return elf_tdata (abfd)->build_id;
|
||||
}
|
||||
|
||||
/* See build-id.h. */
|
||||
|
||||
int
|
||||
build_id_verify (bfd *abfd, size_t check_len, const bfd_byte *check)
|
||||
{
|
||||
const struct elf_build_id *found;
|
||||
int retval = 0;
|
||||
|
||||
found = build_id_bfd_get (abfd);
|
||||
|
||||
if (found == NULL)
|
||||
warning (_("File \"%s\" has no build-id, file skipped"),
|
||||
bfd_get_filename (abfd));
|
||||
else if (found->size != check_len
|
||||
|| memcmp (found->data, check, found->size) != 0)
|
||||
warning (_("File \"%s\" has a different build-id, file skipped"),
|
||||
bfd_get_filename (abfd));
|
||||
else
|
||||
retval = 1;
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* See build-id.h. */
|
||||
|
||||
bfd *
|
||||
build_id_to_debug_bfd (size_t build_id_len, const bfd_byte *build_id)
|
||||
{
|
||||
char *link, *debugdir;
|
||||
VEC (char_ptr) *debugdir_vec;
|
||||
struct cleanup *back_to;
|
||||
int ix;
|
||||
bfd *abfd = NULL;
|
||||
|
||||
/* DEBUG_FILE_DIRECTORY/.build-id/ab/cdef */
|
||||
link = alloca (strlen (debug_file_directory) + (sizeof "/.build-id/" - 1) + 1
|
||||
+ 2 * build_id_len + (sizeof ".debug" - 1) + 1);
|
||||
|
||||
/* Keep backward compatibility so that DEBUG_FILE_DIRECTORY being "" will
|
||||
cause "/.build-id/..." lookups. */
|
||||
|
||||
debugdir_vec = dirnames_to_char_ptr_vec (debug_file_directory);
|
||||
back_to = make_cleanup_free_char_ptr_vec (debugdir_vec);
|
||||
|
||||
for (ix = 0; VEC_iterate (char_ptr, debugdir_vec, ix, debugdir); ++ix)
|
||||
{
|
||||
size_t debugdir_len = strlen (debugdir);
|
||||
const gdb_byte *data = build_id;
|
||||
size_t size = build_id_len;
|
||||
char *s;
|
||||
char *filename = NULL;
|
||||
|
||||
memcpy (link, debugdir, debugdir_len);
|
||||
s = &link[debugdir_len];
|
||||
s += sprintf (s, "/.build-id/");
|
||||
if (size > 0)
|
||||
{
|
||||
size--;
|
||||
s += sprintf (s, "%02x", (unsigned) *data++);
|
||||
}
|
||||
if (size > 0)
|
||||
*s++ = '/';
|
||||
while (size-- > 0)
|
||||
s += sprintf (s, "%02x", (unsigned) *data++);
|
||||
strcpy (s, ".debug");
|
||||
|
||||
/* lrealpath() is expensive even for the usually non-existent files. */
|
||||
if (access (link, F_OK) == 0)
|
||||
filename = lrealpath (link);
|
||||
|
||||
if (filename == NULL)
|
||||
continue;
|
||||
|
||||
/* We expect to be silent on the non-existing files. */
|
||||
abfd = gdb_bfd_open_maybe_remote (filename);
|
||||
if (abfd == NULL)
|
||||
continue;
|
||||
|
||||
if (build_id_verify (abfd, build_id_len, build_id))
|
||||
break;
|
||||
|
||||
gdb_bfd_unref (abfd);
|
||||
abfd = NULL;
|
||||
}
|
||||
|
||||
do_cleanups (back_to);
|
||||
return abfd;
|
||||
}
|
||||
|
||||
/* See build-id.h. */
|
||||
|
||||
char *
|
||||
find_separate_debug_file_by_buildid (struct objfile *objfile)
|
||||
{
|
||||
const struct elf_build_id *build_id;
|
||||
|
||||
build_id = build_id_bfd_get (objfile->obfd);
|
||||
if (build_id != NULL)
|
||||
{
|
||||
bfd *abfd;
|
||||
|
||||
abfd = build_id_to_debug_bfd (build_id->size, build_id->data);
|
||||
/* Prevent looping on a stripped .debug file. */
|
||||
if (abfd != NULL
|
||||
&& filename_cmp (bfd_get_filename (abfd),
|
||||
objfile_name (objfile)) == 0)
|
||||
{
|
||||
warning (_("\"%s\": separate debug info file has no debug info"),
|
||||
bfd_get_filename (abfd));
|
||||
gdb_bfd_unref (abfd);
|
||||
}
|
||||
else if (abfd != NULL)
|
||||
{
|
||||
char *result = xstrdup (bfd_get_filename (abfd));
|
||||
|
||||
gdb_bfd_unref (abfd);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
44
gdb/build-id.h
Normal file
44
gdb/build-id.h
Normal file
|
@ -0,0 +1,44 @@
|
|||
/* build-id-related functions.
|
||||
|
||||
Copyright (C) 1991-2013 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GDB.
|
||||
|
||||
This program 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.
|
||||
|
||||
This program 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/>. */
|
||||
|
||||
#ifndef BUILD_ID_H
|
||||
#define BUILD_ID_H
|
||||
|
||||
/* Return true if ABFD has NT_GNU_BUILD_ID matching the CHECK value.
|
||||
Otherwise, issue a warning and return false. */
|
||||
|
||||
extern int build_id_verify (bfd *abfd,
|
||||
size_t check_len, const bfd_byte *check);
|
||||
|
||||
|
||||
/* Find and open a BFD given a build-id. If no BFD can be found,
|
||||
return NULL. The returned reference to the BFD must be released by
|
||||
the caller. */
|
||||
|
||||
extern bfd *build_id_to_debug_bfd (size_t build_id_len,
|
||||
const bfd_byte *build_id);
|
||||
|
||||
/* Find the separate debug file for OBJFILE, by using the build-id
|
||||
associated with OBJFILE's BFD. If successful, returns a malloc'd
|
||||
file name for the separate debug file. The caller must free this.
|
||||
Otherwise, returns NULL. */
|
||||
|
||||
extern char *find_separate_debug_file_by_buildid (struct objfile *objfile);
|
||||
|
||||
#endif /* BUILD_ID_H */
|
|
@ -69,6 +69,7 @@
|
|||
#include "f-lang.h"
|
||||
#include "source.h"
|
||||
#include "filestuff.h"
|
||||
#include "build-id.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include "gdb_string.h"
|
||||
|
@ -2364,14 +2365,15 @@ dwarf2_get_dwz_file (void)
|
|||
struct cleanup *cleanup;
|
||||
const char *filename;
|
||||
struct dwz_file *result;
|
||||
unsigned long buildid;
|
||||
size_t buildid_len;
|
||||
bfd_byte *buildid;
|
||||
|
||||
if (dwarf2_per_objfile->dwz_file != NULL)
|
||||
return dwarf2_per_objfile->dwz_file;
|
||||
|
||||
bfd_set_error (bfd_error_no_error);
|
||||
data = bfd_get_alt_debug_link_info (dwarf2_per_objfile->objfile->obfd,
|
||||
&buildid);
|
||||
&buildid_len, &buildid);
|
||||
if (data == NULL)
|
||||
{
|
||||
if (bfd_get_error () == bfd_error_no_error)
|
||||
|
@ -2380,6 +2382,7 @@ dwarf2_get_dwz_file (void)
|
|||
bfd_errmsg (bfd_get_error ()));
|
||||
}
|
||||
cleanup = make_cleanup (xfree, data);
|
||||
make_cleanup (xfree, buildid);
|
||||
|
||||
filename = (const char *) data;
|
||||
if (!IS_ABSOLUTE_PATH (filename))
|
||||
|
@ -2396,20 +2399,25 @@ dwarf2_get_dwz_file (void)
|
|||
filename = rel;
|
||||
}
|
||||
|
||||
/* The format is just a NUL-terminated file name, followed by the
|
||||
build-id. For now, though, we ignore the build-id. */
|
||||
/* First try the file name given in the section. If that doesn't
|
||||
work, try to use the build-id instead. */
|
||||
dwz_bfd = gdb_bfd_open (filename, gnutarget, -1);
|
||||
if (dwz_bfd == NULL)
|
||||
error (_("could not read '%s': %s"), filename,
|
||||
bfd_errmsg (bfd_get_error ()));
|
||||
|
||||
if (!bfd_check_format (dwz_bfd, bfd_object))
|
||||
if (dwz_bfd != NULL)
|
||||
{
|
||||
gdb_bfd_unref (dwz_bfd);
|
||||
error (_("file '%s' was not usable: %s"), filename,
|
||||
bfd_errmsg (bfd_get_error ()));
|
||||
if (!build_id_verify (dwz_bfd, buildid_len, buildid))
|
||||
{
|
||||
gdb_bfd_unref (dwz_bfd);
|
||||
dwz_bfd = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (dwz_bfd == NULL)
|
||||
dwz_bfd = build_id_to_debug_bfd (buildid_len, buildid);
|
||||
|
||||
if (dwz_bfd == NULL)
|
||||
error (_("could not find '.gnu_debugaltlink' file for %s"),
|
||||
objfile_name (dwarf2_per_objfile->objfile));
|
||||
|
||||
result = OBSTACK_ZALLOC (&dwarf2_per_objfile->objfile->objfile_obstack,
|
||||
struct dwz_file);
|
||||
result->dwz_bfd = dwz_bfd;
|
||||
|
|
126
gdb/elfread.c
126
gdb/elfread.c
|
@ -45,6 +45,7 @@
|
|||
#include "regcache.h"
|
||||
#include "bcache.h"
|
||||
#include "gdb_bfd.h"
|
||||
#include "build-id.h"
|
||||
|
||||
extern void _initialize_elfread (void);
|
||||
|
||||
|
@ -1087,131 +1088,6 @@ elf_gnu_ifunc_resolver_return_stop (struct breakpoint *b)
|
|||
update_breakpoint_locations (b, sals, sals_end);
|
||||
}
|
||||
|
||||
/* Locate NT_GNU_BUILD_ID from ABFD and return its content. */
|
||||
|
||||
static const struct elf_build_id *
|
||||
build_id_bfd_get (bfd *abfd)
|
||||
{
|
||||
if (!bfd_check_format (abfd, bfd_object)
|
||||
|| bfd_get_flavour (abfd) != bfd_target_elf_flavour
|
||||
|| elf_tdata (abfd)->build_id == NULL)
|
||||
return NULL;
|
||||
|
||||
return elf_tdata (abfd)->build_id;
|
||||
}
|
||||
|
||||
/* Return if FILENAME has NT_GNU_BUILD_ID matching the CHECK value. */
|
||||
|
||||
static int
|
||||
build_id_verify (const char *filename, const struct elf_build_id *check)
|
||||
{
|
||||
bfd *abfd;
|
||||
const struct elf_build_id *found;
|
||||
int retval = 0;
|
||||
|
||||
/* We expect to be silent on the non-existing files. */
|
||||
abfd = gdb_bfd_open_maybe_remote (filename);
|
||||
if (abfd == NULL)
|
||||
return 0;
|
||||
|
||||
found = build_id_bfd_get (abfd);
|
||||
|
||||
if (found == NULL)
|
||||
warning (_("File \"%s\" has no build-id, file skipped"), filename);
|
||||
else if (found->size != check->size
|
||||
|| memcmp (found->data, check->data, found->size) != 0)
|
||||
warning (_("File \"%s\" has a different build-id, file skipped"),
|
||||
filename);
|
||||
else
|
||||
retval = 1;
|
||||
|
||||
gdb_bfd_unref (abfd);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static char *
|
||||
build_id_to_debug_filename (const struct elf_build_id *build_id)
|
||||
{
|
||||
char *link, *debugdir, *retval = NULL;
|
||||
VEC (char_ptr) *debugdir_vec;
|
||||
struct cleanup *back_to;
|
||||
int ix;
|
||||
|
||||
/* DEBUG_FILE_DIRECTORY/.build-id/ab/cdef */
|
||||
link = alloca (strlen (debug_file_directory) + (sizeof "/.build-id/" - 1) + 1
|
||||
+ 2 * build_id->size + (sizeof ".debug" - 1) + 1);
|
||||
|
||||
/* Keep backward compatibility so that DEBUG_FILE_DIRECTORY being "" will
|
||||
cause "/.build-id/..." lookups. */
|
||||
|
||||
debugdir_vec = dirnames_to_char_ptr_vec (debug_file_directory);
|
||||
back_to = make_cleanup_free_char_ptr_vec (debugdir_vec);
|
||||
|
||||
for (ix = 0; VEC_iterate (char_ptr, debugdir_vec, ix, debugdir); ++ix)
|
||||
{
|
||||
size_t debugdir_len = strlen (debugdir);
|
||||
const gdb_byte *data = build_id->data;
|
||||
size_t size = build_id->size;
|
||||
char *s;
|
||||
|
||||
memcpy (link, debugdir, debugdir_len);
|
||||
s = &link[debugdir_len];
|
||||
s += sprintf (s, "/.build-id/");
|
||||
if (size > 0)
|
||||
{
|
||||
size--;
|
||||
s += sprintf (s, "%02x", (unsigned) *data++);
|
||||
}
|
||||
if (size > 0)
|
||||
*s++ = '/';
|
||||
while (size-- > 0)
|
||||
s += sprintf (s, "%02x", (unsigned) *data++);
|
||||
strcpy (s, ".debug");
|
||||
|
||||
/* lrealpath() is expensive even for the usually non-existent files. */
|
||||
if (access (link, F_OK) == 0)
|
||||
retval = lrealpath (link);
|
||||
|
||||
if (retval != NULL && !build_id_verify (retval, build_id))
|
||||
{
|
||||
xfree (retval);
|
||||
retval = NULL;
|
||||
}
|
||||
|
||||
if (retval != NULL)
|
||||
break;
|
||||
}
|
||||
|
||||
do_cleanups (back_to);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static char *
|
||||
find_separate_debug_file_by_buildid (struct objfile *objfile)
|
||||
{
|
||||
const struct elf_build_id *build_id;
|
||||
|
||||
build_id = build_id_bfd_get (objfile->obfd);
|
||||
if (build_id != NULL)
|
||||
{
|
||||
char *build_id_name;
|
||||
|
||||
build_id_name = build_id_to_debug_filename (build_id);
|
||||
/* Prevent looping on a stripped .debug file. */
|
||||
if (build_id_name != NULL
|
||||
&& filename_cmp (build_id_name, objfile_name (objfile)) == 0)
|
||||
{
|
||||
warning (_("\"%s\": separate debug info file has no debug info"),
|
||||
build_id_name);
|
||||
xfree (build_id_name);
|
||||
}
|
||||
else if (build_id_name != NULL)
|
||||
return build_id_name;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Scan and build partial symbols for a symbol file.
|
||||
We have been initialized by a call to elf_symfile_init, which
|
||||
currently does nothing.
|
||||
|
|
|
@ -1,3 +1,13 @@
|
|||
2013-10-08 Tom Tromey <tromey@redhat.com>
|
||||
|
||||
* gdb.dwarf2/dwzbuildid.exp: New file.
|
||||
* lib/dwarf.exp (Dwarf::_section): Add "flags" and "type"
|
||||
parameters.
|
||||
(Dwarf::_defer_output): Change "section" parameter to
|
||||
"section_spec"; update.
|
||||
(Dwarf::gnu_debugaltlink, Dwarf::_note, Dwarf::build_id): New
|
||||
procs.
|
||||
|
||||
2013-10-08 Joel Brobecker <brobecker@adacore.com>
|
||||
|
||||
* gdb.ada/mi_catch_ex.exp: Make "mi_execute_to" test names unique.
|
||||
|
|
170
gdb/testsuite/gdb.dwarf2/dwzbuildid.exp
Normal file
170
gdb/testsuite/gdb.dwarf2/dwzbuildid.exp
Normal file
|
@ -0,0 +1,170 @@
|
|||
# Copyright 2013 Free Software Foundation, Inc.
|
||||
|
||||
# This program 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.
|
||||
#
|
||||
# This program 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/>.
|
||||
|
||||
load_lib dwarf.exp
|
||||
|
||||
# This test can only be run on targets which support DWARF-2 and use gas.
|
||||
if {![dwarf2_support]} {
|
||||
return 0
|
||||
}
|
||||
|
||||
# No remote host testing either.
|
||||
if {[is_remote host]} {
|
||||
return 0
|
||||
}
|
||||
|
||||
|
||||
# Lots of source files since we test a few cases and make new files
|
||||
# for each.
|
||||
# The tests are:
|
||||
# ok - the main file refers to a dwz and the buildids match
|
||||
# mismatch - the buildids do not match
|
||||
# fallback - the buildids do not match but a match is found via buildid
|
||||
standard_testfile main.c \
|
||||
dwzbuildid-ok-base.S dwzbuildid-ok-sep.S \
|
||||
dwzbuildid-mismatch-base.S dwzbuildid-mismatch-sep.S \
|
||||
dwzbuildid-fallback-base.S dwzbuildid-fallback-sep.S \
|
||||
dwzbuildid-fallback-ok.S
|
||||
|
||||
# Write some assembly that just has a .gnu_debugaltlink section.
|
||||
proc write_just_debugaltlink {filename dwzname buildid} {
|
||||
set asm_file [standard_output_file $filename]
|
||||
|
||||
Dwarf::assemble $asm_file {
|
||||
upvar dwzname dwzname
|
||||
upvar buildid buildid
|
||||
|
||||
gnu_debugaltlink $dwzname $buildid
|
||||
|
||||
# Only the DWARF reader checks .gnu_debugaltlink, so make sure
|
||||
# there is a bit of DWARF in here.
|
||||
cu {} {
|
||||
compile_unit {{language @DW_LANG_C}} {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Write some DWARF that also sets the buildid.
|
||||
proc write_dwarf_file {filename buildid {value 99}} {
|
||||
set asm_file [standard_output_file $filename]
|
||||
|
||||
Dwarf::assemble $asm_file {
|
||||
declare_labels partial_label double_label int_label int_label2
|
||||
|
||||
upvar buildid buildid
|
||||
upvar value value
|
||||
|
||||
build_id $buildid
|
||||
|
||||
cu {} {
|
||||
compile_unit {{language @DW_LANG_C}} {
|
||||
int_label2: base_type {
|
||||
{name int}
|
||||
{byte_size 4 sdata}
|
||||
{encoding @DW_ATE_signed}
|
||||
}
|
||||
|
||||
constant {
|
||||
{name the_int}
|
||||
{type :$int_label2}
|
||||
{const_value $value data1}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if { [gdb_compile ${srcdir}/${subdir}/${srcfile} ${binfile}1.o \
|
||||
object {nodebug}] != "" } {
|
||||
return -1
|
||||
}
|
||||
|
||||
# The values don't really matter, just whether they are equal.
|
||||
set ok_prefix 01
|
||||
set ok_suffix 0203040506
|
||||
set ok_suffix2 02030405ff
|
||||
set ok_buildid ${ok_prefix}${ok_suffix}
|
||||
set ok_buildid2 ${ok_prefix}${ok_suffix2}
|
||||
set bad_buildid ffffffffffff
|
||||
|
||||
set outdir [standard_output_file {}]
|
||||
set basedir $outdir/.build-id
|
||||
file mkdir $basedir $basedir/$ok_prefix
|
||||
|
||||
# Test where the separate debuginfo's buildid matches.
|
||||
write_just_debugaltlink $srcfile2 ${binfile}3.o $ok_buildid
|
||||
write_dwarf_file $srcfile3 $ok_buildid
|
||||
|
||||
# Test where the separate debuginfo's buildid does not match.
|
||||
write_just_debugaltlink $srcfile4 ${binfile}5.o $ok_buildid
|
||||
write_dwarf_file $srcfile5 $bad_buildid
|
||||
|
||||
# Test where the separate debuginfo's buildid does not match, but then
|
||||
# we find a match in the .build-id directory.
|
||||
write_just_debugaltlink $srcfile6 ${binfile}7.o $ok_buildid2
|
||||
# Use 77 as the value so that if we load the bad debuginfo, we will
|
||||
# see the wrong result.
|
||||
write_dwarf_file $srcfile7 $bad_buildid 77
|
||||
write_dwarf_file $srcfile8 $ok_buildid2
|
||||
|
||||
# Compile everything.
|
||||
for {set i 2} {$i <= 8} {incr i} {
|
||||
if {[gdb_compile [standard_output_file [set srcfile$i]] \
|
||||
${binfile}$i.o object nodebug] != ""} {
|
||||
return -1
|
||||
}
|
||||
}
|
||||
|
||||
# Copy a file into the .build-id place for the "fallback" test.
|
||||
file copy -force -- ${binfile}8.o $basedir/$ok_prefix/$ok_suffix2.debug
|
||||
|
||||
# Link the executables.
|
||||
if {[gdb_compile [list ${binfile}1.o ${binfile}2.o] ${binfile}-ok \
|
||||
executable {}] != ""} {
|
||||
return -1
|
||||
}
|
||||
|
||||
if {[gdb_compile [list ${binfile}1.o ${binfile}4.o] ${binfile}-mismatch \
|
||||
executable {}] != ""} {
|
||||
return -1
|
||||
}
|
||||
|
||||
if {[gdb_compile [list ${binfile}1.o ${binfile}6.o] ${binfile}-fallback \
|
||||
executable {}] != ""} {
|
||||
return -1
|
||||
}
|
||||
|
||||
|
||||
foreach testname {ok mismatch fallback} {
|
||||
with_test_prefix $testname {
|
||||
gdb_exit
|
||||
gdb_start
|
||||
gdb_reinitialize_dir $srcdir/$subdir
|
||||
|
||||
gdb_test_no_output "set debug-file-directory $outdir"
|
||||
|
||||
gdb_load ${binfile}-${testname}
|
||||
|
||||
if {[runto_main]} {
|
||||
if {$testname == "mismatch"} {
|
||||
gdb_test "print the_int" \
|
||||
"No symbol table is loaded.*"
|
||||
} else {
|
||||
gdb_test "print the_int" " = 99"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -523,20 +523,27 @@ namespace eval Dwarf {
|
|||
}
|
||||
}
|
||||
|
||||
proc _section {name} {
|
||||
_emit " .section $name"
|
||||
proc _section {name {flags ""} {type ""}} {
|
||||
if {$flags == "" && $type == ""} {
|
||||
_emit " .section $name"
|
||||
} elseif {$type == ""} {
|
||||
_emit " .section $name, \"$flags\""
|
||||
} else {
|
||||
_emit " .section $name, \"$flags\", %$type"
|
||||
}
|
||||
}
|
||||
|
||||
proc _defer_output {section body} {
|
||||
# SECTION_SPEC is a list of arguments to _section.
|
||||
proc _defer_output {section_spec body} {
|
||||
variable _defer
|
||||
variable _deferred_output
|
||||
|
||||
set old_defer $_defer
|
||||
set _defer $section
|
||||
set _defer [lindex $section_spec 0]
|
||||
|
||||
if {![info exists _deferred_output($_defer)]} {
|
||||
set _deferred_output($_defer) ""
|
||||
_section $section
|
||||
eval _section $section_spec
|
||||
}
|
||||
|
||||
uplevel $body
|
||||
|
@ -948,6 +955,51 @@ namespace eval Dwarf {
|
|||
unset the_array(_)
|
||||
}
|
||||
|
||||
# Emit a .gnu_debugaltlink section with the given file name and
|
||||
# build-id. The buildid should be represented as a hexadecimal
|
||||
# string, like "ffeeddcc".
|
||||
proc gnu_debugaltlink {filename buildid} {
|
||||
_defer_output .gnu_debugaltlink {
|
||||
_op .ascii [_quote $filename]
|
||||
foreach {a b} [split $buildid {}] {
|
||||
_op .byte 0x$a$b
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
proc _note {type name hexdata} {
|
||||
set namelen [expr [string length $name] + 1]
|
||||
|
||||
# Name size.
|
||||
_op .4byte $namelen
|
||||
# Data size.
|
||||
_op .4byte [expr [string length $hexdata] / 2]
|
||||
# Type.
|
||||
_op .4byte $type
|
||||
# The name.
|
||||
_op .ascii [_quote $name]
|
||||
# Alignment.
|
||||
set align 2
|
||||
set total [expr {($namelen + (1 << $align) - 1) & (-1 << $align)}]
|
||||
for {set i $namelen} {$i < $total} {incr i} {
|
||||
_op .byte 0
|
||||
}
|
||||
# The data.
|
||||
foreach {a b} [split $hexdata {}] {
|
||||
_op .byte 0x$a$b
|
||||
}
|
||||
}
|
||||
|
||||
# Emit a note section holding the given build-id.
|
||||
proc build_id {buildid} {
|
||||
_defer_output {.note.gnu.build-id a note} {
|
||||
# From elf/common.h.
|
||||
set NT_GNU_BUILD_ID 3
|
||||
|
||||
_note $NT_GNU_BUILD_ID GNU $buildid
|
||||
}
|
||||
}
|
||||
|
||||
# The top-level interface to the DWARF assembler.
|
||||
# FILENAME is the name of the file where the generated assembly
|
||||
# code is written.
|
||||
|
|
Loading…
Reference in a new issue