Re: [patch 1/2] build id

Jan Kratochvil
2010-11-30T06:07:15Z

[-- Attachment #1: Type: text/plain, Size: 4170 bytes --]

Hi Sami,

really forgot these two FIXMEs should be probably dealt with.

On Tue, 23 Nov 2010 17:27:36 +0100, sami wagiaalla wrote:
> +/* Locate NT_GNU_BUILD_ID and return its matching debug filename.
> + FIXME: NOTE decoding should be unified with the BFD core notes decoding. */
[...]
> +static void
> +elf_swap_ehdr_in (bfd *abfd,
> + const Elf64_External_Ehdr *src64,
> + Elf_Internal_Ehdr *dst)
> +{
> + int is64 = bfd_get_arch_size (abfd) == 64;
> +#define SRC(field) (is64 ? src64->field \
> + : ((const Elf32_External_Ehdr *) src64)->field)
> +
> + int signed_vma = get_elf_backend_data (abfd)->sign_extend_vma;
> + memcpy (dst->e_ident, SRC (e_ident), EI_NIDENT);
> + dst->e_type = H_GET_16 (abfd, SRC (e_type));
> + dst->e_machine = H_GET_16 (abfd, SRC (e_machine));
> + dst->e_version = H_GET_32 (abfd, SRC (e_version));
> + if (signed_vma)
> + dst->e_entry = H_GET_SIGNED_WORD (abfd, SRC (e_entry));
> + else
> + dst->e_entry = H_GET_WORD (abfd, SRC (e_entry));
> + dst->e_phoff = H_GET_WORD (abfd, SRC (e_phoff));
> + dst->e_shoff = H_GET_WORD (abfd, SRC (e_shoff));
> + dst->e_flags = H_GET_32 (abfd, SRC (e_flags));
> + dst->e_ehsize = H_GET_16 (abfd, SRC (e_ehsize));
> + dst->e_phentsize = H_GET_16 (abfd, SRC (e_phentsize));
> + dst->e_phnum = H_GET_16 (abfd, SRC (e_phnum));
> + dst->e_shentsize = H_GET_16 (abfd, SRC (e_shentsize));
> + dst->e_shnum = H_GET_16 (abfd, SRC (e_shnum));
> + dst->e_shstrndx = H_GET_16 (abfd, SRC (e_shstrndx));
> +
> +#undef SRC
> +}

The code like this one is copy-pasted from bfd/elfcode.h . This is apparently
wrong and the FIXME tries to address this.

OTOH I had a first version [attached] which was really trying to apply bfd/ to
any functionality and it was probably also wrong. ELF is not so complicated
it would always make sense to bend over backwards any piece of bfd/ for any
ELF functionality GDB needs.

I cannot say offhand how much it should be merged with bfd/ but it seems to me
it should be merged a bit more, the copy-pasting is too obvious now.

> +/* BUILD_ID_ADDR_GET gets ADDR located somewhere in the object.
> + Find the first section before ADDR containing an ELF header.
> + We rely on the fact the sections from multiple files do not mix.
> + FIXME: We should check ADDR is contained _inside_ the section with possibly
> + missing content (P_FILESZ < P_MEMSZ). These omitted sections are currently
> + hidden by _BFD_ELF_MAKE_SECTION_FROM_PHDR. */

When you dump a core each segment can have now three kinds of dump:

/usr/share/doc/kernel-doc-*/Documentation/filesystems/proc.txt
- (bit 3) file-backed shared memory
- (bit 4) ELF header pages in file-backed private memory areas (it is
effective only if the bit 2 is cleared)

echo 0x7f >/proc/self/coredump_filter
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x001000 0x0000000000400000 0x0000000000000000 0x0d5000 0x0d5000 R E 0x1000
^^^^^^^^
echo 0x00 >/proc/self/coredump_filter
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x001000 0x0000000000400000 0x0000000000000000 0x000000 0x0d5000 R E 0x1000
^^^^^^^^

echo 0x33 >/proc/self/coredump_filter # default
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x001000 0x0000000000400000 0x0000000000000000 0x001000 0x0d5000 R E 0x1000
^^^^^^^^

bfd/ generates virtual sections (asection) from these segments, for some (or
only the last? _bfd_elf_make_section_from_phdr <split != 0>) cases two
sections are generated.

If the code chooses the wrong of two sections covering that same memory range
it may miss the build-id content as it may be in the other section.

Also it may check even < and not just the >= condition to avoid possibly false
build-ids (it may not be needed, but not sure why it is not now).

Thanks,
Jan

[-- Attachment #2: gdb-buildid-v1.patch --]
[-- Type: text/plain, Size: 26224 bytes --]

Index: bfd/elf.c
===================================================================
RCS file: /cvs/src/src/bfd/elf.c,v
retrieving revision 1.400
diff -u -p -r1.400 elf.c
--- bfd/elf.c 24 Jul 2007 08:09:20 -0000 1.400
+++ bfd/elf.c 25 Jul 2007 01:00:39 -0000
@@ -2225,6 +2225,11 @@ _bfd_elf_new_section_hook (bfd *abfd, as
by the difference between the two sizes. In effect, the segment is split
into it's initialized and uninitialized parts.

+ Meaning of files size different than the memory size is different for core
+ files by Linux kernel with its fs.binfmt_elf.core_dump_elf_headers = 1.
+ Missing memory data should be fetched there from the readonly segments of
+ the executable file.
+
*/

bfd_boolean
@@ -2239,8 +2244,8 @@ _bfd_elf_make_section_from_phdr (bfd *ab
size_t len;
int split;

- split = ((hdr->p_memsz > 0)
- && (hdr->p_filesz > 0)
+ split = (abfd->format != bfd_core
+ && (hdr->p_memsz > 0) && (hdr->p_filesz > 0)
&& (hdr->p_memsz > hdr->p_filesz));
sprintf (namebuf, "%s%d%s", typename, index, split ? "a" : "");
len = strlen (namebuf) + 1;
Index: bfd/elfcode.h
===================================================================
RCS file: /cvs/src/src/bfd/elfcode.h,v
retrieving revision 1.84
diff -u -p -r1.84 elfcode.h
--- bfd/elfcode.h 9 Jul 2007 21:23:37 -0000 1.84
+++ bfd/elfcode.h 25 Jul 2007 01:00:39 -0000
@@ -1635,6 +1635,7 @@ NAME(_bfd_elf,bfd_from_remote_memory)
int err;
unsigned int i;
bfd_vma loadbase;
+ int loadbase_set;

/* Read in the ELF header in external format. */
err = target_read_memory (ehdr_vma, (bfd_byte *) &x_ehdr, sizeof x_ehdr);
@@ -1711,6 +1712,7 @@ NAME(_bfd_elf,bfd_from_remote_memory)
contents_size = 0;
last_phdr = NULL;
loadbase = ehdr_vma;
+ loadbase_set = 0;
for (i = 0; i < i_ehdr.e_phnum; ++i)
{
elf_swap_phdr_in (templ, &x_phdrs[i], &i_phdrs[i]);
@@ -1725,8 +1727,13 @@ NAME(_bfd_elf,bfd_from_remote_memory)
if (segment_end > (bfd_vma) contents_size)
contents_size = segment_end;

- if ((i_phdrs[i].p_offset & -i_phdrs[i].p_align) == 0)
- loadbase = ehdr_vma - (i_phdrs[i].p_vaddr & -i_phdrs[i].p_align);
+ if (!loadbase_set && (i_phdrs[i].p_offset & -i_phdrs[i].p_align) == 0)
+ {
+ loadbase = ehdr_vma - (i_phdrs[i].p_vaddr & -i_phdrs[i].p_align);
+ /* Only the first segment indicates the file bias.
+ DATA sections may have P_VADDR arbitrarily higher. */
+ loadbase_set = 1;
+ }

last_phdr = &i_phdrs[i];
}
@@ -1775,7 +1782,7 @@ NAME(_bfd_elf,bfd_from_remote_memory)
if (end > (bfd_vma) contents_size)
end = contents_size;
err = target_read_memory ((loadbase + i_phdrs[i].p_vaddr)
- & -i_phdrs[i].p_align,
+ /* & -i_phdrs[i].p_align */,
contents + start, end - start);
if (err)
{
Index: gdb/Makefile.in
===================================================================
RCS file: /cvs/src/src/gdb/Makefile.in,v
retrieving revision 1.923
diff -u -p -r1.923 Makefile.in
--- gdb/Makefile.in 3 Jul 2007 12:14:43 -0000 1.923
+++ gdb/Makefile.in 25 Jul 2007 01:00:40 -0000
@@ -1888,7 +1888,7 @@ corelow.o: corelow.c $(defs_h) $(arch_ut
$(inferior_h) $(symtab_h) $(command_h) $(bfd_h) $(target_h) \
$(gdbcore_h) $(gdbthread_h) $(regcache_h) $(regset_h) $(symfile_h) \
$(exec_h) $(readline_h) $(gdb_assert_h) \
- $(exceptions_h) $(solib_h)
+ $(exceptions_h) $(solib_h) $(auxv_h) $(elf_common_h)
core-regset.o: core-regset.c $(defs_h) $(command_h) $(gdbcore_h) \
$(inferior_h) $(target_h) $(regcache_h) $(gdb_string_h) $(gregset_h)
cp-abi.o: cp-abi.c $(defs_h) $(value_h) $(cp_abi_h) $(command_h) $(gdbcmd_h) \
@@ -2709,7 +2709,7 @@ symfile.o: symfile.c $(defs_h) $(bfdlink
$(gdb_stabs_h) $(gdb_obstack_h) $(completer_h) $(bcache_h) \
$(hashtab_h) $(readline_h) $(gdb_assert_h) $(block_h) \
$(gdb_string_h) $(gdb_stat_h) $(observer_h) $(exec_h) \
- $(parser_defs_h) $(varobj_h)
+ $(parser_defs_h) $(varobj_h) $(gdb_stdint_h) $(libbfd_h) $(elf_bfd_h)
symfile-mem.o: symfile-mem.c $(defs_h) $(symtab_h) $(gdbcore_h) \
$(objfiles_h) $(exceptions_h) $(gdbcmd_h) $(target_h) $(value_h) \
$(symfile_h) $(observer_h) $(auxv_h) $(elf_common_h)
Index: gdb/corelow.c
===================================================================
RCS file: /cvs/src/src/gdb/corelow.c,v
retrieving revision 1.63
diff -u -p -r1.63 corelow.c
--- gdb/corelow.c 16 Jun 2007 17:16:25 -0000 1.63
+++ gdb/corelow.c 25 Jul 2007 01:00:41 -0000
@@ -46,6 +46,8 @@
#include "gdb_assert.h"
#include "exceptions.h"
#include "solib.h"
+#include "auxv.h"
+#include "elf/common.h"

#ifndef O_LARGEFILE
@@ -253,6 +255,50 @@ add_to_thread_list (bfd *abfd, asection
inferior_ptid = pid_to_ptid (thread_id); /* Yes, make it current */
}

+static void
+build_id_locate_exec (int from_tty)
+{
+ CORE_ADDR at_entry;
+ struct build_id *build_id;
+ char *exec_filename, *debug_filename;
+
+ if (exec_bfd != NULL)
+ return;
+
+ if (target_auxv_search (&current_target, AT_ENTRY, &at_entry) <= 0)
+ return;
+
+ build_id = build_id_addr_get (at_entry);
+ if (build_id == NULL)
+ return;
+
+ exec_filename = build_id_to_filename (build_id, debug_file_directory, 0);
+ if (exec_filename != NULL)
+ exec_file_attach (exec_filename, from_tty);
+
+ /* `.note.gnu.build-id' section exists even for files without a separate
+ debuginfo. */
+ debug_filename = build_id_to_filename (build_id, debug_file_directory, 1);
+ if (debug_filename != NULL)
+ {
+ symbol_file_add_main (debug_filename, from_tty);
+ xfree (debug_filename);
+ }
+ else if (exec_filename != NULL)
+ symbol_file_add_main (exec_filename, from_tty);
+
+ xfree (exec_filename);
+
+ if (exec_filename != NULL)
+ {
+#ifdef SOLIB_ADD
+ SOLIB_ADD (NULL, 0, &current_target, auto_solib_add);
+#else
+ solib_add (NULL, 0, &current_target, auto_solib_add);
+#endif
+ }
+}
+
/* This routine opens and sets up the core file bfd. */

static void
@@ -377,6 +423,9 @@ core_open (char *filename, int from_tty)
/* Fetch all registers from core file. */
target_fetch_registers (get_current_regcache (), -1);

+ /* Find build_id identifiers. */
+ build_id_locate_exec (from_tty);
+
/* Now, set up the frame cache, and print the top of stack. */
reinit_frame_cache ();
print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC);
Index: gdb/solib-svr4.c
===================================================================
RCS file: /cvs/src/src/gdb/solib-svr4.c,v
retrieving revision 1.70
diff -u -p -r1.70 solib-svr4.c
--- gdb/solib-svr4.c 12 Jul 2007 20:15:24 -0000 1.70
+++ gdb/solib-svr4.c 25 Jul 2007 01:00:42 -0000
@@ -749,10 +749,29 @@ svr4_current_sos (void)
safe_strerror (errcode));
else
{
- strncpy (new->so_name, buffer, SO_NAME_MAX_PATH_SIZE - 1);
- new->so_name[SO_NAME_MAX_PATH_SIZE - 1] = '\0';
+ struct build_id *build_id;
+
+ strncpy (new->so_original_name, buffer, SO_NAME_MAX_PATH_SIZE - 1);
+ new->so_original_name[SO_NAME_MAX_PATH_SIZE - 1] = '\0';
xfree (buffer);
- strcpy (new->so_original_name, new->so_name);
+ /* May get overwritten below. */
+ strcpy (new->so_name, new->so_original_name);
+
+ build_id = build_id_addr_get (LM_DYNAMIC_FROM_LINK_MAP (new));
+ if (build_id != NULL)
+ {
+ char *name;
+
+ name = build_id_to_filename (build_id, debug_file_directory,
+ 0);
+ if (name != NULL)
+ {
+ strncpy (new->so_name, name, SO_NAME_MAX_PATH_SIZE - 1);
+ new->so_name[SO_NAME_MAX_PATH_SIZE - 1] = '\0';
+ xfree (name);
+ }
+ xfree (build_id);
+ }
}

/* If this entry has no name, or its name matches the name
Index: gdb/symfile.c
===================================================================
RCS file: /cvs/src/src/gdb/symfile.c,v
retrieving revision 1.188
diff -u -p -r1.188 symfile.c
--- gdb/symfile.c 3 Jul 2007 15:32:20 -0000 1.188
+++ gdb/symfile.c 25 Jul 2007 01:00:42 -0000
@@ -53,6 +53,9 @@
#include "exec.h"
#include "parser-defs.h"
#include "varobj.h"
+#include "gdb_stdint.h"
+#include "libbfd.h"
+#include "elf-bfd.h"

#include <sys/types.h>
#include <fcntl.h>
@@ -61,6 +64,7 @@
#include <ctype.h>
#include <time.h>
#include <sys/time.h>
+#include <sys/param.h>

int (*deprecated_ui_load_progress_hook) (const char *section, unsigned long num);
@@ -1084,7 +1088,7 @@ symbol_file_add_with_addrs_or_offsets (b
}

debugfile = find_separate_debug_file (objfile);
- if (debugfile)
+ if (debugfile && strcmp (debugfile, objfile->name) != 0)
{
if (addrs != NULL)
{
@@ -1225,15 +1229,346 @@ symbol_file_clear (int from_tty)
printf_unfiltered (_("No symbol file now.\n"));
}

+/* Locate NT_GNU_BUILD_ID and return its matching debug filename.
+ FIXME: NOTE decoding should be unified with the BFD core notes decoding. */
+
+struct build_id *
+build_id_buf_get (bfd *abfd, gdb_byte *buf, bfd_size_type size)
+{
+ bfd_byte *p;
+
+ p = buf;
+ while (p < buf + size)
+ {
+ /* FIXME: bad alignment assumption. */
+ Elf_External_Note *xnp = (Elf_External_Note *) p;
+ size_t namesz = H_GET_32 (abfd, xnp->namesz);
+ size_t descsz = H_GET_32 (abfd, xnp->descsz);
+ bfd_byte *descdata = xnp->name + BFD_ALIGN (namesz, 4);
+
+ if (H_GET_32 (abfd, xnp->type) == NT_GNU_BUILD_ID
+ && namesz == sizeof "GNU"
+ && memcmp (xnp->name, "GNU", sizeof "GNU") == 0)
+ {
+ size_t size = descsz;
+ gdb_byte *data = (void *) descdata;
+ struct build_id *retval;
+
+ retval = xmalloc (sizeof *retval - 1 + size);
+ retval->size = size;
+ memcpy (retval->data, data, size);
+
+ return retval;
+ }
+ p = descdata + BFD_ALIGN (descsz, 4);
+ }
+ return NULL;
+}
+
+struct build_id *
+build_id_bfd_get (bfd *abfd)
+{
+ Elf_Internal_Ehdr *ehdr;
+ Elf_Internal_Phdr *phdr;
+ int i;
+
+ if (!bfd_check_format (abfd, bfd_object)
+ || bfd_get_flavour (abfd) != bfd_target_elf_flavour)
+ return NULL;
+
+ ehdr = elf_tdata (abfd)->elf_header;
+
+ if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN)
+ return NULL;
+
+ phdr = elf_tdata (abfd)->phdr;
+
+ for (i = 0; i < ehdr->e_phnum; i++)
+ if (phdr[i].p_type == PT_NOTE && phdr[i].p_filesz > 0)
+ {
+ Elf_Internal_Phdr *hdr = &phdr[i];
+ struct build_id *retval;
+ gdb_byte *buf;
+
+ if (bfd_seek (abfd, hdr->p_offset, SEEK_SET) != 0)
+ return NULL;
+
+ buf = xmalloc (hdr->p_filesz);
+
+ if (bfd_bread (buf, hdr->p_filesz, abfd) != hdr->p_filesz)
+ {
+ xfree (buf);
+ return NULL;
+ }
+
+ retval = build_id_buf_get (abfd, buf, hdr->p_filesz);
+ if (retval != NULL)
+ {
+ xfree (buf);
+ return retval;
+ }
+ }
+ return NULL;
+}
+
+/* Transfer memory without any failures on the parts missing in the core file.
+ FIXME: Avoid BFD warnings:
+ BFD: <in-memory>: invalid string offset 27 >= 0 for section `' */
+
+static asection *build_id_read_memory_sect;
+
+static int
+build_id_read_memory (CORE_ADDR memaddr, gdb_byte *myaddr, int len)
+{
+ bfd_size_type size, transfer;
+ bfd_vma vma = bfd_section_vma (build_id_read_memory_sect->owner,
+ build_id_read_memory_sect);
+
+ if (memaddr >= vma)
+ memaddr -= vma;
+ else
+ {
+ /* Not cleared. Should not happen. */
+ myaddr += vma - memaddr;
+ if (len >= vma - memaddr)
+ len -= vma - memaddr;
+ else
+ len = 0;
+ memaddr = 0;
+ }
+
+ size = bfd_get_section_size (build_id_read_memory_sect);
+ transfer = len;
+ if (memaddr >= size)
+ transfer = 0;
+ else if (memaddr + len >= size)
+ transfer = size - memaddr;
+ else
+ transfer = len;
+ if (transfer > 0
+ && !bfd_get_section_contents (build_id_read_memory_sect->owner,
+ build_id_read_memory_sect,
+ myaddr, memaddr, transfer))
+ return EIO;
+ /* BFD tries to read from arbitrary memory - far VMA areas.
+ Do not: memset (myaddr + transfer, 0, len - transfer);
+ It would overwrite the first page content.
+ FIXME: Fix BFD, it may leave unitialized memory here. */
+ return 0;
+}
+
+static struct build_id *
+build_id_try_sect (asection *sect)
+{
+ bfd *frag_bfd;
+ bfd_vma sect_vma = bfd_section_vma (core_bfd, sect);
+ bfd_vma loadbase;
+ struct build_id *retval;
+
+ build_id_read_memory_sect = sect;
+ frag_bfd = bfd_elf_bfd_from_remote_memory (core_bfd, sect_vma, &loadbase,
+ build_id_read_memory);
+ if (frag_bfd == NULL)
+ return NULL;
+
+ retval = build_id_bfd_get (frag_bfd);
+
+ if (!bfd_close (frag_bfd))
+ warning (_("cannot close \"%s\": %s"), bfd_get_filename (frag_bfd),
+ bfd_errmsg (bfd_get_error ()));
+
+ return retval;
+}
+
+/* BUILD_ID_ADDR_GET gets ADDR located somewhere in the object.
+ Find the first section before ADDR containing an ELF header.
+ We rely on the fact the sections from multiple files do not mix.
+ FIXME: We should check ADDR is contained _inside_ the section with possibly
+ missing content (P_FILESZ < P_MEMSZ). These omitted sections are currently
+ hidden by _BFD_ELF_MAKE_SECTION_FROM_PHDR. */
+
+static CORE_ADDR build_id_addr;
+struct build_id_addr_sect
+ {
+ struct build_id_addr_sect *next;
+ asection *sect;
+ };
+static struct build_id_addr_sect *build_id_addr_sect;
+
+static void build_id_addr_candidate (bfd *abfd, asection *sect, void *obj)
+{
+ bfd_vma sect_vma = bfd_section_vma (abfd, sect);
+
+ if (build_id_addr >= bfd_section_vma (abfd, sect))
+ {
+ struct build_id_addr_sect *candidate;
+
+ candidate = xmalloc (sizeof *candidate);
+ candidate->next = build_id_addr_sect;
+ build_id_addr_sect = candidate;
+ candidate->sect = sect;
+ }
+}
+
+struct build_id *
+build_id_addr_get (CORE_ADDR addr)
+{
+ struct build_id_addr_sect *candidate;
+ struct build_id *retval = NULL;
+
+ if (core_bfd == NULL)
+ return NULL;
+
+ build_id_addr = addr;
+ gdb_assert (build_id_addr_sect == NULL);
+ bfd_map_over_sections (core_bfd, build_id_addr_candidate, NULL);
+
+ /* Sections are sorted in the high-to-low VMAs order. */
+ for (candidate = build_id_addr_sect; candidate != NULL;
+ candidate = candidate->next)
+ {
+ retval = build_id_try_sect (build_id_addr_sect->sect);
+ if (retval != NULL)
+ break;
+ }
+
+ while (build_id_addr_sect != NULL)
+ {
+ candidate = build_id_addr_sect;
+ build_id_addr_sect = candidate->next;
+ xfree (candidate);
+ }
+
+ return retval;
+}
+
+/* Try to resolve symlinks in `/usr/lib/debug/.build-id/' for the user. */
+
static char *
-get_debug_link_info (struct objfile *objfile, unsigned long *crc32_out)
+build_id_resolve (char *filename)
+{
+ char dest[MAXPATHLEN];
+ ssize_t dest_len;
+
+ dest_len = readlink (filename, dest, sizeof dest - 1);
+ if (dest_len >= 0)
+ {
+ dest[dest_len] = 0;
+ return xstrdup (dest);
+ }
+ else if (errno == ENOENT)
+ return NULL;
+
+ return filename;
+}
+
+static int
+build_id_verify (const char *filename, struct build_id *check, int is_debug)
+{
+ bfd *abfd;
+ struct build_id *found = NULL;
+ int retval = 0;
+
+ abfd = bfd_openr (filename, gnutarget);
+ if (abfd == NULL)
+ {
+ warning (_("cannot open \"%s\" to verify its build-id: %s"),
+ filename, bfd_errmsg (bfd_get_error ()));
+ return 0;
+ }
+
+ /* Separate debuginfo files have corrupted PHDR but SHDR is correct there.
+ Core files may have missing (corrupt) SHDR but PDHR is correct there. */
+ if (!is_debug)
+ found = build_id_bfd_get (abfd);
+ else if (bfd_check_format (abfd, bfd_object)
+ && bfd_get_flavour (abfd) == bfd_target_elf_flavour)
+ {
+ asection *sect = NULL;
+ bfd_byte *buf;
+
+ sect = bfd_get_section_by_name (abfd, ".note.gnu.build-id");
+ if (sect != NULL && bfd_malloc_and_get_section (abfd, sect, &buf))
+ {
+ found = build_id_buf_get (abfd, buf,
+ bfd_get_section_limit (abfd, sect));
+ xfree (buf);
+ }
+ }
+
+ 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;
+
+ if (!bfd_close (abfd))
+ warning (_("cannot close \"%s\": %s"), filename,
+ bfd_errmsg (bfd_get_error ()));
+ return retval;
+}
+
+char *
+build_id_to_filename (struct build_id *build_id, const char *prefix,
+ int add_debug_suffix)
+{
+ char *link, *s, *retval;
+ gdb_byte *data = build_id->data;
+ size_t size = build_id->size;
+
+ /* DEBUG_FILE_DIRECTORY/.build-id/ab/cdef */
+ link = xmalloc (strlen (prefix) + strlen ("/.build-id/") + 1
+ + 2 * size
+ + (add_debug_suffix ? sizeof ".debug" - 1 : 0)
+ + 1);
+ s = link + sprintf (link, "%s/.build-id/", prefix);
+ if (size > 0)
+ {
+ size--;
+ s += sprintf (s, "%02x", (unsigned) *data++);
+ }
+ if (size > 0)
+ *s++ = '/';
+ while (size-- > 0)
+ s += sprintf (s, "%02x", (unsigned) *data++);
+ if (add_debug_suffix)
+ strcpy (s, ".debug");
+ else
+ *s = 0;
+
+ retval = build_id_resolve (link);
+ /* BUILD_ID_RESOLVE may return the original parameter not duplicated. */
+ if (retval == NULL || retval != link)
+ xfree (link);
+
+ if (retval != NULL && !build_id_verify (retval, build_id, add_debug_suffix))
+ {
+ xfree (retval);
+ retval = NULL;
+ }
+
+ return retval;
+}
+
+struct debug_link_info
+ {
+ uint32_t crc32;
+ char basename_debug[1];
+ };
+
+/* Read the `.gnu_debuglink' section. */
+
+static struct debug_link_info *
+get_debug_link_info (struct objfile *objfile)
{
asection *sect;
bfd_size_type debuglink_size;
- unsigned long crc32;
char *contents;
int crc_offset;
unsigned char *p;
+ struct debug_link_info *retval;

sect = bfd_get_section_by_name (objfile->obfd, ".gnu_debuglink");

@@ -1242,7 +1577,8 @@ get_debug_link_info (struct objfile *obj

debuglink_size = bfd_section_size (objfile->obfd, sect);

- contents = xmalloc (debuglink_size);
+ retval = xmalloc (sizeof *retval - 1 + debuglink_size);
+ contents = retval->basename_debug;
bfd_get_section_contents (objfile->obfd, sect, contents,
(file_ptr)0, (bfd_size_type)debuglink_size);

@@ -1250,30 +1586,101 @@ get_debug_link_info (struct objfile *obj
crc_offset = strlen (contents) + 1;
crc_offset = (crc_offset + 3) & ~3;

- crc32 = bfd_get_32 (objfile->obfd, (bfd_byte *) (contents + crc_offset));
+ retval->crc32 = bfd_get_32 (objfile->obfd,
+ (bfd_byte *) (contents + crc_offset));

- *crc32_out = crc32;
- return contents;
+ return retval;
}

+struct debug_info
+ {
+ /* Both pointers may be NULL if the section is not present. */
+ struct debug_link_info *debug_link;
+ struct build_id *build_id;
+ /* Never NULL, to be freed if DEBUG_LINK is NULL. */
+ char *basename_debug;
+ };
+
+/* Returns TRUE if a separate debug file may exist.
+ If FALSE is returned FREE_DEBUG_INFO must not be called. */
+
static int
-separate_debug_file_exists (const char *name, unsigned long crc)
+get_debug_info (struct objfile *objfile, struct debug_info *debug_info)
{
- unsigned long file_crc = 0;
- int fd;
- gdb_byte buffer[8*1024];
- int count;
+ debug_info->debug_link = get_debug_link_info (objfile);
+ debug_info->build_id = build_id_bfd_get (objfile->obfd);

- fd = open (name, O_RDONLY | O_BINARY);
- if (fd < 0)
+ if (debug_info->debug_link == NULL && debug_info->build_id == NULL)
return 0;

- while ((count = read (fd, buffer, sizeof (buffer))) > 0)
- file_crc = gnu_debuglink_crc32 (file_crc, buffer, count);
+ if (debug_info->debug_link != NULL)
+ debug_info->basename_debug = debug_info->debug_link->basename_debug;
+ else
+ xasprintf (&debug_info->basename_debug, "%s.debug",
+ lbasename (objfile->name));
+
+ return 1;
+}
+
+static void
+free_debug_info (struct debug_info *debug_info)
+{
+ if (debug_info->debug_link != NULL)
+ xfree (debug_info->debug_link);
+ else
+ xfree (debug_info->basename_debug);
+ xfree (debug_info->build_id);
+}
+
+static char *
+separate_debug_file_exists (const char *path_debug, const char *path_build_id,
+ struct debug_info *info)
+{
+ char *name_debug;
+
+ xasprintf (&name_debug, "%s/%s", path_debug, info->basename_debug);
+
+ /* On missing `.gnu_debuglink' but with existing `.note.gnu.build_id'
+ try just the `BASENAME.debug' existence without checking its CRC. */
+
+ if (info->debug_link == NULL && access (name_debug, R_OK) == 0)
+ return name_debug;
+
+ if (info->build_id != NULL)
+ {
+ char *build_id_name;
+
+ build_id_name = build_id_to_filename (info->build_id, path_build_id, 1);
+ if (build_id_name != NULL)
+ {
+ xfree (name_debug);
+ return build_id_name;
+ }
+ }
+
+ if (info->debug_link != NULL)
+ {
+ int fd;
+
+ fd = open (name_debug, O_RDONLY | O_BINARY);
+ if (fd >= 0)
+ {
+ unsigned long file_crc = 0;
+ gdb_byte buffer[8*1024];
+ int count;
+
+ while ((count = read (fd, buffer, sizeof (buffer))) > 0)
+ file_crc = gnu_debuglink_crc32 (file_crc, buffer, count);

- close (fd);
+ close (fd);

- return crc == file_crc;
+ if (info->debug_link->crc32 == file_crc)
+ return name_debug;
+ }
+ }
+
+ xfree (name_debug);
+ return NULL;
}

char *debug_file_directory = NULL;
@@ -1294,18 +1701,16 @@ static char *
find_separate_debug_file (struct objfile *objfile)
{
asection *sect;
- char *basename;
char *dir;
char *debugfile;
char *name_copy;
char *canon_name;
bfd_size_type debuglink_size;
- unsigned long crc32;
int i;
+ struct debug_info info;
+ char *retval;

- basename = get_debug_link_info (objfile, &crc32);
-
- if (basename == NULL)
+ if (! get_debug_info (objfile, &info))
return NULL;

dir = xstrdup (objfile->name);
@@ -1320,77 +1725,73 @@ find_separate_debug_file (struct objfile
break;
}
gdb_assert (i >= 0 && IS_DIR_SEPARATOR (dir[i]));
- dir[i+1] = '\0';
-
- debugfile = alloca (strlen (debug_file_directory) + 1
- + strlen (dir)
- + strlen (DEBUG_SUBDIRECTORY)
- + strlen ("/")
- + strlen (basename)
- + 1);
+ dir[i] = '\0';

/* First try in the same directory as the original file. */
- strcpy (debugfile, dir);
- strcat (debugfile, basename);
-
- if (separate_debug_file_exists (debugfile, crc32))
+ retval = separate_debug_file_exists (dir, dir, &info);
+ if (retval != NULL)
{
- xfree (basename);
+ free_debug_info (&info);
xfree (dir);
- return xstrdup (debugfile);
+ return retval;
}

+ debugfile = alloca (strlen (debug_file_directory) + 1
+ + strlen (dir)
+ + strlen ("/")
+ + strlen (DEBUG_SUBDIRECTORY)
+ + 1);
+
/* Then try in the subdirectory named DEBUG_SUBDIRECTORY. */
strcpy (debugfile, dir);
- strcat (debugfile, DEBUG_SUBDIRECTORY);
strcat (debugfile, "/");
- strcat (debugfile, basename);
+ strcat (debugfile, DEBUG_SUBDIRECTORY);

- if (separate_debug_file_exists (debugfile, crc32))
+ retval = separate_debug_file_exists (debugfile, debugfile, &info);
+ if (retval != NULL)
{
- xfree (basename);
+ free_debug_info (&info);
xfree (dir);
- return xstrdup (debugfile);
+ return retval;
}

- /* Then try in the global debugfile directory. */
+ /* Then try in the global debugfile directory.
+ DIR needs to be prosent for `.gnu_debuglink' but it must not be present
+ for `.note.gnu.build_id'. */
strcpy (debugfile, debug_file_directory);
strcat (debugfile, "/");
strcat (debugfile, dir);
- strcat (debugfile, basename);

- if (separate_debug_file_exists (debugfile, crc32))
+ retval = separate_debug_file_exists (debugfile, debug_file_directory, &info);
+ if (retval != NULL)
{
- xfree (basename);
+ free_debug_info (&info);
xfree (dir);
- return xstrdup (debugfile);
+ return retval;
}

/* If the file is in the sysroot, try using its base path in the
global debugfile directory. */
canon_name = lrealpath (dir);
- if (canon_name
- && strncmp (canon_name, gdb_sysroot, strlen (gdb_sysroot)) == 0
- && IS_DIR_SEPARATOR (canon_name[strlen (gdb_sysroot)]))
+ if (canon_name && strcmp (canon_name, gdb_sysroot) == 0)
{
strcpy (debugfile, debug_file_directory);
strcat (debugfile, canon_name + strlen (gdb_sysroot));
- strcat (debugfile, "/");
- strcat (debugfile, basename);

- if (separate_debug_file_exists (debugfile, crc32))
+ retval = separate_debug_file_exists (debugfile, debugfile, &info);
+ if (retval != NULL)
{
xfree (canon_name);
- xfree (basename);
+ free_debug_info (&info);
xfree (dir);
- return xstrdup (debugfile);
+ return retval;
}
}

if (canon_name)
xfree (canon_name);

- xfree (basename);
+ free_debug_info (&info);
xfree (dir);
return NULL;
}
Index: gdb/symfile.h
===================================================================
RCS file: /cvs/src/src/gdb/symfile.h,v
retrieving revision 1.39
diff -u -p -r1.39 symfile.h
--- gdb/symfile.h 18 Jun 2007 15:46:38 -0000 1.39
+++ gdb/symfile.h 25 Jul 2007 01:00:43 -0000
@@ -352,6 +352,18 @@ extern int symfile_map_offsets_to_segmen
struct symfile_segment_data *get_symfile_segment_data (bfd *abfd);
void free_symfile_segment_data (struct symfile_segment_data *data);

+/* build-id support. */
+struct build_id
+ {
+ size_t size;
+ gdb_byte data[1];
+ };
+
+extern struct build_id *build_id_addr_get (CORE_ADDR addr);
+extern struct build_id *build_id_bfd_get (bfd *abfd);
+extern char *build_id_to_filename (struct build_id *build_id,
+ const char *prefix, int add_debug_suffix);