From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 13956 invoked by alias); 30 Nov 2010 06:07:15 -0000 Received: (qmail 13635 invoked by uid 22791); 30 Nov 2010 06:07:10 -0000 X-SWARE-Spam-Status: No, hits=-6.0 required=5.0 tests=AWL,BAYES_00,RCVD_IN_DNSWL_HI,SPF_HELO_PASS,TW_BJ,TW_CP,TW_CS,TW_XN,TW_YM,T_RP_MATCHES_RCVD X-Spam-Check-By: sourceware.org Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Tue, 30 Nov 2010 06:07:01 +0000 Received: from int-mx09.intmail.prod.int.phx2.redhat.com (int-mx09.intmail.prod.int.phx2.redhat.com [10.5.11.22]) by mx1.redhat.com (8.13.8/8.13.8) with ESMTP id oAU66xHe004631 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK) for ; Tue, 30 Nov 2010 01:07:00 -0500 Received: from host0.dyn.jankratochvil.net (ovpn01.gateway.prod.ext.phx2.redhat.com [10.5.9.1]) by int-mx09.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id oAU66tGM023313 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO); Tue, 30 Nov 2010 01:06:58 -0500 Received: from host0.dyn.jankratochvil.net (localhost.localdomain [127.0.0.1]) by host0.dyn.jankratochvil.net (8.14.4/8.14.4) with ESMTP id oAU66rSa002781; Tue, 30 Nov 2010 07:06:54 +0100 Received: (from jkratoch@localhost) by host0.dyn.jankratochvil.net (8.14.4/8.14.4/Submit) id oAU66qjY002774; Tue, 30 Nov 2010 07:06:52 +0100 Date: Tue, 30 Nov 2010 06:07:00 -0000 From: Jan Kratochvil To: sami wagiaalla Cc: gdb-patches@sourceware.org Subject: Re: [patch 1/2] build id Message-ID: <20101130060652.GB554@host0.dyn.jankratochvil.net> References: <4CEBEB78.1020809@redhat.com> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="ikeVEW9yuYc//A+q" Content-Disposition: inline In-Reply-To: <4CEBEB78.1020809@redhat.com> User-Agent: Mutt/1.5.21 (2010-09-15) X-IsSubscribed: yes Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org X-SW-Source: 2010-11/txt/msg00498.txt.bz2 --ikeVEW9yuYc//A+q Content-Type: text/plain; charset=us-ascii Content-Disposition: inline Content-length: 4170 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 ) 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 --ikeVEW9yuYc//A+q Content-Type: text/plain; charset=us-ascii Content-Disposition: inline; filename="gdb-buildid-v1.patch" Content-length: 26224 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 (¤t_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, ¤t_target, auto_solib_add); +#else + solib_add (NULL, 0, ¤t_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 #include @@ -61,6 +64,7 @@ #include #include #include +#include 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: : 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); + /* From dwarf2read.c */ extern int dwarf2_has_info (struct objfile *); --ikeVEW9yuYc//A+q--