src/
authorRoland McGrath <roland@redhat.com>
Thu, 4 Oct 2007 08:50:09 +0000 (08:50 +0000)
committerRoland McGrath <roland@redhat.com>
Thu, 4 Oct 2007 08:50:09 +0000 (08:50 +0000)
2007-10-04  Roland McGrath  <roland@redhat.com>

* readelf.c (print_archive_index): New variable.
(options, parse_opt): Accept -c/--archive-index to set it.
(dump_archive_index): New function.
(process_file): Take new arg WILL_PRINT_ARCHIVE_INDEX.
Call dump_archive_index on archives if set.
(main): Update caller.
(any_control_option): Give it file scope, moved out of ...
(parse_opt): ... here.

tests/
2007-10-04  Roland McGrath  <roland@redhat.com>

* run-readelf-test4.sh: New file.
* Makefile.am (TESTS, EXTRA_DIST): Add it.

31 files changed:
NEWS
libdw/ChangeLog
libdw/libdw.map
libdwfl/ChangeLog
libdwfl/Makefile.am
libdwfl/dwfl_build_id_find_debuginfo.c [moved from libelf/gelf_rawchunk.c with 67% similarity]
libdwfl/dwfl_build_id_find_elf.c [new file with mode: 0644]
libdwfl/dwfl_module.c
libdwfl/dwfl_module_build_id.c [new file with mode: 0644]
libdwfl/dwfl_module_getdwarf.c
libdwfl/dwfl_module_report_build_id.c [moved from libelf/gelf_freechunk.c with 69% similarity]
libdwfl/find-debuginfo.c
libdwfl/libdwfl.h
libdwfl/libdwflP.h
libdwfl/linux-kernel-modules.c
libelf/ChangeLog
libelf/Makefile.am
libelf/elf_end.c
libelf/elf_getdata_rawchunk.c [new file with mode: 0644]
libelf/gelf.h
libelf/libelf.h
libelf/libelf.map
libelf/libelfP.h
src/ChangeLog
src/elflint.c
src/readelf.c
src/unstrip.c
tests/ChangeLog
tests/Makefile.am
tests/run-readelf-test3.sh [new file with mode: 0755]
tests/run-readelf-test4.sh [new file with mode: 0755]

diff --git a/NEWS b/NEWS
index d257c1f..3120ce5 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,7 +1,20 @@
 Version 0.130:
 
 readelf: -p option can take an argument like -x for one section,
-        or no argument (as before) for all SHF_STRINGS sections
+        or no argument (as before) for all SHF_STRINGS sections;
+        new option --archive-index (or -c)
+
+libelf: new function elf_getdata_rawchunk, replaces gelf_rawchunk;
+       new functions gelf_getnote, gelf_getauxv, gelf_update_auxv
+
+readelf, elflint: handle SHT_NOTE sections without requiring phdrs
+
+libdwfl: new functions dwfl_build_id_find_elf, dwfl_build_id_find_debuginfo,
+        dwfl_module_build_id, dwfl_module_report_build_id;
+        support dynamic symbol tables found via phdrs;
+        dwfl_standard_find_debuginfo now uses build IDs when available
+
+unstrip: new option --list (or -n)
 
 Version 0.129:
 
index 7b6a44c..34c4e5b 100644 (file)
@@ -1,3 +1,11 @@
+2007-10-03  Roland McGrath  <roland@redhat.com>
+
+       * libdw.map (ELFUTILS_0.130: Add dwfl_build_id_find_elf
+       and dwfl_build_id_find_debuginfo.
+
+       * libdw.map (ELFUTILS_0.130): New version set, inherits from
+       ELFUTILS_0.127.  Add dwfl_module_build_id, dwfl_module_report_build_id.
+
 2007-10-02  Roland McGrath  <roland@redhat.com>
 
        * libdw_visit_scopes.c (classify_die): Return walk for class_type and
index 654bdfb..8ef5f63 100644 (file)
@@ -165,3 +165,14 @@ ELFUTILS_0.127 {
   local:
     *;
 } ELFUTILS_0.126;
+
+ELFUTILS_0.130 {
+  global:
+    dwfl_build_id_find_elf;
+    dwfl_build_id_find_debuginfo;
+    dwfl_module_build_id;
+    dwfl_module_report_build_id;
+
+  local:
+    *;
+} ELFUTILS_0.127;
index 3a99753..454d934 100644 (file)
@@ -1,3 +1,44 @@
+2007-10-04  Roland McGrath  <roland@redhat.com>
+
+       * linux-kernel-modules.c (intuit_kernel_bounds): Take new arg NOTES,
+       fill in with vaddr of "__start_notes" symbol if found.
+       (check_notes): New function.
+       (check_kernel_notes): New function.
+       (dwfl_linux_kernel_report_kernel): Call it.
+       (check_module_notes): New function.
+       (dwfl_linux_kernel_report_modules): Call it.
+
+       * linux-kernel-modules.c (dwfl_linux_kernel_find_elf):
+       Try dwfl_build_id_find_elf first.
+
+       * linux-kernel-modules.c (report_kernel): Don't leak FD if !REPORT.
+       Set kernel module e_type to ET_DYN.
+
+2007-10-03  Roland McGrath  <roland@redhat.com>
+
+       * find-debuginfo.c (validate): New function, broken out of ...
+       (find_debuginfo_in_path): ... here.  New function, broken out of ...
+       (dwfl_standard_find_debuginfo): ... here.  Call it, after trying
+       dwfl_build_id_find_debuginfo first.
+
+       * dwfl_build_id_find_elf.c: New file.
+       * dwfl_build_id_find_debuginfo.c: New file.
+       * Makefile.am (libdwfl_a_SOURCES): Add them.
+       * libdwfl.h: Declare them.
+       * libdwflP.h: Add INTDECLs.
+
+       * dwfl_module_build_id.c: New file.
+       * dwfl_module_report_build_id.c: New file.
+       * Makefile.am (libdwfl_a_SOURCES): Add them.
+       * libdwfl.h: Declare them.
+       * libdwflP.h (struct Dwfl_Module): New members build_id_bits,
+       build_id_len, build_id_vaddr.  Declare __libdwfl_find_build_id.
+       * dwfl_module.c (__libdwfl_module_free): Free MOD->build_id_bits.
+
+       * dwfl_module_getdwarf.c (find_offsets): New function.
+       (find_dynsym): New function, calls that.
+       (find_symtab): Call it.
+
 2007-09-11  Roland McGrath  <roland@redhat.com>
 
        * dwfl_module_addrsym.c: Prefer a later global symbol at the same
index e5cbb97..83834cd 100644 (file)
@@ -50,11 +50,14 @@ euinclude_HEADERS = libdwfl.h
 
 libdwfl_a_SOURCES = dwfl_begin.c dwfl_end.c dwfl_error.c dwfl_version.c \
                    dwfl_module.c dwfl_report_elf.c relocate.c \
+                   dwfl_module_build_id.c dwfl_module_report_build_id.c \
                    derelocate.c offline.c \
                    dwfl_module_info.c  dwfl_getmodules.c \
                    dwfl_module_getdwarf.c dwfl_getdwarf.c \
                    dwfl_validate_address.c \
                    argp-std.c find-debuginfo.c \
+                   dwfl_build_id_find_elf.c \
+                   dwfl_build_id_find_debuginfo.c \
                    linux-kernel-modules.c linux-proc-maps.c \
                    dwfl_addrmodule.c dwfl_addrdwarf.c \
                    cu.c dwfl_module_nextcu.c dwfl_nextcu.c dwfl_cumodule.c \
similarity index 67%
rename from libelf/gelf_rawchunk.c
rename to libdwfl/dwfl_build_id_find_debuginfo.c
index ced6f9e..97def07 100644 (file)
@@ -1,7 +1,6 @@
-/* Retrieve uninterpreted chunk of the file contents.
-   Copyright (C) 2002, 2005, 2007 Red Hat, Inc.
+/* Find the debuginfo file for a module from its build ID.
+   Copyright (C) 2007 Red Hat, Inc.
    This file is part of Red Hat elfutils.
-   Contributed by Ulrich Drepper <drepper@redhat.com>, 2002.
 
    Red Hat elfutils is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by the
    Network licensing program, please visit www.openinventionnetwork.com
    <http://www.openinventionnetwork.com>.  */
 
-#ifdef HAVE_CONFIG_H
-# include <config.h>
-#endif
-
-#include <errno.h>
-#include <libelf.h>
-#include <stddef.h>
-#include <stdlib.h>
+#include "libdwflP.h"
 #include <unistd.h>
 
-#include <system.h>
-#include "libelfP.h"
-
 
-char *
-gelf_rawchunk (elf, offset, size)
-     Elf *elf;
-     GElf_Off offset;
-     GElf_Word size;
+int
+dwfl_build_id_find_debuginfo (Dwfl_Module *mod,
+                             void **userdata __attribute__ ((unused)),
+                             const char *modname __attribute__ ((unused)),
+                             Dwarf_Addr base __attribute__ ((unused)),
+                             const char *file __attribute__ ((unused)),
+                             const char *debuglink __attribute__ ((unused)),
+                             GElf_Word crc __attribute__ ((unused)),
+                             char **debuginfo_file_name)
 {
-  if (elf == NULL)
-    {
-      /* No valid descriptor.  */
-      __libelf_seterrno (ELF_E_INVALID_HANDLE);
-      return NULL;
-    }
-
-  if (unlikely (offset >= elf->maximum_size
-               || offset + size >= elf->maximum_size
-               || offset + size < offset))
+  int fd = -1;
+  const unsigned char *bits;
+  GElf_Addr vaddr;
+  if (INTUSE(dwfl_module_build_id) (mod, &bits, &vaddr) > 0)
+    fd = __libdwfl_open_by_build_id (mod, true, debuginfo_file_name);
+  if (fd >= 0)
     {
-      /* Invalid request.  */
-      __libelf_seterrno (ELF_E_INVALID_OP);
-      return NULL;
+      /* We need to open an Elf handle on the file so we can check its
+        build ID note for validation.  Backdoor the handle into the
+        module data structure since we had to open it early anyway.  */
+      mod->debug.elf = elf_begin (fd, ELF_C_READ_MMAP_PRIVATE, NULL);
+      if (likely (__libdwfl_find_build_id (mod, false, mod->debug.elf) == 2))
+       /* Also backdoor the gratuitous flag.  */
+       mod->debug.valid = true;
+      else
+       {
+         /* A mismatch!  */
+         elf_end (mod->debug.elf);
+         mod->debug.elf = NULL;
+         close (fd);
+         fd = -1;
+         free (*debuginfo_file_name);
+         *debuginfo_file_name = NULL;
+         errno = 0;
+       }
     }
-
-  /* If the file is mmap'ed return an appropriate pointer.  */
-  if (elf->map_address != NULL)
-    return (char *) elf->map_address + elf->start_offset + offset;
-
-  /* We allocate the memory and read the data from the file.  */
-  char *result = (char *) malloc (size);
-  if (result == NULL)
-    __libelf_seterrno (ELF_E_NOMEM);
-  else
-    /* Read the file content.  */
-    if (unlikely ((size_t) pread_retry (elf->fildes, result, size,
-                                       elf->start_offset + offset)
-                 != size))
-      {
-       /* Something went wrong.  */
-       __libelf_seterrno (ELF_E_READ_ERROR);
-       free (result);
-       result = NULL;
-      }
-
-  return result;
+  return fd;
 }
+INTDEF (dwfl_build_id_find_debuginfo)
diff --git a/libdwfl/dwfl_build_id_find_elf.c b/libdwfl/dwfl_build_id_find_elf.c
new file mode 100644 (file)
index 0000000..c621501
--- /dev/null
@@ -0,0 +1,152 @@
+/* Find an ELF file for a module from its build ID.
+   Copyright (C) 2007 Red Hat, Inc.
+   This file is part of Red Hat elfutils.
+
+   Red Hat elfutils 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; version 2 of the License.
+
+   Red Hat 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 Red Hat elfutils; if not, write to the Free Software Foundation,
+   Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
+
+   In addition, as a special exception, Red Hat, Inc. gives You the
+   additional right to link the code of Red Hat elfutils with code licensed
+   under any Open Source Initiative certified open source license
+   (http://www.opensource.org/licenses/index.php) which requires the
+   distribution of source code with any binary distribution and to
+   distribute linked combinations of the two.  Non-GPL Code permitted under
+   this exception must only link to the code of Red Hat elfutils through
+   those well defined interfaces identified in the file named EXCEPTION
+   found in the source code files (the "Approved Interfaces").  The files
+   of Non-GPL Code may instantiate templates or use macros or inline
+   functions from the Approved Interfaces without causing the resulting
+   work to be covered by the GNU General Public License.  Only Red Hat,
+   Inc. may make changes or additions to the list of Approved Interfaces.
+   Red Hat's grant of this exception is conditioned upon your not adding
+   any new exceptions.  If you wish to add a new Approved Interface or
+   exception, please contact Red Hat.  You must obey the GNU General Public
+   License in all respects for all of the Red Hat elfutils code and other
+   code used in conjunction with Red Hat elfutils except the Non-GPL Code
+   covered by this exception.  If you modify this file, you may extend this
+   exception to your version of the file, but you are not obligated to do
+   so.  If you do not wish to provide this exception without modification,
+   you must delete this exception statement from your version and license
+   this file solely under the GPL without exception.
+
+   Red Hat elfutils is an included package of the Open Invention Network.
+   An included package of the Open Invention Network is a package for which
+   Open Invention Network licensees cross-license their patents.  No patent
+   license is granted, either expressly or impliedly, by designation as an
+   included package.  Should you wish to participate in the Open Invention
+   Network licensing program, please visit www.openinventionnetwork.com
+   <http://www.openinventionnetwork.com>.  */
+
+#include "libdwflP.h"
+#include <inttypes.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+
+int
+internal_function
+__libdwfl_open_by_build_id (Dwfl_Module *mod, bool debug, char **file_name)
+{
+  *file_name = NULL;
+  errno = 0;
+  if (mod->build_id_len <= 0)
+    return -1;
+
+  const size_t id_len = mod->build_id_len;
+  const uint8_t *id = mod->build_id_bits;
+
+  /* Search debuginfo_path directories' .build-id/ subdirectories.  */
+
+  char id_name[sizeof "/.build-id/" + 1 + id_len * 2 + sizeof ".debug" - 1];
+  strcpy (id_name, "/.build-id/");
+  int n = snprintf (&id_name[sizeof "/.build-id/" - 1],
+                   4, "%02" PRIx8 "/", (uint8_t) id[0]);
+  assert (n == 3);
+  for (size_t i = 1; i < id_len; ++i)
+    {
+      n = snprintf (&id_name[sizeof "/.build-id/" - 1 + 3 + (i - 1) * 2],
+                   3, "%02" PRIx8, (uint8_t) id[i]);
+      assert (n == 2);
+    }
+  if (debug)
+    strcpy (&id_name[sizeof "/.build-id/" - 1 + 3 + (id_len - 1) * 2],
+           ".debug");
+
+  const Dwfl_Callbacks *const cb = mod->dwfl->callbacks;
+  char *path = strdupa ((cb->debuginfo_path ? *cb->debuginfo_path : NULL)
+                       ?: DEFAULT_DEBUGINFO_PATH);
+
+  int fd = -1;
+  char *dir;
+  while (fd < 0 && (dir = strsep (&path, ":")) != NULL)
+    {
+      if (dir[0] == '+' || dir[0] == '-')
+       ++dir;
+
+      /* Only absolute directory names are useful to us.  */
+      if (dir[0] != '/')
+       continue;
+
+      size_t dirlen = strlen (dir);
+      char *name = malloc (dirlen + sizeof id_name);
+      if (unlikely (name == NULL))
+       break;
+      memcpy (mempcpy (name, dir, dirlen), id_name, sizeof id_name);
+
+      fd = TEMP_FAILURE_RETRY (open64 (name, O_RDONLY));
+      if (fd >= 0)
+       {
+         if (*file_name != NULL)
+           free (*file_name);
+         *file_name = canonicalize_file_name (name);
+         if (*file_name == NULL)
+           {
+             *file_name = name;
+             name = NULL;
+           }
+       }
+      free (name);
+    }
+
+  return fd;
+}
+
+int
+dwfl_build_id_find_elf (Dwfl_Module *mod,
+                       void **userdata __attribute__ ((unused)),
+                       const char *modname __attribute__ ((unused)),
+                       Dwarf_Addr base __attribute__ ((unused)),
+                       char **file_name, Elf **elfp)
+{
+  *elfp = NULL;
+  int fd = __libdwfl_open_by_build_id (mod, false, file_name);
+  if (fd >= 0)
+    {
+      *elfp = elf_begin (fd, ELF_C_READ_MMAP_PRIVATE, NULL);
+      if (__libdwfl_find_build_id (mod, false, *elfp) == 2)
+       /* This is a backdoor signal to short-circuit the ID refresh.  */
+       mod->main.valid = true;
+      else
+       {
+         /* This file does not contain the ID it should!  */
+         elf_end (*elfp);
+         *elfp = NULL;
+         close (fd);
+         fd = -1;
+         free (*file_name);
+         *file_name = NULL;
+       }
+    }
+  return fd;
+}
+INTDEF (dwfl_build_id_find_elf)
index b84a0a8..4cee37c 100644 (file)
@@ -103,6 +103,9 @@ __libdwfl_module_free (Dwfl_Module *mod)
     free_file (&mod->debug);
   free_file (&mod->main);
 
+  if (mod->build_id_bits != NULL)
+    free (mod->build_id_bits);
+
   free (mod->name);
   free (mod);
 }
diff --git a/libdwfl/dwfl_module_build_id.c b/libdwfl/dwfl_module_build_id.c
new file mode 100644 (file)
index 0000000..f9a6357
--- /dev/null
@@ -0,0 +1,164 @@
+/* Return build ID information for a module.
+   Copyright (C) 2007 Red Hat, Inc.
+   This file is part of Red Hat elfutils.
+
+   Red Hat elfutils 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; version 2 of the License.
+
+   Red Hat 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 Red Hat elfutils; if not, write to the Free Software Foundation,
+   Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
+
+   In addition, as a special exception, Red Hat, Inc. gives You the
+   additional right to link the code of Red Hat elfutils with code licensed
+   under any Open Source Initiative certified open source license
+   (http://www.opensource.org/licenses/index.php) which requires the
+   distribution of source code with any binary distribution and to
+   distribute linked combinations of the two.  Non-GPL Code permitted under
+   this exception must only link to the code of Red Hat elfutils through
+   those well defined interfaces identified in the file named EXCEPTION
+   found in the source code files (the "Approved Interfaces").  The files
+   of Non-GPL Code may instantiate templates or use macros or inline
+   functions from the Approved Interfaces without causing the resulting
+   work to be covered by the GNU General Public License.  Only Red Hat,
+   Inc. may make changes or additions to the list of Approved Interfaces.
+   Red Hat's grant of this exception is conditioned upon your not adding
+   any new exceptions.  If you wish to add a new Approved Interface or
+   exception, please contact Red Hat.  You must obey the GNU General Public
+   License in all respects for all of the Red Hat elfutils code and other
+   code used in conjunction with Red Hat elfutils except the Non-GPL Code
+   covered by this exception.  If you modify this file, you may extend this
+   exception to your version of the file, but you are not obligated to do
+   so.  If you do not wish to provide this exception without modification,
+   you must delete this exception statement from your version and license
+   this file solely under the GPL without exception.
+
+   Red Hat elfutils is an included package of the Open Invention Network.
+   An included package of the Open Invention Network is a package for which
+   Open Invention Network licensees cross-license their patents.  No patent
+   license is granted, either expressly or impliedly, by designation as an
+   included package.  Should you wish to participate in the Open Invention
+   Network licensing program, please visit www.openinventionnetwork.com
+   <http://www.openinventionnetwork.com>.  */
+
+#include "libdwflP.h"
+
+static int
+found_build_id (Dwfl_Module *mod, bool set,
+               const void *bits, int len, GElf_Addr vaddr)
+{
+  if (!set)
+    /* When checking bits, we do not compare VADDR because the
+       address found in a debuginfo file may not match the main
+       file as modified by prelink.  */
+    return 1 + (mod->build_id_len == len
+               && !memcmp (bits, mod->build_id_bits, len));
+
+  void *copy = malloc (len);
+  if (unlikely (copy == NULL))
+    {
+      __libdwfl_seterrno (DWFL_E_NOMEM);
+      return -1;
+    }
+
+  mod->build_id_bits = memcpy (copy, bits, len);
+  mod->build_id_vaddr = vaddr;
+  mod->build_id_len = len;
+  return len;
+}
+
+static int
+check_notes (Dwfl_Module *mod, bool set, Elf_Data *data, GElf_Addr data_vaddr)
+{
+  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"))
+      return found_build_id (mod, set,
+                            data->d_buf + desc_pos, nhdr.n_descsz,
+                            data_vaddr == 0 ? 0 : data_vaddr + pos);
+  return 0;
+}
+
+int
+internal_function
+__libdwfl_find_build_id (Dwfl_Module *mod, bool set, Elf *elf)
+{
+  int result = 0;
+
+  Elf_Scn *scn = elf_nextscn (elf, NULL);
+
+  if (scn == NULL)
+    {
+      /* No sections, have to look for phdrs.  */
+      GElf_Ehdr ehdr_mem;
+      GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem);
+      if (unlikely (ehdr == NULL))
+       {
+         __libdwfl_seterrno (DWFL_E_LIBELF);
+         return -1;
+       }
+      for (uint_fast16_t i = 0; result == 0 && i < ehdr_mem.e_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 (mod, set,
+                                 elf_getdata_rawchunk (elf,
+                                                       phdr->p_offset,
+                                                       phdr->p_filesz,
+                                                       ELF_T_NHDR),
+                                 phdr->p_vaddr + mod->main.bias);
+       }
+    }
+  else
+    do
+      {
+       GElf_Shdr shdr_mem;
+       GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+       if (likely (shdr != NULL) && shdr->sh_type == SHT_NOTE)
+         result = check_notes (mod, set, elf_getdata (scn, NULL),
+                               (shdr->sh_flags & SHF_ALLOC)
+                               ? shdr->sh_addr : 0);
+      }
+    while (result == 0 && (scn = elf_nextscn (elf, scn)) != NULL);
+
+  return result;
+}
+
+int
+dwfl_module_build_id (Dwfl_Module *mod,
+                     const unsigned char **bits, GElf_Addr *vaddr)
+{
+  if (mod == NULL)
+    return -1;
+
+  if (mod->build_id_len == 0 && mod->main.elf != NULL)
+    {
+      /* We have the file, but have not examined it yet.  */
+      int result = __libdwfl_find_build_id (mod, true, mod->main.elf);
+      if (result <= 0)
+       {
+         mod->build_id_len = -1;       /* Cache negative result.  */
+         return result;
+       }
+    }
+
+  if (mod->build_id_len <= 0)
+    return 0;
+
+  *bits = mod->build_id_bits;
+  *vaddr = mod->build_id_vaddr;
+  return mod->build_id_len;
+}
+INTDEF (dwfl_module_build_id)
index c0aeadd..90c77c8 100644 (file)
@@ -113,6 +113,15 @@ find_file (Dwfl_Module *mod)
                                                    &mod->main.name,
                                                    &mod->main.elf);
   mod->elferr = open_elf (mod, &mod->main);
+
+  if (mod->elferr == DWFL_E_NOERROR && !mod->main.valid)
+    {
+      /* Clear any explicitly reported build ID, just in case it was wrong.
+        We'll fetch it from the file when asked.  */
+      if (mod->build_id_len > 0)
+       free (mod->build_id_bits);
+      mod->build_id_len = 0;
+    }
 }
 
 /* Search an ELF file for a ".gnu_debuglink" section.  */
@@ -237,6 +246,214 @@ load_symtab (struct dwfl_file *file, struct dwfl_file **symfile,
   return DWFL_E_NO_SYMTAB;
 }
 
+
+/* Translate addresses into file offsets.
+   OFFS[*] start out zero and remain zero if unresolved.  */
+static void
+find_offsets (Elf *elf, const GElf_Ehdr *ehdr, size_t n,
+             GElf_Addr addrs[n], GElf_Off offs[n])
+{
+  size_t unsolved = n;
+  for (uint_fast16_t i = 0; i < ehdr->e_phnum; ++i)
+    {
+      GElf_Phdr phdr_mem;
+      GElf_Phdr *phdr = gelf_getphdr (elf, i, &phdr_mem);
+      if (phdr != NULL && phdr->p_type == PT_LOAD && phdr->p_memsz > 0)
+       for (size_t j = 0; j < n; ++j)
+         if (offs[j] == 0
+             && addrs[j] >= phdr->p_vaddr
+             && addrs[j] - phdr->p_vaddr < phdr->p_filesz)
+           {
+             offs[j] = addrs[j] - phdr->p_vaddr + phdr->p_offset;
+             if (--unsolved == 0)
+               break;
+           }
+    }
+}
+
+/* Try to find a dynamic symbol table via phdrs.  */
+static void
+find_dynsym (Dwfl_Module *mod)
+{
+  GElf_Ehdr ehdr_mem;
+  GElf_Ehdr *ehdr = gelf_getehdr (mod->main.elf, &ehdr_mem);
+
+  for (uint_fast16_t i = 0; i < ehdr->e_phnum; ++i)
+    {
+      GElf_Phdr phdr_mem;
+      GElf_Phdr *phdr = gelf_getphdr (mod->main.elf, i, &phdr_mem);
+      if (phdr == NULL)
+       break;
+
+      if (phdr->p_type == PT_DYNAMIC)
+       {
+         /* Examine the dynamic section for the pointers we need.  */
+
+         Elf_Data *data = elf_getdata_rawchunk (mod->main.elf,
+                                                phdr->p_offset, phdr->p_filesz,
+                                                ELF_T_DYN);
+         if (data == NULL)
+           continue;
+
+         enum
+           {
+             i_symtab,
+             i_strtab,
+             i_hash,
+             i_gnu_hash,
+             i_max
+           };
+         GElf_Addr addrs[i_max] = { 0, };
+         GElf_Xword strsz = 0;
+         size_t n = data->d_size / gelf_fsize (mod->main.elf,
+                                               ELF_T_DYN, 1, EV_CURRENT);
+         for (size_t j = 0; j < n; ++j)
+           {
+             GElf_Dyn dyn_mem;
+             GElf_Dyn *dyn = gelf_getdyn (data, j, &dyn_mem);
+             if (dyn != NULL)
+               switch (dyn->d_tag)
+                 {
+                 case DT_SYMTAB:
+                   addrs[i_symtab] = dyn->d_un.d_ptr;
+                   continue;
+
+                 case DT_HASH:
+                   addrs[i_hash] = dyn->d_un.d_ptr;
+                   continue;
+
+                 case DT_GNU_HASH:
+                   addrs[i_gnu_hash] = dyn->d_un.d_ptr;
+                   continue;
+
+                 case DT_STRTAB:
+                   addrs[i_strtab] = dyn->d_un.d_ptr;
+                   continue;
+
+                 case DT_STRSZ:
+                   strsz = dyn->d_un.d_val;
+                   continue;
+
+                 default:
+                   continue;
+
+                 case DT_NULL:
+                   break;
+                 }
+             break;
+           }
+
+         /* Translate pointers into file offsets.  */
+         GElf_Off offs[i_max] = { 0, };
+         find_offsets (mod->main.elf, ehdr, i_max, addrs, offs);
+
+         /* Figure out the size of the symbol table.  */
+         if (offs[i_hash] != 0)
+           {
+             /* In the original format, .hash says the size of .dynsym.  */
+
+             size_t entsz = SH_ENTSIZE_HASH (ehdr);
+             data = elf_getdata_rawchunk (mod->main.elf,
+                                          offs[i_hash] + entsz, entsz,
+                                          entsz == 4 ? ELF_T_WORD
+                                          : ELF_T_XWORD);
+             if (data != NULL)
+               mod->syments = (entsz == 4
+                               ? *(const GElf_Word *) data->d_buf
+                               : *(const GElf_Xword *) data->d_buf);
+           }
+         if (offs[i_gnu_hash] != 0 && mod->syments == 0)
+           {
+             /* In the new format, we can derive it with some work.  */
+
+             const struct
+             {
+               Elf32_Word nbuckets;
+               Elf32_Word symndx;
+               Elf32_Word maskwords;
+               Elf32_Word shift2;
+             } *header;
+
+             data = elf_getdata_rawchunk (mod->main.elf, offs[i_gnu_hash],
+                                          sizeof *header, ELF_T_WORD);
+             if (data != NULL)
+               {
+                 header = data->d_buf;
+                 Elf32_Word nbuckets = header->nbuckets;
+                 Elf32_Word symndx = header->symndx;
+                 GElf_Off buckets_at = (offs[i_gnu_hash] + sizeof *header
+                                        + (gelf_getclass (mod->main.elf)
+                                           * sizeof (Elf32_Word)
+                                           * header->maskwords));
+
+                 data = elf_getdata_rawchunk (mod->main.elf, buckets_at,
+                                              nbuckets * sizeof (Elf32_Word),
+                                              ELF_T_WORD);
+                 if (data != NULL && symndx < nbuckets)
+                   {
+                     const Elf32_Word *const buckets = data->d_buf;
+                     Elf32_Word maxndx = symndx;
+                     for (Elf32_Word bucket = 0; bucket < nbuckets; ++bucket)
+                       if (buckets[bucket] > maxndx)
+                         maxndx = buckets[bucket];
+
+                     GElf_Off hasharr_at = (buckets_at
+                                            + nbuckets * sizeof (Elf32_Word));
+                     hasharr_at += (maxndx - symndx) * sizeof (Elf32_Word);
+                     do
+                       {
+                         data = elf_getdata_rawchunk (mod->main.elf,
+                                                      hasharr_at,
+                                                      sizeof (Elf32_Word),
+                                                      ELF_T_WORD);
+                         if (data != NULL
+                             && (*(const Elf32_Word *) data->d_buf & 1u))
+                           {
+                             mod->syments = maxndx + 1;
+                             break;
+                           }
+                         ++maxndx;
+                         hasharr_at += sizeof (Elf32_Word);
+                       } while (data != NULL);
+                   }
+               }
+           }
+         if (offs[i_strtab] > offs[i_symtab] && mod->syments == 0)
+           mod->syments = ((offs[i_strtab] - offs[i_symtab])
+                           / gelf_fsize (mod->main.elf,
+                                         ELF_T_SYM, 1, EV_CURRENT));
+
+         if (mod->syments > 0)
+           {
+             mod->symdata = elf_getdata_rawchunk (mod->main.elf,
+                                                  offs[i_symtab],
+                                                  gelf_fsize (mod->main.elf,
+                                                              ELF_T_SYM,
+                                                              mod->syments,
+                                                              EV_CURRENT),
+                                                  ELF_T_SYM);
+             if (mod->symdata != NULL)
+               {
+                 mod->symstrdata = elf_getdata_rawchunk (mod->main.elf,
+                                                         offs[i_strtab],
+                                                         strsz,
+                                                         ELF_T_BYTE);
+                 if (mod->symstrdata == NULL)
+                   mod->symdata = NULL;
+               }
+             if (mod->symdata == NULL)
+               mod->symerr = DWFL_E (LIBELF, elf_errno ());
+             else
+               {
+                 mod->symfile = &mod->main;
+                 mod->symerr = DWFL_E_NOERROR;
+               }
+             return;
+           }
+       }
+    }
+}
+
 /* Try to find a symbol table in either MOD->main.elf or MOD->debug.elf.  */
 static void
 find_symtab (Dwfl_Module *mod)
@@ -290,11 +507,16 @@ find_symtab (Dwfl_Module *mod)
          break;
 
        case DWFL_E_NO_SYMTAB:
-         if (symscn == NULL)
-           return;
-         /* We still have the dynamic symbol table.  */
-         mod->symerr = DWFL_E_NOERROR;
-         break;
+         if (symscn != NULL)
+           {
+             /* We still have the dynamic symbol table.  */
+             mod->symerr = DWFL_E_NOERROR;
+             break;
+           }
+
+         /* Last ditch, look for dynamic symbols without section headers.  */
+         find_dynsym (mod);
+         return;
        }
       break;
     }
similarity index 69%
rename from libelf/gelf_freechunk.c
rename to libdwfl/dwfl_module_report_build_id.c
index 7b293ab..4886931 100644 (file)
@@ -1,7 +1,6 @@
-/* Release uninterpreted chunk of the file contents.
-   Copyright (C) 2002 Red Hat, Inc.
+/* Report build ID information for a module.
+   Copyright (C) 2007 Red Hat, Inc.
    This file is part of Red Hat elfutils.
-   Contributed by Ulrich Drepper <drepper@redhat.com>, 2002.
 
    Red Hat elfutils is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by the
    Network licensing program, please visit www.openinventionnetwork.com
    <http://www.openinventionnetwork.com>.  */
 
-#ifdef HAVE_CONFIG_H
-# include <config.h>
-#endif
+#include "libdwflP.h"
 
-#include <libelf.h>
-#include <stddef.h>
-#include <stdlib.h>
+// XXX vs report changed module: punting old file
+int
+dwfl_module_report_build_id (Dwfl_Module *mod,
+                            const unsigned char *bits, size_t len,
+                            GElf_Addr vaddr)
+{
+  if (mod == NULL)
+    return -1;
 
-#include "libelfP.h"
+  if (mod->main.elf != NULL)
+    {
+      /* Once we know about a file, we won't take any lies about
+        its contents.  The only permissible call is a no-op.  */
 
+      if ((size_t) mod->build_id_len == len
+         && (mod->build_id_vaddr == vaddr || vaddr == 0)
+         && !memcmp (bits, mod->build_id_bits, len))
+       return 0;
 
-void
-gelf_freechunk (elf, ptr)
-     Elf *elf;
-     char *ptr;
-{
-  if (elf == NULL)
-    /* No valid descriptor.  */
-    return;
+      __libdwfl_seterrno (DWFL_E_ALREADY_ELF);
+      return -1;
+    }
+
+  if (vaddr != 0 && (vaddr < mod->low_addr || vaddr + len > mod->high_addr))
+    {
+      __libdwfl_seterrno (DWFL_E_ADDR_OUTOFRANGE);
+      return -1;
+    }
+
+  void *copy = NULL;
+  if (len > 0)
+    {
+      copy = malloc (len);
+      if (unlikely (copy == NULL))
+       {
+         __libdwfl_seterrno (DWFL_E_NOMEM);
+         return -1;
+       }
+      memcpy (copy, bits, len);
+    }
+
+  if (mod->build_id_len > 0)
+    free (mod->build_id_bits);
+
+  mod->build_id_bits = copy;
+  mod->build_id_len = len;
+  mod->build_id_vaddr = vaddr;
 
-  /* We do not have to do anything if the pointer returned by
-     gelf_rawchunk points into the memory allocated for the ELF
-     descriptor.  */
-  if (ptr < (char *) elf->map_address + elf->start_offset
-      || ptr >= ((char *) elf->map_address + elf->start_offset
-                + elf->maximum_size))
-    free (ptr);
+  return 0;
 }
+INTDEF (dwfl_module_report_build_id)
index ca1fadc..f1ff3a4 100644 (file)
@@ -90,15 +90,37 @@ check_crc (int fd, GElf_Word debuglink_crc)
          && file_crc == debuglink_crc);
 }
 
-int
-dwfl_standard_find_debuginfo (Dwfl_Module *mod,
-                             void **userdata __attribute__ ((unused)),
-                             const char *modname __attribute__ ((unused)),
-                             GElf_Addr base __attribute__ ((unused)),
-                             const char *file_name,
-                             const char *debuglink_file,
-                             GElf_Word debuglink_crc,
-                             char **debuginfo_file_name)
+static bool
+validate (Dwfl_Module *mod, int fd, bool check, GElf_Word debuglink_crc)
+{
+  /* If we have a build ID, check only that.  */
+  if (mod->build_id_len > 0)
+    {
+      /* We need to open an Elf handle on the file so we can check its
+        build ID note for validation.  Backdoor the handle into the
+        module data structure since we had to open it early anyway.  */
+      mod->debug.elf = elf_begin (fd, ELF_C_READ_MMAP_PRIVATE, NULL);
+      if (likely (__libdwfl_find_build_id (mod, false, mod->debug.elf) == 2))
+       /* Also backdoor the gratuitous flag.  */
+       mod->debug.valid = true;
+      else
+       {
+         /* A mismatch!  */
+         elf_end (mod->debug.elf);
+         mod->debug.elf = NULL;
+         mod->debug.valid = false;
+       }
+
+      return mod->debug.valid;
+    }
+
+  return !check || check_crc (fd, debuglink_crc);
+}
+
+static int
+find_debuginfo_in_path (Dwfl_Module *mod, const char *file_name,
+                       const char *debuglink_file, GElf_Word debuglink_crc,
+                       char **debuginfo_file_name)
 {
   bool cancheck = debuglink_crc != (GElf_Word) 0;
 
@@ -181,7 +203,7 @@ dwfl_standard_find_debuginfo (Dwfl_Module *mod,
          default:
            return -1;
          }
-      if (!check || check_crc (fd, debuglink_crc))
+      if (validate (mod, fd, check, debuglink_crc))
        {
          *debuginfo_file_name = fname;
          return fd;
@@ -194,4 +216,33 @@ dwfl_standard_find_debuginfo (Dwfl_Module *mod,
   errno = 0;
   return -1;
 }
+
+int
+dwfl_standard_find_debuginfo (Dwfl_Module *mod,
+                             void **userdata __attribute__ ((unused)),
+                             const char *modname __attribute__ ((unused)),
+                             GElf_Addr base __attribute__ ((unused)),
+                             const char *file_name,
+                             const char *debuglink_file,
+                             GElf_Word debuglink_crc,
+                             char **debuginfo_file_name)
+{
+  /* First try by build ID if we have one.  If that succeeds or fails
+     other than just by finding nothing, that's all we do.  */
+  const unsigned char *bits;
+  GElf_Addr vaddr;
+  if (INTUSE(dwfl_module_build_id) (mod, &bits, &vaddr) > 0)
+    {
+      int fd = INTUSE(dwfl_build_id_find_debuginfo) (mod,
+                                                    NULL, NULL, 0,
+                                                    NULL, NULL, 0,
+                                                    debuginfo_file_name);
+      if (fd >= 0 || errno != 0)
+       return fd;
+    }
+
+  /* Failing that, search the path by name.  */
+  return find_debuginfo_in_path (mod, file_name, debuglink_file, debuglink_crc,
+                                debuginfo_file_name);
+}
 INTDEF (dwfl_standard_find_debuginfo)
index ee1b505..5438ee2 100644 (file)
@@ -194,27 +194,82 @@ extern ptrdiff_t dwfl_getmodules (Dwfl *dwfl,
 extern Dwfl_Module *dwfl_addrmodule (Dwfl *dwfl, Dwarf_Addr address);
 
 
+/* Report the known build ID bits associated with a module.
+   If VADDR is nonzero, it gives the absolute address where those
+   bits are found within the module.  This can be called at any
+   time, but is usually used immediately after dwfl_report_module.
+   Once the module's main ELF file is opened, the ID note found
+   there takes precedence and cannot be changed.  */
+extern int dwfl_module_report_build_id (Dwfl_Module *mod,
+                                       const unsigned char *bits, size_t len,
+                                       GElf_Addr vaddr)
+  __nonnull_attribute__ (2);
+
+/* Extract the build ID bits associated with a module.
+   Returns -1 for errors, 0 if no ID is known, or the number of ID bytes.
+   When an ID is found, *BITS points to it; *VADDR is the absolute address
+   at which the ID bits are found within the module, or 0 if unknown.
+
+   This returns 0 when the module's main ELF file has not yet been loaded
+   and its build ID bits were not reported.  To ensure the ID is always
+   returned when determinable, call dwfl_module_getelf first.  */
+extern int dwfl_module_build_id (Dwfl_Module *mod,
+                                const unsigned char **bits, GElf_Addr *vaddr)
+  __nonnull_attribute__ (2, 3);
+
+
 /*** Standard callbacks ***/
 
-/* Standard find_debuginfo callback function.
-   This is controlled by a string specifying directories to look in.
+/* These standard find_elf and find_debuginfo callbacks are
+   controlled by a string specifying directories to look in.
    If `debuginfo_path' is set in the Dwfl_Callbacks structure
-   and the char * it points to is not null, that supplies the string.
-   Otherwise a default path is used.
-
-   If the first character of the string is + or - that says to check or to
-   ignore (respectively) the CRC32 checksum from the .gnu_debuglink
-   section.  The default is to check it.  The remainder of the string is
-   composed of elements separated by colons.  Each element can start with +
-   or - to override the global checksum behavior.  If the remainder of the
-   element is empty, the directory containing the main file is tried; if
-   it's an absolute path name, the absolute directory path containing the
-   main file is taken as a subdirectory of this path; a relative path name
-   is taken as a subdirectory of the directory containing the main file.
-   Hence for /bin/ls, string ":.debug:/usr/lib/debug" says to look in /bin,
-   then /bin/.debug, then /usr/lib/debug/bin, for the file name in the
-   .gnu_debuglink section (or "ls.debug" if none was found).  */
+   and the char * it points to is not null, that supplies the
+   string.  Otherwise a default path is used.
+
+   If the first character of the string is + or - that enables or
+   disables CRC32 checksum validation when it's necessary.  The
+   remainder of the string is composed of elements separated by
+   colons.  Each element can start with + or - to override the
+   global checksum behavior.  This flag is never relevant when
+   working with build IDs, but it's always parsed in the path
+   string.  The remainder of the element indicates a directory.
+
+   Searches by build ID consult only the elements naming absolute
+   directory paths.  They look under those directories for a link
+   named ".build-id/xx/yy" or ".build-id/xx/yy.debug", where "xxyy"
+   is the lower-case hexadecimal representation of the ID bytes.
+
+   In searches for debuginfo by name, if the remainder of the
+   element is empty, the directory containing the main file is
+   tried; if it's an absolute path name, the absolute directory path
+   containing the main file is taken as a subdirectory of this path;
+   a relative path name is taken as a subdirectory of the directory
+   containing the main file.  Hence for /bin/ls, the default string
+   ":.debug:/usr/lib/debug" says to look in /bin, then /bin/.debug,
+   then /usr/lib/debug/bin, for the file name in the .gnu_debuglink
+   section (or "ls.debug" if none was found).  */
+
+/* Standard find_elf callback function working solely on build ID.
+   This can be tried first by any find_elf callback, to use the
+   bits passed to dwfl_module_report_build_id, if any.  */
+extern int dwfl_build_id_find_elf (Dwfl_Module *, void **,
+                                  const char *, Dwarf_Addr,
+                                  char **, Elf **);
+
+/* Standard find_debuginfo callback function working solely on build ID.
+   This can be tried first by any find_debuginfo callback,
+   to use the build ID bits from the main file when present.  */
+extern int dwfl_build_id_find_debuginfo (Dwfl_Module *, void **,
+                                        const char *, Dwarf_Addr,
+                                        const char *, const char *,
+                                        GElf_Word, char **);
 
+/* Standard find_debuginfo callback function.
+   If a build ID is available, this tries first to use that.
+   If there is no build ID or no valid debuginfo found by ID,
+   it searches the debuginfo path by name, as described above.
+   Any file found in the path is validated by build ID if possible,
+   or else by CRC32 checksum if enabled, and skipped if it does not match.  */
 extern int dwfl_standard_find_debuginfo (Dwfl_Module *, void **,
                                         const char *, Dwarf_Addr,
                                         const char *, const char *,
index 6de87ab..4b458e1 100644 (file)
@@ -88,6 +88,7 @@
   DWFL_ERROR (ADDR_OUTOFRANGE, N_("address out of range"))                   \
   DWFL_ERROR (NO_MATCH, N_("no matching address range"))                     \
   DWFL_ERROR (TRUNCATED, N_("image truncated"))                                      \
+  DWFL_ERROR (ALREADY_ELF, N_("ELF file opened"))                            \
   DWFL_ERROR (BADELF, N_("not a valid ELF file"))                            \
   DWFL_ERROR (WEIRD_TYPE, N_("cannot handle DWARF type description"))
 
@@ -119,6 +120,7 @@ struct dwfl_file
 {
   char *name;
   int fd;
+  bool valid;                  /* The build ID note has been matched.  */
 
   Elf *elf;
   GElf_Addr bias;              /* Actual load address - p_vaddr.  */
@@ -134,6 +136,10 @@ struct Dwfl_Module
   char *name;                  /* Iterator name for this module.  */
   GElf_Addr low_addr, high_addr;
 
+  void *build_id_bits;         /* malloc'd copy of build ID bits.  */
+  GElf_Addr build_id_vaddr;    /* Address where they reside, 0 if unknown.  */
+  int build_id_len;            /* -1 for prior failure, 0 if unset.  */
+
   struct dwfl_file main, debug;
   Ebl *ebl;
   GElf_Half e_type;            /* GElf_Ehdr.e_type cache.  */
@@ -253,11 +259,21 @@ extern Dwfl_Error __libdwfl_addrcu (Dwfl_Module *mod, Dwarf_Addr addr,
 
 /* Ensure that CU->lines (and CU->cu->lines) is set up.  */
 extern Dwfl_Error __libdwfl_cu_getsrclines (struct dwfl_cu *cu)
-     internal_function;
+  internal_function;
+
+/* Look in ELF for an NT_GNU_BUILD_ID note.  If SET is true, store it
+   in MOD and return its length.  If SET is false, instead compare it
+   to that stored in MOD and return 2 if they match, 1 if they do not.
+   Returns -1 for errors, 0 if no note is found.  */
+extern int __libdwfl_find_build_id (Dwfl_Module *mod, bool set, Elf *elf)
+  internal_function;
 
+/* Open a main or debuginfo file by its build ID, returns the fd.  */
+extern int __libdwfl_open_by_build_id (Dwfl_Module *mod, bool debug,
+                                      char **file_name) internal_function;
 
 extern uint32_t __libdwfl_crc32 (uint32_t crc, unsigned char *buf, size_t len)
-     attribute_hidden;
+  attribute_hidden;
 extern int __libdwfl_crc32_file (int fd, uint32_t *resp) attribute_hidden;
 
 
@@ -270,17 +286,21 @@ INTDECL (dwfl_addrdwarf)
 INTDECL (dwfl_addrdie)
 INTDECL (dwfl_module_addrdie)
 INTDECL (dwfl_module_addrsym)
+INTDECL (dwfl_module_build_id)
 INTDECL (dwfl_module_getdwarf)
 INTDECL (dwfl_module_getelf)
 INTDECL (dwfl_module_getsym)
 INTDECL (dwfl_module_getsymtab)
 INTDECL (dwfl_module_getsrc)
+INTDECL (dwfl_module_report_build_id)
 INTDECL (dwfl_report_elf)
 INTDECL (dwfl_report_begin)
 INTDECL (dwfl_report_begin_add)
 INTDECL (dwfl_report_module)
 INTDECL (dwfl_report_offline)
 INTDECL (dwfl_report_end)
+INTDECL (dwfl_build_id_find_elf)
+INTDECL (dwfl_build_id_find_debuginfo)
 INTDECL (dwfl_standard_find_debuginfo)
 INTDECL (dwfl_linux_kernel_find_elf)
 INTDECL (dwfl_linux_kernel_module_section_address)
index 26f185f..6164ae7 100644 (file)
@@ -67,6 +67,8 @@
 
 #define MODULEDIRFMT   "/lib/modules/%s"
 
+#define KNOTESFILE     "/sys/kernel/notes"
+#define        MODNOTESFMT     "/sys/module/%s/notes"
 #define KSYMSFILE      "/proc/kallsyms"
 #define MODULELIST     "/proc/modules"
 #define        SECADDRDIRFMT   "/sys/module/%s/sections/"
@@ -175,13 +177,19 @@ report_kernel (Dwfl *dwfl, const char **release,
          report = want > 0;
        }
 
-      if (report
-         && INTUSE(dwfl_report_elf) (dwfl, KERNEL_MODNAME,
-                                     fname, fd, 0) == NULL)
+      if (report)
        {
-         close (fd);
-         result = -1;
+         Dwfl_Module *mod = INTUSE(dwfl_report_elf) (dwfl, KERNEL_MODNAME,
+                                                     fname, fd, 0);
+         if (mod == NULL)
+           result = -1;
+
+         /* The kernel is ET_EXEC, but always treat it as relocatable.  */
+         mod->e_type = ET_DYN;
        }
+
+      if (!report || result < 0)
+       close (fd);
     }
 
   free (fname);
@@ -297,7 +305,7 @@ INTDEF (dwfl_linux_kernel_report_offline)
 
 /* Grovel around to guess the bounds of the runtime kernel image.  */
 static int
-intuit_kernel_bounds (Dwarf_Addr *start, Dwarf_Addr *end)
+intuit_kernel_bounds (Dwarf_Addr *start, Dwarf_Addr *end, Dwarf_Addr *notes)
 {
   FILE *f = fopen (KSYMSFILE, "r");
   if (f == NULL)
@@ -305,6 +313,8 @@ intuit_kernel_bounds (Dwarf_Addr *start, Dwarf_Addr *end)
 
   (void) __fsetlocking (f, FSETLOCKING_BYCALLER);
 
+  *notes = 0;
+
   char *line = NULL;
   size_t linesz = 0;
   size_t n = getline (&line, &linesz, f);
@@ -323,6 +333,14 @@ intuit_kernel_bounds (Dwarf_Addr *start, Dwarf_Addr *end)
              result = -1;
              break;
            }
+
+         if (*notes == 0)
+           {
+             const char *sym = (strsep (&p, " \t\n")
+                                ? strsep (&p, " \t\n") : NULL);
+             if (sym != NULL && !strcmp (sym, "__start_notes"))
+               *notes = last;
+           }
        }
       if ((n == 0 && feof_unlocked (f)) || (n > 1 && line[n - 2] == ']'))
        {
@@ -345,15 +363,129 @@ intuit_kernel_bounds (Dwarf_Addr *start, Dwarf_Addr *end)
   return result;
 }
 
+
+/* Look for a build ID note in NOTESFILE and associate the ID with MOD.  */
+static int
+check_notes (Dwfl_Module *mod, const char *notesfile,
+            Dwarf_Addr vaddr, const char *secname)
+{
+  int fd = open64 (notesfile, O_RDONLY);
+  if (fd < 0)
+    return 1;
+
+  assert (sizeof (Elf32_Nhdr) == sizeof (GElf_Nhdr));
+  assert (sizeof (Elf64_Nhdr) == sizeof (GElf_Nhdr));
+  union
+  {
+    GElf_Nhdr nhdr;
+    unsigned char data[8192];
+  } buf;
+
+  ssize_t n = read (fd, buf.data, sizeof buf);
+  close (fd);
+
+  if (n <= 0)
+    return 1;
+
+  unsigned char *p = buf.data;
+  while (p < &buf.data[n])
+    {
+      /* No translation required since we are reading the native kernel.  */
+      GElf_Nhdr *nhdr = (void *) p;
+      p += sizeof *nhdr;
+      unsigned char *name = p;
+      p += (nhdr->n_namesz + 3) & -4U;
+      unsigned char *bits = p;
+      p += (nhdr->n_descsz + 3) & -4U;
+
+      if (p <= &buf.data[n]
+         && nhdr->n_type == NT_GNU_BUILD_ID
+         && nhdr->n_namesz == sizeof "GNU"
+         && !memcmp (name, "GNU", sizeof "GNU"))
+       {
+         /* Found it.  For a module we must figure out its VADDR now.  */
+
+         if (secname != NULL
+             && (INTUSE(dwfl_linux_kernel_module_section_address)
+                 (mod, NULL, mod->name, 0, secname, 0, NULL, &vaddr) != 0
+                 || vaddr == (GElf_Addr) -1l))
+           vaddr = 0;
+
+         if (vaddr != 0)
+           vaddr += bits - buf.data;
+         return INTUSE(dwfl_module_report_build_id) (mod, bits,
+                                                     nhdr->n_descsz, vaddr);
+       }
+    }
+
+  return 0;
+}
+
+/* Look for a build ID for the kernel.  */
+static int
+check_kernel_notes (Dwfl_Module *kernelmod, GElf_Addr vaddr)
+{
+  return check_notes (kernelmod, KNOTESFILE, vaddr, NULL) < 0 ? -1 : 0;
+}
+
+/* Look for a build ID for a loaded kernel module.  */
+static int
+check_module_notes (Dwfl_Module *mod)
+{
+  char *dirs[2] = { NULL, NULL };
+  if (asprintf (&dirs[0], MODNOTESFMT, mod->name) < 0)
+    return ENOMEM;
+
+  FTS *fts = fts_open (dirs, FTS_NOSTAT, NULL);
+  if (fts == NULL)
+    {
+      free (dirs[0]);
+      return 0;
+    }
+
+  int result = 0;
+  FTSENT *f;
+  while ((f = fts_read (fts)) != NULL)
+    {
+      switch (f->fts_info)
+       {
+       case FTS_F:
+       case FTS_NSOK:
+         result = check_notes (mod, f->fts_accpath, 0, f->fts_name);
+         if (result > 0)       /* Nothing found.  */
+           {
+             result = 0;
+             continue;
+           }
+         break;
+
+       case FTS_ERR:
+       case FTS_DNR:
+         result = f->fts_errno;
+         break;
+
+       case FTS_NS:
+       default:
+         continue;
+       }
+
+      /* We only get here when finished or in error cases.  */
+      break;
+    }
+  fts_close (fts);
+  free (dirs[0]);
+
+  return result;
+}
+
 int
 dwfl_linux_kernel_report_kernel (Dwfl *dwfl)
 {
   Dwarf_Addr start;
   Dwarf_Addr end;
-  inline int report (void)
+  inline Dwfl_Module *report (void)
     {
-      return INTUSE(dwfl_report_module) (dwfl, KERNEL_MODNAME,
-                                        start, end) == NULL ? -1 : 0;
+      return INTUSE(dwfl_report_module) (dwfl, KERNEL_MODNAME, start, end);
     }
 
   /* This is a bit of a kludge.  If we already reported the kernel,
@@ -363,14 +495,18 @@ dwfl_linux_kernel_report_kernel (Dwfl *dwfl)
       {
        start = m->low_addr;
        end = m->high_addr;
-       return report ();
+       return report () == NULL ? -1 : 0;
       }
 
   /* Try to figure out the bounds of the kernel image without
      looking for any vmlinux file.  */
-  int result = intuit_kernel_bounds (&start, &end);
+  Dwarf_Addr notes;
+  int result = intuit_kernel_bounds (&start, &end, &notes);
   if (result == 0)
-    return report ();
+    {
+      Dwfl_Module *mod = report ();
+      return unlikely (mod == NULL) ? -1 : check_kernel_notes (mod, notes);
+    }
   if (result != ENOENT)
     return result;
 
@@ -383,13 +519,20 @@ INTDEF (dwfl_linux_kernel_report_kernel)
 /* Dwfl_Callbacks.find_elf for the running Linux kernel and its modules.  */
 
 int
-dwfl_linux_kernel_find_elf (Dwfl_Module *mod __attribute__ ((unused)),
+dwfl_linux_kernel_find_elf (Dwfl_Module *mod,
                            void **userdata __attribute__ ((unused)),
                            const char *module_name,
                            Dwarf_Addr base __attribute__ ((unused)),
-                           char **file_name,
-                           Elf **elfp __attribute__ ((unused)))
+                           char **file_name, Elf **elfp)
 {
+  if (mod->build_id_len > 0)
+    {
+      int fd = INTUSE(dwfl_build_id_find_elf) (mod, NULL, NULL, 0,
+                                              file_name, elfp);
+      if (fd >= 0 || errno != 0)
+       return fd;
+    }
+
   const char *release = kernel_release ();
   if (release == NULL)
     return errno;
@@ -508,7 +651,7 @@ dwfl_linux_kernel_module_section_address
 {
   char *sysfile;
   if (asprintf (&sysfile, SECADDRDIRFMT "%s", modname, secname) < 0)
-    return ENOMEM;
+    return DWARF_CB_ABORT;
 
   FILE *f = fopen (sysfile, "r");
   free (sysfile);
@@ -559,7 +702,7 @@ dwfl_linux_kernel_module_section_address
              int len = asprintf (&sysfile, SECADDRDIRFMT "%s",
                                  modname, secname);
              if (len < 0)
-               return ENOMEM;
+               return DWARF_CB_ABORT;
              char *end = sysfile + len;
              do
                {
@@ -620,12 +763,17 @@ dwfl_linux_kernel_report_modules (Dwfl *dwfl)
   while (getline (&line, &linesz, f) > 0
         && sscanf (line, "%128s %lu %*s %*s %*s %" PRIx64 " %*s\n",
                    modname, &modsz, &modaddr) == 3)
-    if (INTUSE(dwfl_report_module) (dwfl, modname,
-                                   modaddr, modaddr + modsz) == NULL)
-      {
-       result = -1;
-       break;
-      }
+    {
+      Dwfl_Module *mod = INTUSE(dwfl_report_module) (dwfl, modname,
+                                                    modaddr, modaddr + modsz);
+      if (mod == NULL)
+       {
+         result = -1;
+         break;
+       }
+
+      result = check_module_notes (mod);
+    }
   free (line);
 
   if (result == 0)
index c879aed..1532a4c 100644 (file)
@@ -1,3 +1,27 @@
+2007-10-04  Roland McGrath  <roland@redhat.com>
+
+       * elf_end.c (elf_end): Don't free ELF->state.ar.ar_sym when it's -1l.
+
+2007-10-03  Roland McGrath  <roland@redhat.com>
+
+       * libelf.h (Elf_Data): Use off64_t for d_off.
+       (Elf_Arhdr): Use off64_t for ar_size.
+       (elf_update, elf_getbase, elf_getaroff): Return off64_t.
+
+       * gelf_rawchunk.c: File removed.
+       * gelf_freechunk.c: File removed.
+       * Makefile.am (libelf_a_SOURCES): Remove them.
+       * libelf.map (ELFUTILS_1.0): Remove exports.
+       * gelf.h: Remove decls.
+
+       * elf_getdata_rawchunk.c: New file.
+       * Makefile.am (libelf_a_SOURCES): Add it.
+       * libelf.map (ELFUTILS_1.3): Add elf_getdata_rawchunk.
+       * libelf.h: Declare it.
+       * libelfP.h (Elf_Data_Chunk): New type.
+       (struct Elf.elf): New member `rawchunks'.
+       * elf_end.c (elf_end): Free recorded rawchunk buffers.
+
 2007-08-24  Roland McGrath  <roland@redhat.com>
 
        * gelf_getnote.c: New file.
index 3e52bce..58c9b5a 100644 (file)
@@ -71,6 +71,7 @@ libelf_a_SOURCES = elf_version.c elf_hash.c elf_error.c elf_fill.c \
                   elf32_getshdr.c elf64_getshdr.c gelf_getshdr.c \
                   gelf_update_shdr.c \
                   elf_strptr.c elf_rawdata.c elf_getdata.c elf_newdata.c \
+                  elf_getdata_rawchunk.c \
                   elf_flagelf.c elf_flagehdr.c elf_flagphdr.c elf_flagscn.c \
                   elf_flagshdr.c elf_flagdata.c elf_memory.c \
                   elf_update.c elf32_updatenull.c elf64_updatenull.c \
@@ -93,7 +94,6 @@ libelf_a_SOURCES = elf_version.c elf_hash.c elf_error.c elf_fill.c \
                   gelf_update_verdaux.c \
                   elf_getshnum.c elf_getshstrndx.c \
                   gelf_checksum.c elf32_checksum.c elf64_checksum.c \
-                  gelf_rawchunk.c gelf_freechunk.c \
                   libelf_crc32.c libelf_next_prime.c \
                   elf_clone.c \
                   gelf_getlib.c gelf_update_lib.c \
index 4b4e11f..5112eae 100644 (file)
@@ -1,5 +1,5 @@
 /* Free resources associated with Elf descriptor.
-   Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2005 Red Hat, Inc.
+   Copyright (C) 1998,1999,2000,2001,2002,2004,2005,2007 Red Hat, Inc.
    This file is part of Red Hat elfutils.
    Written by Ulrich Drepper <drepper@redhat.com>, 1998.
 
@@ -89,7 +89,8 @@ elf_end (elf)
         descriptor.  The long name table cannot be freed yet since
         the archive headers for the ELF files in the archive point
         into this array.  */
-      free (elf->state.ar.ar_sym);
+      if (elf->state.ar.ar_sym != (Elf_Arsym *) -1l)
+       free (elf->state.ar.ar_sym);
       elf->state.ar.ar_sym = NULL;
 
       if (elf->state.ar.children != NULL)
@@ -134,6 +135,21 @@ elf_end (elf)
 
     case ELF_K_ELF:
       {
+       Elf_Data_Chunk *rawchunks
+         = (elf->class == ELFCLASS32
+            || (offsetof (struct Elf, state.elf32.rawchunks)
+                == offsetof (struct Elf, state.elf64.rawchunks))
+            ? elf->state.elf32.rawchunks
+            : elf->state.elf64.rawchunks);
+       while (rawchunks != NULL)
+         {
+           Elf_Data_Chunk *next = rawchunks->next;
+           if (rawchunks->dummy_scn.flags & ELF_F_MALLOCED)
+             free (rawchunks->data.d.d_buf);
+           free (rawchunks);
+           rawchunks = next;
+         }
+
        Elf_ScnList *list = (elf->class == ELFCLASS32
                             || (offsetof (struct Elf, state.elf32.scns)
                                 == offsetof (struct Elf, state.elf64.scns))
diff --git a/libelf/elf_getdata_rawchunk.c b/libelf/elf_getdata_rawchunk.c
new file mode 100644 (file)
index 0000000..bea0f3f
--- /dev/null
@@ -0,0 +1,188 @@
+/* Return converted data from raw chunk of ELF file.
+   Copyright (C) 2007 Red Hat, Inc.
+   This file is part of Red Hat elfutils.
+
+   Red Hat elfutils 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; version 2 of the License.
+
+   Red Hat 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 Red Hat elfutils; if not, write to the Free Software Foundation,
+   Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
+
+   In addition, as a special exception, Red Hat, Inc. gives You the
+   additional right to link the code of Red Hat elfutils with code licensed
+   under any Open Source Initiative certified open source license
+   (http://www.opensource.org/licenses/index.php) which requires the
+   distribution of source code with any binary distribution and to
+   distribute linked combinations of the two.  Non-GPL Code permitted under
+   this exception must only link to the code of Red Hat elfutils through
+   those well defined interfaces identified in the file named EXCEPTION
+   found in the source code files (the "Approved Interfaces").  The files
+   of Non-GPL Code may instantiate templates or use macros or inline
+   functions from the Approved Interfaces without causing the resulting
+   work to be covered by the GNU General Public License.  Only Red Hat,
+   Inc. may make changes or additions to the list of Approved Interfaces.
+   Red Hat's grant of this exception is conditioned upon your not adding
+   any new exceptions.  If you wish to add a new Approved Interface or
+   exception, please contact Red Hat.  You must obey the GNU General Public
+   License in all respects for all of the Red Hat elfutils code and other
+   code used in conjunction with Red Hat elfutils except the Non-GPL Code
+   covered by this exception.  If you modify this file, you may extend this
+   exception to your version of the file, but you are not obligated to do
+   so.  If you do not wish to provide this exception without modification,
+   you must delete this exception statement from your version and license
+   this file solely under the GPL without exception.
+
+   Red Hat elfutils is an included package of the Open Invention Network.
+   An included package of the Open Invention Network is a package for which
+   Open Invention Network licensees cross-license their patents.  No patent
+   license is granted, either expressly or impliedly, by designation as an
+   included package.  Should you wish to participate in the Open Invention
+   Network licensing program, please visit www.openinventionnetwork.com
+   <http://www.openinventionnetwork.com>.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <system.h>
+#include "libelfP.h"
+#include "common.h"
+
+Elf_Data *
+elf_getdata_rawchunk (elf, offset, size, type)
+     Elf *elf;
+     off64_t offset;
+     size_t size;
+     Elf_Type type;
+{
+  if (unlikely (elf == NULL))
+    return NULL;
+
+  if (unlikely (elf->kind != ELF_K_ELF))
+    {
+      /* No valid descriptor.  */
+      __libelf_seterrno (ELF_E_INVALID_HANDLE);
+      return NULL;
+    }
+
+  if (unlikely (size > elf->maximum_size
+               || (off64_t) (elf->maximum_size - size) < offset))
+    {
+      /* Invalid request.  */
+      __libelf_seterrno (ELF_E_INVALID_OP);
+      return NULL;
+    }
+
+  if (type >= ELF_T_NUM)
+    {
+      __libelf_seterrno (ELF_E_UNKNOWN_TYPE);
+      return NULL;
+    }
+
+  /* Get the raw bytes from the file.  */
+  void *rawchunk;
+  int flags = 0;
+
+  /* If the file is mmap'ed we can use it directly.  */
+  if (elf->map_address != NULL)
+    rawchunk = elf->map_address + elf->start_offset + offset;
+  else
+    {
+      /* We allocate the memory and read the data from the file.  */
+      rawchunk = malloc (size);
+      if (rawchunk == NULL)
+       {
+       nomem:
+         __libelf_seterrno (ELF_E_NOMEM);
+         return NULL;
+       }
+
+      /* Read the file content.  */
+      if (unlikely ((size_t) pread_retry (elf->fildes, rawchunk, size,
+                                         elf->start_offset + offset)
+                   != size))
+       {
+         /* Something went wrong.  */
+         free (rawchunk);
+         __libelf_seterrno (ELF_E_READ_ERROR);
+         return NULL;
+       }
+
+      flags = ELF_F_MALLOCED;
+    }
+
+  /* Copy and/or convert the data as needed for aligned native-order access.  */
+  size_t align = __libelf_type_align (elf->class, type);
+  void *buffer;
+  if (elf->state.elf32.ehdr->e_ident[EI_DATA] == MY_ELFDATA)
+    {
+      if (((uintptr_t) rawchunk & (align - 1)) == 0)
+       /* No need to copy, we can use the raw data.  */
+       buffer = rawchunk;
+      else
+       {
+         /* A malloc'd block is always sufficiently aligned.  */
+         assert (flags == 0);
+
+         buffer = malloc (size);
+         if (unlikely (buffer == NULL))
+           goto nomem;
+         flags = ELF_F_MALLOCED;
+
+         /* The copy will be appropriately aligned for direct access.  */
+         memcpy (buffer, rawchunk, size);
+       }
+    }
+  else
+    {
+      if (flags)
+       buffer = rawchunk;
+      else
+       {
+         buffer = malloc (size);
+         if (unlikely (buffer == NULL))
+           goto nomem;
+         flags = ELF_F_MALLOCED;
+       }
+
+      /* Call the conversion function.  */
+      (*__elf_xfctstom[LIBELF_EV_IDX][LIBELF_EV_IDX][elf->class - 1][type])
+       (buffer, rawchunk, size, 0);
+    }
+
+  /* Allocate the dummy container to point at this buffer.  */
+  Elf_Data_Chunk *chunk = calloc (1, sizeof *chunk);
+  if (chunk == NULL)
+    {
+      if (flags)
+       free (buffer);
+      goto nomem;
+    }
+
+  chunk->dummy_scn.elf = elf;
+  chunk->dummy_scn.flags = flags;
+  chunk->data.s = &chunk->dummy_scn;
+  chunk->data.d.d_buf = buffer;
+  chunk->data.d.d_size = size;
+  chunk->data.d.d_type = type;
+  chunk->data.d.d_align = align;
+  chunk->data.d.d_version = __libelf_version;
+
+  chunk->next = elf->state.elf.rawchunks;
+  elf->state.elf.rawchunks = chunk;
+
+  return &chunk->data.d;
+}
index 9f81b7e..533e15a 100644 (file)
@@ -343,13 +343,6 @@ extern size_t gelf_getnote (Elf_Data *__data, size_t __offset,
                            size_t *__name_offset, size_t *__desc_offset);
 
 
-/* Retrieve uninterpreted chunk of the file contents.  */
-extern char *gelf_rawchunk (Elf *__elf, GElf_Off __offset, GElf_Word __size);
-
-/* Release uninterpreted chunk of the file contents.  */
-extern void gelf_freechunk (Elf *__elf, char *__ptr);
-
-
 /* Compute simple checksum from permanent parts of the ELF file.  */
 extern long int gelf_checksum (Elf *__elf);
 
index a5d744c..bd8dcb0 100644 (file)
@@ -95,7 +95,7 @@ typedef struct
   Elf_Type d_type;             /* Type of this piece of data.  */
   unsigned int d_version;      /* ELF version.  */
   size_t d_size;               /* Size in bytes.  */
-  off_t d_off;                 /* Offset into section.  */
+  off64_t d_off;                       /* Offset into section.  */
   size_t d_align;              /* Alignment in section.  */
 } Elf_Data;
 
@@ -157,7 +157,7 @@ typedef struct
   uid_t ar_uid;                        /* User ID.  */
   gid_t ar_gid;                        /* Group ID.  */
   mode_t ar_mode;              /* File mode.  */
-  off_t ar_size;               /* File size.  */
+  off64_t ar_size;             /* File size.  */
   char *ar_rawname;            /* Original name of archive member.  */
 } Elf_Arhdr;
 
@@ -198,13 +198,13 @@ extern Elf_Cmd elf_next (Elf *__elf);
 extern int elf_end (Elf *__elf);
 
 /* Update ELF descriptor and write file to disk.  */
-extern off_t elf_update (Elf *__elf, Elf_Cmd __cmd);
+extern off64_t elf_update (Elf *__elf, Elf_Cmd __cmd);
 
 /* Determine what kind of file is associated with ELF.  */
 extern Elf_Kind elf_kind (Elf *__elf) __attribute__ ((__pure__));
 
 /* Get the base offset for an object file.  */
-extern off_t elf_getbase (Elf *__elf);
+extern off64_t elf_getbase (Elf *__elf);
 
 
 /* Retrieve file identification data.  */
@@ -298,6 +298,13 @@ extern Elf_Data *elf_rawdata (Elf_Scn *__scn, Elf_Data *__data);
 /* Create new data descriptor for section SCN.  */
 extern Elf_Data *elf_newdata (Elf_Scn *__scn);
 
+/* Get data translated from a chunk of the file contents as section data
+   would be for TYPE.  The resulting Elf_Data pointer is valid until
+   elf_end (ELF) is called.  */
+extern Elf_Data *elf_getdata_rawchunk (Elf *__elf,
+                                      off64_t __offset, size_t __size,
+                                      Elf_Type __type);
+
 
 /* Return pointer to string at OFFSET in section INDEX.  */
 extern char *elf_strptr (Elf *__elf, size_t __index, size_t __offset);
@@ -307,7 +314,7 @@ extern char *elf_strptr (Elf *__elf, size_t __index, size_t __offset);
 extern Elf_Arhdr *elf_getarhdr (Elf *__elf);
 
 /* Return offset in archive for current file ELF.  */
-extern off_t elf_getaroff (Elf *__elf);
+extern off64_t elf_getaroff (Elf *__elf);
 
 /* Select archive element at OFFSET.  */
 extern size_t elf_rand (Elf *__elf, size_t __offset);
index 9453c43..aaaf916 100644 (file)
@@ -55,7 +55,6 @@ ELFUTILS_1.0 {
     elf_update;
     elf_version;
     gelf_checksum;
-    gelf_freechunk;
     gelf_fsize;
     gelf_getclass;
     gelf_getdyn;
@@ -75,7 +74,6 @@ ELFUTILS_1.0 {
     gelf_getversym;
     gelf_newehdr;
     gelf_newphdr;
-    gelf_rawchunk;
     gelf_update_dyn;
     gelf_update_ehdr;
     gelf_update_move;
@@ -120,6 +118,7 @@ ELFUTILS_1.2 {
 
 ELFUTILS_1.3 {
   global:
+    elf_getdata_rawchunk;
     gelf_getauxv;
     gelf_update_auxv;
     gelf_getnote;
index 291206c..7e6305c 100644 (file)
@@ -255,6 +255,18 @@ typedef struct Elf_ScnList
 } Elf_ScnList;
 
 
+/* elf_getdata_rawchunk result.  */
+typedef struct Elf_Data_Chunk
+{
+  Elf_Data_Scn data;
+  union
+  {
+    Elf_Scn dummy_scn;
+    struct Elf_Data_Chunk *next;
+  };
+} Elf_Data_Chunk;
+
+
 /* The ELF descriptor.  */
 struct Elf
 {
@@ -313,6 +325,7 @@ struct Elf
       Elf_ScnList *scns_last;  /* Last element in the section list.
                                   If NULL the data has not yet been
                                   read from the file.  */
+      Elf_Data_Chunk *rawchunks; /* List of elf_getdata_rawchunk results.  */
       unsigned int scnincr;    /* Number of sections allocate the last
                                   time.  */
       off64_t sizestr_offset;  /* Offset of the size string in the parent
@@ -332,6 +345,7 @@ struct Elf
       Elf_ScnList *scns_last;  /* Last element in the section list.
                                   If NULL the data has not yet been
                                   read from the file.  */
+      Elf_Data_Chunk *rawchunks; /* List of elf_getdata_rawchunk results.  */
       unsigned int scnincr;    /* Number of sections allocate the last
                                   time.  */
       off64_t sizestr_offset;  /* Offset of the size string in the parent
@@ -357,6 +371,7 @@ struct Elf
       Elf_ScnList *scns_last;  /* Last element in the section list.
                                   If NULL the data has not yet been
                                   read from the file.  */
+      Elf_Data_Chunk *rawchunks; /* List of elf_getdata_rawchunk results.  */
       unsigned int scnincr;    /* Number of sections allocate the last
                                   time.  */
       off64_t sizestr_offset;  /* Offset of the size string in the parent
index 832c5b9..f4937cf 100644 (file)
@@ -1,3 +1,34 @@
+2007-10-04  Roland McGrath  <roland@redhat.com>
+
+       * readelf.c (print_archive_index): New variable.
+       (options, parse_opt): Accept -c/--archive-index to set it.
+       (dump_archive_index): New function.
+       (process_file): Take new arg WILL_PRINT_ARCHIVE_INDEX.
+       Call dump_archive_index on archives if set.
+       (main): Update caller.
+       (any_control_option): Give it file scope, moved out of ...
+       (parse_opt): ... here.
+
+2007-10-03  Roland McGrath  <roland@redhat.com>
+
+       * unstrip.c (struct arg_info): Add `list' flag.
+       (options, parse_opt): Grok -n/--list to set it.
+       (list_module): New function.
+       (handle_implicit_modules): Call it under -n.
+
+       * elflint.c (check_note_section): New function.
+       (check_sections): Call it for SHT_NOTE.
+
+       * readelf.c (handle_notes): Use sections when available.
+
+       * elflint.c (check_note_data): New function, broken out of ...
+       (check_note): ... here.  Call it and elf_getdata_rawchunk.
+
+       * readelf.c (handle_auxv_note): Take offset as argument, not buffer.
+       Use elf_getdata_rawchunk and gelf_getauxv.
+       (handle_notes_data): New function, broken out of ...
+       (handle_notes): ... here.  Call it and elf_getdata_rawchunk.
+
 2007-10-01  Roland McGrath  <roland@redhat.com>
 
        * readelf.c (hex_dump): Fix transposed subtraction generating spaces.
index 37936b1..e855d48 100644 (file)
@@ -96,6 +96,9 @@ static void process_file (int fd, Elf *elf, const char *prefix,
                          bool only_one);
 static void process_elf_file (Elf *elf, const char *prefix, const char *suffix,
                              const char *fname, size_t size, bool only_one);
+static void check_note_section (Ebl *ebl, GElf_Ehdr *ehdr,
+                               GElf_Shdr *shdr, int idx);
+
 
 /* Report an error.  */
 #define ERROR(str, args...) \
@@ -3499,6 +3502,10 @@ section [%2zu] '%s': relocatable files cannot have dynamic symbol tables\n"),
          check_group (ebl, ehdr, shdr, cnt);
          break;
 
+       case SHT_NOTE:
+         check_note_section (ebl, ehdr, shdr, cnt);
+         break;
+
        case SHT_GNU_versym:
          /* We cannot process this section now since we have no guarantee
             that the verneed and verdef sections have already been read.
@@ -3565,163 +3572,141 @@ no .gnu.versym section present but .gnu.versym_d or .gnu.versym_r section exist\
 }
 
 
-static void
-check_note (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Phdr *phdr, int cnt)
+static GElf_Off
+check_note_data (Ebl *ebl, const GElf_Ehdr *ehdr,
+                Elf_Data *data, int shndx, int phndx, GElf_Off start)
 {
-  if (ehdr->e_type != ET_CORE && ehdr->e_type != ET_REL
-      && ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN)
-    ERROR (gettext ("\
-phdr[%d]: no note entries defined for the type of file\n"),
-          cnt);
-
-  if (is_debuginfo)
-    /* The p_offset values in a separate debug file are bogus.  */
-    return;
-
-  char *notemem = gelf_rawchunk (ebl->elf, phdr->p_offset, phdr->p_filesz);
-
-  /* ELF64 files often use note section entries in the 32-bit format.
-     The p_align field is set to 8 in case the 64-bit format is used.
-     In case the p_align value is 0 or 4 the 32-bit format is
-     used.  */
-  GElf_Xword align = phdr->p_align == 0 || phdr->p_align == 4 ? 4 : 8;
-#define ALIGNED_LEN(len) (((len) + align - 1) & ~(align - 1))
-
-  GElf_Xword idx = 0;
-  while (idx < phdr->p_filesz)
+  size_t offset = 0;
+  size_t last_offset = 0;
+  GElf_Nhdr nhdr;
+  size_t name_offset;
+  size_t desc_offset;
+  while (offset < data->d_size
+        && (offset = gelf_getnote (data, offset,
+                                   &nhdr, &name_offset, &desc_offset)) > 0)
     {
-      uint64_t namesz;
-      uint64_t descsz;
-      uint64_t type;
-      uint32_t namesz32;
-      uint32_t descsz32;
-
-      if (align == 4)
-       {
-         uint32_t *ptr = (uint32_t *) (notemem + idx);
-
-         if ((__BYTE_ORDER == __LITTLE_ENDIAN
-              && ehdr->e_ident[EI_DATA] == ELFDATA2MSB)
-             || (__BYTE_ORDER == __BIG_ENDIAN
-                 && ehdr->e_ident[EI_DATA] == ELFDATA2LSB))
-           {
-             namesz32 = namesz = bswap_32 (*ptr);
-             ++ptr;
-             descsz32 = descsz = bswap_32 (*ptr);
-             ++ptr;
-             type = bswap_32 (*ptr);
-           }
-         else
-           {
-             namesz32 = namesz = *ptr++;
-             descsz32 = descsz = *ptr++;
-             type = *ptr;
-           }
-       }
-      else
-       {
-         uint64_t *ptr = (uint64_t *) (notemem + idx);
-         uint32_t *ptr32 = (uint32_t *) (notemem + idx);
-
-         if ((__BYTE_ORDER == __LITTLE_ENDIAN
-              && ehdr->e_ident[EI_DATA] == ELFDATA2MSB)
-             || (__BYTE_ORDER == __BIG_ENDIAN
-                 && ehdr->e_ident[EI_DATA] == ELFDATA2LSB))
-           {
-             namesz = bswap_64 (*ptr);
-             ++ptr;
-             descsz = bswap_64 (*ptr);
-             ++ptr;
-             type = bswap_64 (*ptr);
-
-             namesz32 = bswap_32 (*ptr32);
-             ++ptr32;
-             descsz32 = bswap_32 (*ptr32);
-           }
-         else
-           {
-             namesz = *ptr++;
-             descsz = *ptr++;
-             type = *ptr;
-
-             namesz32 = *ptr32++;
-             descsz32 = *ptr32;
-           }
-       }
-
-      if (idx + 3 * align > phdr->p_filesz
-         || (idx + 3 * align + ALIGNED_LEN (namesz) + ALIGNED_LEN (descsz)
-             > phdr->p_filesz))
-       {
-         if (ehdr->e_ident[EI_CLASS] == ELFCLASS64
-             && idx + 3 * 4 <= phdr->p_filesz
-             && (idx + 3 * 4 + ALIGNED_LEN (namesz32) + ALIGNED_LEN (descsz32)
-                 <= phdr->p_filesz))
-           ERROR (gettext ("\
-phdr[%d]: note entries probably in form of a 32-bit ELF file\n"), cnt);
-         else
-           ERROR (gettext ("phdr[%d]: extra %zu bytes after last note\n"),
-                  cnt, (size_t) (phdr->p_filesz - idx));
-         break;
-       }
+      last_offset = offset;
 
       /* Make sure it is one of the note types we know about.  */
       if (ehdr->e_type == ET_CORE)
-       {
-         switch (type)
-           {
-           case NT_PRSTATUS:
-           case NT_FPREGSET:
-           case NT_PRPSINFO:
-           case NT_TASKSTRUCT:         /* NT_PRXREG on Solaris.  */
-           case NT_PLATFORM:
-           case NT_AUXV:
-           case NT_GWINDOWS:
-           case NT_ASRS:
-           case NT_PSTATUS:
-           case NT_PSINFO:
-           case NT_PRCRED:
-           case NT_UTSNAME:
-           case NT_LWPSTATUS:
-           case NT_LWPSINFO:
-           case NT_PRFPXREG:
-             /* Known type.  */
-             break;
+       switch (nhdr.n_type)
+         {
+         case NT_PRSTATUS:
+         case NT_FPREGSET:
+         case NT_PRPSINFO:
+         case NT_TASKSTRUCT:           /* NT_PRXREG on Solaris.  */
+         case NT_PLATFORM:
+         case NT_AUXV:
+         case NT_GWINDOWS:
+         case NT_ASRS:
+         case NT_PSTATUS:
+         case NT_PSINFO:
+         case NT_PRCRED:
+         case NT_UTSNAME:
+         case NT_LWPSTATUS:
+         case NT_LWPSINFO:
+         case NT_PRFPXREG:
+           /* Known type.  */
+           break;
 
-           default:
+         default:
+           if (shndx == 0)
              ERROR (gettext ("\
-phdr[%d]: unknown core file note type %" PRIu64 " at offset %" PRIu64 "\n"),
-                    cnt, type, idx);
-           }
-       }
+phdr[%d]: unknown core file note type %" PRIu32 " at offset %" PRIu64 "\n"),
+                    phndx, (uint32_t) nhdr.n_type, start + offset);
+           else
+             ERROR (gettext ("\
+section [%2d] '%s': unknown core file note type %" PRIu32
+                             " at offset %Zu\n"),
+                    shndx, section_name (ebl, shndx),
+                    (uint32_t) nhdr.n_type, offset);
+         }
       else
-       switch (type)
+       switch (nhdr.n_type)
          {
-         case NT_GNU_ABI_TAG:  /* aka NT_VERSION */
+         case NT_GNU_ABI_TAG:
          case NT_GNU_HWCAP:
          case NT_GNU_BUILD_ID:
-           /* Known type.  */
            break;
 
          case 0:
            /* Linux vDSOs use a type 0 note for the kernel version word.  */
-           if (namesz == sizeof "Linux"
-               && !memcmp (notemem + idx + 3 * align, "Linux", sizeof "Linux"))
+           if (nhdr.n_namesz == sizeof "Linux"
+               && !memcmp (data->d_buf + name_offset, "Linux", sizeof "Linux"))
              break;
 
          default:
-           ERROR (gettext ("\
-phdr[%d]: unknown object file note type %" PRIu64 " at offset %" PRIu64 "\n"),
-                  cnt, type, idx);
+           if (shndx == 0)
+             ERROR (gettext ("\
+phdr[%d]: unknown object file note type %" PRIu32 " at offset %Zu\n"),
+                    phndx, (uint32_t) nhdr.n_type, offset);
+           else
+             ERROR (gettext ("\
+section [%2d] '%s': unknown object file note type %" PRIu32
+                             " at offset %Zu\n"),
+                    shndx, section_name (ebl, shndx),
+                    (uint32_t) nhdr.n_type, offset);
          }
-
-      /* Move to the next entry.  */
-      idx += 3 * align + ALIGNED_LEN (namesz) + ALIGNED_LEN (descsz);
     }
 
-  gelf_freechunk (ebl->elf, notemem);
+  return last_offset;
+}
+
+static void
+check_note (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Phdr *phdr, int cnt)
+{
+  if (ehdr->e_type != ET_CORE && ehdr->e_type != ET_REL
+      && ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN)
+    ERROR (gettext ("\
+phdr[%d]: no note entries defined for the type of file\n"),
+          cnt);
+
+  if (is_debuginfo)
+    /* The p_offset values in a separate debug file are bogus.  */
+    return;
+
+  GElf_Off notes_size = 0;
+  Elf_Data *data = elf_getdata_rawchunk (ebl->elf,
+                                        phdr->p_offset, phdr->p_filesz,
+                                        ELF_T_NHDR);
+  if (data != NULL)
+    notes_size = check_note_data (ebl, ehdr, data, 0, cnt, phdr->p_offset);
+
+  if (notes_size == 0)
+    ERROR (gettext ("phdr[%d]: cannot get content of note section: %s\n"),
+          cnt, elf_errmsg (-1));
+  else if (notes_size != phdr->p_filesz)
+    ERROR (gettext ("phdr[%d]: extra %" PRIu64 " bytes after last note\n"),
+          cnt, phdr->p_filesz - notes_size);
 }
 
+static void
+check_note_section (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Shdr *shdr, int idx)
+{
+  Elf_Data *data = elf_getdata (elf_getscn (ebl->elf, idx), NULL);
+  if (data == NULL)
+    {
+      ERROR (gettext ("section [%2d] '%s': cannot get section data\n"),
+            idx, section_name (ebl, idx));
+      return;
+    }
+
+  if (ehdr->e_type != ET_CORE && ehdr->e_type != ET_REL
+      && ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN)
+    ERROR (gettext ("\
+section [%2d] '%s': no note entries defined for the type of file\n"),
+            idx, section_name (ebl, idx));
+
+  GElf_Off notes_size = check_note_data (ebl, ehdr, data, idx, 0, 0);
+
+  if (notes_size == 0)
+    ERROR (gettext ("section [%2d] '%s': cannot get content of note section\n"),
+          idx, section_name (ebl, idx));
+  else if (notes_size != shdr->sh_size)
+    ERROR (gettext ("section [%2d] '%s': extra %" PRIu64
+                   " bytes after last note\n"),
+          idx, section_name (ebl, idx), shdr->sh_size - notes_size);
+}
 
 static void
 check_program_header (Ebl *ebl, GElf_Ehdr *ehdr)
index 823cf2b..fee908e 100644 (file)
@@ -50,6 +50,7 @@
 #include <sys/param.h>
 
 #include <system.h>
+#include "../libelf/libelfP.h"
 #include "../libebl/libeblP.h"
 #include "../libdw/libdwP.h"
 #include "../libdw/memory-access.h"
@@ -90,6 +91,8 @@ static const struct argp_option options[] =
   { "strings", 'p', "SECTION", OPTION_ARG_OPTIONAL,
     N_("Print string contents of sections"), 0 },
   { "string-dump", 'p', NULL, OPTION_ALIAS | OPTION_HIDDEN, NULL, 0 },
+  { "archive-index", 'c', NULL, 0,
+    N_("Display the symbol index of an archive"), 0 },
 
   { NULL, 0, NULL, 0, N_("Output control:"), 0 },
 
@@ -151,6 +154,12 @@ static bool print_notes;
 /* True if SHF_STRINGS section content should be printed.  */
 static bool print_string_sections;
 
+/* True if archive index should be printed.  */
+static bool print_archive_index;
+
+/* True if any of the control options except print_archive_index is set.  */
+static bool any_control_option;
+
 /* Select printing of debugging sections.  */
 static enum section_e
 {
@@ -190,7 +199,8 @@ static size_t shnum;
 
 /* Declarations of local functions.  */
 static void process_file (int fd, Elf *elf, const char *prefix,
-                         const char *fname, bool only_one);
+                         const char *fname, bool only_one,
+                         bool will_print_archive_index);
 static void process_elf_file (Elf *elf, const char *prefix, const char *fname,
                              bool only_one);
 static void print_ehdr (Ebl *ebl, GElf_Ehdr *ehdr);
@@ -215,6 +225,7 @@ static void print_liblist (Ebl *ebl);
 static void dump_data (Ebl *ebl);
 static void dump_strings (Ebl *ebl);
 static void print_strings (Ebl *ebl);
+static void dump_archive_index (Elf *, const char *);
 
 
 int
@@ -252,7 +263,8 @@ main (int argc, char *argv[])
               elf_errmsg (-1));
       else
        {
-         process_file (fd, elf, NULL, argv[remaining], only_one);
+         process_file (fd, elf, NULL, argv[remaining], only_one,
+                       print_archive_index);
 
          /* Now we can close the descriptor.  */
          if (elf_end (elf) != 0)
@@ -273,9 +285,6 @@ static error_t
 parse_opt (int key, char *arg,
           struct argp_state *state __attribute__ ((unused)))
 {
-  /* True if any of the control options is set.  */
-  static bool any_control_option;
-
   switch (key)
     {
     case 'a':
@@ -336,6 +345,9 @@ parse_opt (int key, char *arg,
       print_version_info = true;
       any_control_option = true;
       break;
+    case 'c':
+      print_archive_index = true;
+      break;
     case 'w':
       if (arg == NULL)
        print_debug_sections = section_all;
@@ -393,7 +405,7 @@ parse_opt (int key, char *arg,
       fputs (gettext ("Missing file name.\n"), stderr);
       goto do_argp_help;
     case ARGP_KEY_FINI:
-      if (! any_control_option)
+      if (! any_control_option && ! print_archive_index)
        {
          fputs (gettext ("No operation specified.\n"), stderr);
        do_argp_help:
@@ -426,7 +438,7 @@ warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
 /* Process one file.  */
 static void
 process_file (int fd, Elf *elf, const char *prefix, const char *fname,
-             bool only_one)
+             bool only_one, bool will_print_archive_index)
 {
   /* We can handle two types of files: ELF files and archives.  */
   Elf_Kind kind = elf_kind (elf);
@@ -435,8 +447,13 @@ process_file (int fd, Elf *elf, const char *prefix, const char *fname,
   switch (kind)
     {
     case ELF_K_ELF:
+      if (unlikely (will_print_archive_index))
+       error (0, 0,
+              gettext ("'%s' is not an archive, cannot print archive index"),
+              fname);
       /* Yes!  It's an ELF file.  */
-      process_elf_file (elf, prefix, fname, only_one);
+      if (any_control_option)
+       process_elf_file (elf, prefix, fname, only_one);
       break;
 
     case ELF_K_AR:
@@ -454,6 +471,9 @@ process_file (int fd, Elf *elf, const char *prefix, const char *fname,
          }
        memcpy (cp, fname, fname_len);
 
+       if (will_print_archive_index)
+         dump_archive_index (elf, new_prefix);
+
        /* It's an archive.  We process each file in it.  */
        Elf *subelf;
        Elf_Cmd cmd = ELF_C_READ_MMAP;
@@ -467,7 +487,8 @@ process_file (int fd, Elf *elf, const char *prefix, const char *fname,
                Elf_Arhdr *arhdr = elf_getarhdr (subelf);
                assert (arhdr != NULL);
 
-               process_file (fd, subelf, new_prefix, arhdr->ar_name, false);
+               process_file (fd, subelf, new_prefix, arhdr->ar_name, false,
+                             kind == ELF_K_AR && will_print_archive_index);
              }
 
            /* Get next archive element.  */
@@ -5493,12 +5514,21 @@ handle_core_registers (Ebl *ebl, Elf *core, const void *desc,
 }
 
 static void
-handle_auxv_note (Ebl *ebl, Elf *core, GElf_Word descsz, const void *desc)
+handle_auxv_note (Ebl *ebl, Elf *core, GElf_Word descsz, GElf_Off desc_pos)
 {
+  Elf_Data *data = elf_getdata_rawchunk (core, desc_pos, descsz, ELF_T_AUXV);
+  if (data == NULL)
+  elf_error:
+    error (EXIT_FAILURE, 0,
+          gettext ("cannot convert core note data: %s"), elf_errmsg (-1));
+
   const size_t nauxv = descsz / gelf_fsize (core, ELF_T_AUXV, 1, EV_CURRENT);
   for (size_t i = 0; i < nauxv; ++i)
     {
-      const GElf_auxv_t *av = (const GElf_auxv_t *) desc + i;
+      GElf_auxv_t av_mem;
+      GElf_auxv_t *av = gelf_getauxv (data, i, &av_mem);
+      if (av == NULL)
+       goto elf_error;
 
       const char *name;
       const char *fmt;
@@ -5578,16 +5608,101 @@ handle_core_note (Ebl *ebl, const GElf_Nhdr *nhdr, const void *desc)
     putchar_unlocked ('\n');
 }
 
+static void
+handle_notes_data (Ebl *ebl, const GElf_Ehdr *ehdr,
+                  GElf_Off start, Elf_Data *data)
+{
+  fputs_unlocked (gettext ("  Owner          Data size  Type\n"), stdout);
+
+  if (data == NULL)
+    goto bad_note;
+
+  size_t offset = 0;
+  GElf_Nhdr nhdr;
+  size_t name_offset;
+  size_t desc_offset;
+  while (offset < data->d_size
+        && (offset = gelf_getnote (data, offset,
+                                   &nhdr, &name_offset, &desc_offset)) > 0)
+    {
+      const char *name = data->d_buf + name_offset;
+      const char *desc = data->d_buf + desc_offset;
+
+      char buf[100];
+      char buf2[100];
+      printf (gettext ("  %-13.*s  %9" PRId32 "  %s\n"),
+             (int) nhdr.n_namesz, name, nhdr.n_descsz,
+             ehdr->e_type == ET_CORE
+             ? ebl_core_note_type_name (ebl, nhdr.n_type,
+                                        buf, sizeof (buf))
+             : ebl_object_note_type_name (ebl, nhdr.n_type,
+                                          buf2, sizeof (buf2)));
+
+      /* Filter out invalid entries.  */
+      if (memchr (name, '\0', nhdr.n_namesz) != NULL
+         /* XXX For now help broken Linux kernels.  */
+         || 1)
+       {
+         if (ehdr->e_type == ET_CORE)
+           {
+             if (nhdr.n_type == NT_AUXV)
+               handle_auxv_note (ebl, ebl->elf, nhdr.n_descsz,
+                                 start + desc_offset);
+             else
+               handle_core_note (ebl, &nhdr, desc);
+           }
+         else
+           ebl_object_note (ebl, name, nhdr.n_type, nhdr.n_descsz, desc);
+       }
+    }
+
+  if (offset == data->d_size)
+    return;
+
+ bad_note:
+  error (EXIT_FAILURE, 0,
+        gettext ("cannot get content of note section: %s"),
+        elf_errmsg (-1));
+}
 
 static void
 handle_notes (Ebl *ebl, GElf_Ehdr *ehdr)
 {
-  int class = gelf_getclass (ebl->elf);
-  size_t cnt;
+  /* If we have section headers, just look for SHT_NOTE sections.
+     In a debuginfo file, the program headers are not reliable.  */
+  if (shnum != 0)
+    {
+      /* Get the section header string table index.  */
+      size_t shstrndx;
+      if (elf_getshstrndx (ebl->elf, &shstrndx) < 0)
+       error (EXIT_FAILURE, 0,
+              gettext ("cannot get section header string table index"));
+
+      Elf_Scn *scn = NULL;
+      while ((scn = elf_nextscn (ebl->elf, scn)) != NULL)
+       {
+         GElf_Shdr shdr_mem;
+         GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+
+         if (shdr == NULL || shdr->sh_type != SHT_NOTE)
+           /* Not what we are looking for.  */
+           continue;
+
+         printf (gettext ("\
+\nNote section [%2zu] '%s' of %" PRIu64 " bytes at offset %#0" PRIx64 ":\n"),
+                 elf_ndxscn (scn),
+                 elf_strptr (ebl->elf, shstrndx, shdr->sh_name),
+                 shdr->sh_size, shdr->sh_offset);
+
+         handle_notes_data (ebl, ehdr, shdr->sh_offset,
+                            elf_getdata (scn, NULL));
+       }
+      return;
+    }
 
   /* We have to look through the program header to find the note
      sections.  There can be more than one.  */
-  for (cnt = 0; cnt < ehdr->e_phnum; ++cnt)
+  for (size_t cnt = 0; cnt < ehdr->e_phnum; ++cnt)
     {
       GElf_Phdr mem;
       GElf_Phdr *phdr = gelf_getphdr (ebl->elf, cnt, &mem);
@@ -5597,82 +5712,13 @@ handle_notes (Ebl *ebl, GElf_Ehdr *ehdr)
        continue;
 
       printf (gettext ("\
-\nNote segment of %" PRId64 " bytes at offset %#0" PRIx64 ":\n"),
+\nNote segment of %" PRIu64 " bytes at offset %#0" PRIx64 ":\n"),
              phdr->p_filesz, phdr->p_offset);
 
-      char *notemem = gelf_rawchunk (ebl->elf, phdr->p_offset, phdr->p_filesz);
-      if (notemem == NULL)
-       error (EXIT_FAILURE, 0,
-              gettext ("cannot get content of note section: %s"),
-              elf_errmsg (-1));
-
-      fputs_unlocked (gettext ("  Owner          Data size  Type\n"), stdout);
-
-
-      /* Handle the note section content.  It consists of one or more
-        entries each of which consists of five parts:
-
-        - a 32-bit name length
-        - a 32-bit descriptor length
-        - a 32-bit type field
-        - the NUL-terminated name, length as specified in the first field
-        - the descriptor, length as specified in the second field
-
-        The variable sized fields are padded to 32- or 64-bits
-        depending on whether the file is a 32- or 64-bit ELF file.
-      */
-      // XXX Which 64-bit archs need 8-byte alignment?  x86-64 does not.
-      size_t align = class == ELFCLASS32 ? 4 : 4; // XXX 8;
-#define ALIGNED_LEN(len) (((len) + align - 1) & ~(align - 1))
-
-      size_t idx = 0;
-      while (idx < phdr->p_filesz)
-       {
-         const GElf_Nhdr *nhdr = (const GElf_Nhdr *) (notemem + idx);
-         const char *name = (const char *) (nhdr + 1);
-         const void *desc = &name[ALIGNED_LEN (nhdr->n_namesz)];
-
-         if (idx + 12 > phdr->p_filesz
-             || (idx + 12 + ALIGNED_LEN (nhdr->n_namesz)
-                 + ALIGNED_LEN (nhdr->n_descsz) > phdr->p_filesz))
-           /* This entry isn't completely contained in the note
-              section.  Ignore it.  */
-           break;
-
-         char buf[100];
-         char buf2[100];
-         printf (gettext ("  %-13.*s  %9" PRId32 "  %s\n"),
-                 (int) nhdr->n_namesz, name,
-                 nhdr->n_descsz,
-                 ehdr->e_type == ET_CORE
-                 ? ebl_core_note_type_name (ebl, nhdr->n_type,
-                                            buf, sizeof (buf))
-                 : ebl_object_note_type_name (ebl, nhdr->n_type,
-                                              buf2, sizeof (buf2)));
-
-         /* Filter out invalid entries.  */
-         if (memchr (name, '\0', nhdr->n_namesz) != NULL
-             /* XXX For now help broken Linux kernels.  */
-             || 1)
-           {
-             if (ehdr->e_type == ET_CORE)
-               {
-                 if (nhdr->n_type == NT_AUXV)
-                   handle_auxv_note (ebl, ebl->elf, nhdr->n_descsz, desc);
-                 else
-                   handle_core_note (ebl, nhdr, desc);
-               }
-             else
-               ebl_object_note (ebl, name, nhdr->n_type,
-                                nhdr->n_descsz, desc);
-           }
-
-         /* Move to the next entry.  */
-         idx += (12 + ALIGNED_LEN (nhdr->n_namesz)
-                 + ALIGNED_LEN (nhdr->n_descsz));
-       }
-
-      gelf_freechunk (ebl->elf, notemem);
+      handle_notes_data (ebl, ehdr, phdr->p_offset,
+                        elf_getdata_rawchunk (ebl->elf,
+                                              phdr->p_offset, phdr->p_filesz,
+                                              ELF_T_NHDR));
     }
 }
 
@@ -5866,3 +5912,48 @@ print_strings (Ebl *ebl)
       print_string_section (scn, &shdr_mem, name);
     }
 }
+
+static void
+dump_archive_index (Elf *elf, const char *fname)
+{
+  size_t narsym;
+  const Elf_Arsym *arsym = elf_getarsym (elf, &narsym);
+  if (arsym == NULL)
+    {
+      int result = elf_errno ();
+      if (result != ELF_E_NO_INDEX)
+       error (EXIT_FAILURE, 0,
+              gettext ("cannot get symbol index of archive '%s': %s"),
+              fname, elf_errmsg (result));
+      else
+       printf (gettext ("\nArchive '%s' has no symbol index\n"), fname);
+      return;
+    }
+
+  printf (gettext ("\nIndex of archive '%s' has %Zu entries:\n"),
+         fname, narsym);
+
+  size_t as_off = 0;
+  for (const Elf_Arsym *s = arsym; s < &arsym[narsym - 1]; ++s)
+    {
+      if (s->as_off != as_off)
+       {
+         as_off = s->as_off;
+
+         Elf *subelf;
+         if (elf_rand (elf, as_off) == 0
+             || (subelf = elf_begin (-1, ELF_C_READ_MMAP, elf)) == NULL)
+           error (EXIT_FAILURE, 0,
+                  gettext ("cannot extract member at offset %Zu in '%s': %s"),
+                  as_off, fname, elf_errmsg (-1));
+
+         const Elf_Arhdr *h = elf_getarhdr (subelf);
+
+         printf (gettext ("Archive member '%s' contains:\n"), h->ar_name);
+
+         elf_end (subelf);
+       }
+
+      printf ("\t%s\n", s->as_name);
+    }
+}
index a8c3002..ec22aa9 100644 (file)
@@ -53,6 +53,7 @@
 #include <stdbool.h>
 #include <stdio.h>
 #include <stdio_ext.h>
+#include <inttypes.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
@@ -90,6 +91,8 @@ static const struct argp_option options[] =
   { "all", 'a', NULL, 0,
     N_("Create output for modules that have no separate debug information"),
     0 },
+  { "list-only", 'n', NULL, 0,
+    N_("Only list module and file names, build IDs"), 0 },
   { NULL, 0, NULL, 0, NULL, 0 }
 };
 
@@ -99,6 +102,7 @@ struct arg_info
   const char *output_dir;
   Dwfl *dwfl;
   char **args;
+  bool list;
   bool all;
   bool ignore;
   bool modnames;
@@ -147,6 +151,9 @@ parse_opt (int key, char *arg, struct argp_state *state)
     case 'i':
       info->ignore = true;
       break;
+    case 'n':
+      info->list = true;
+      break;
 
     case ARGP_KEY_ARGS:
     case ARGP_KEY_NO_ARGS:
@@ -159,6 +166,15 @@ parse_opt (int key, char *arg, struct argp_state *state)
          return EINVAL;
        }
 
+      if (info->list && (info->dwfl == NULL
+                        || info->output_dir != NULL
+                        || info->output_file != NULL))
+       {
+         argp_error (state,
+                     _("-n cannot be used with explicit files or -o or -d"));
+         return EINVAL;
+       }
+
       if (info->output_dir != NULL)
        {
          struct stat64 st;
@@ -194,7 +210,8 @@ parse_opt (int key, char *arg, struct argp_state *state)
             from defaulting to "-e a.out".  */
          return ENOSYS;
        }
-      else if (info->output_file == NULL && info->output_dir == NULL)
+      else if (info->output_file == NULL && info->output_dir == NULL
+              && !info->list)
        {
          argp_error (state,
                      _("-o or -d is required when using implicit files"));
@@ -1947,6 +1964,47 @@ handle_output_dir_module (const char *output_dir, Dwfl_Module *mod,
 }
 
 
+static void
+list_module (Dwfl_Module *mod)
+{
+  /* Make sure we have searched for the files.  */
+  GElf_Addr bias;
+  bool have_elf = dwfl_module_getelf (mod, &bias) != NULL;
+  bool have_dwarf = dwfl_module_getdwarf (mod, &bias) != NULL;
+
+  const char *file;
+  const char *debug;
+  Dwarf_Addr start;
+  Dwarf_Addr end;
+  const char *name = dwfl_module_info (mod, NULL, &start, &end,
+                                      NULL, NULL, &file, &debug);
+  if (file != NULL && debug != NULL && (debug == file || !strcmp (debug, file)))
+    debug = ".";
+
+  const unsigned char *id;
+  GElf_Addr id_vaddr;
+  int id_len = dwfl_module_build_id (mod, &id, &id_vaddr);
+
+  printf ("%#" PRIx64 "+%#" PRIx64 " ", start, end - start);
+
+  if (id_len > 0)
+    {
+      do
+       printf ("%02" PRIx8, *id++);
+      while (--id_len > 0);
+      if (id_vaddr != 0)
+       printf ("@%#" PRIx64, id_vaddr);
+    }
+  else
+    putchar ('-');
+
+  printf (" %s %s %s\n",
+         file ?: have_elf ? "." : "-",
+         debug ?: have_dwarf ? "." : "-",
+         name);
+}
+
+
 struct match_module_info
 {
   char **patterns;
@@ -2006,7 +2064,11 @@ handle_implicit_modules (const struct arg_info *info)
   if (offset == 0)
     error (EXIT_FAILURE, 0, _("no matching modules found"));
 
-  if (info->output_dir == NULL)
+  if (info->list)
+    do
+      list_module (mmi.found);
+    while ((offset = next (offset)) > 0);
+  else if (info->output_dir == NULL)
     {
       if (next (offset) != 0)
        error (EXIT_FAILURE, 0, _("matched more than one module"));
@@ -2068,7 +2130,19 @@ With no arguments, process all modules found.\n\
 Multiple modules are written to files under OUTPUT-DIRECTORY, \
 creating subdirectories as needed.  \
 With -m these files have simple module names, otherwise they have the \
-name of the main file complete with directory underneath OUTPUT-DIRECTORY.")
+name of the main file complete with directory underneath OUTPUT-DIRECTORY.\n\
+\n\
+With -n no files are written, but one line to standard output for each module:\
+\n\tSTART+SIZE BUILDID FILE DEBUGFILE MODULENAME\n\
+START and SIZE are hexadecimal giving the address bounds of the module.  \
+BUILDID is hexadecimal for the build ID bits, or - if no ID is known; \
+the hexadecimal may be followed by @0xADDR giving the address where the \
+ID resides if that is known.  \
+FILE is the file name found for the module, or - if none was found, \
+or . if an ELF image is available but not from any named file.  \
+DEBUGFILE is the separate debuginfo file name, \
+or - if no debuginfo was found, or . if FILE contains the debug information.\
+")
     };
 
   int remaining;
@@ -2102,7 +2176,7 @@ name of the main file complete with directory underneath OUTPUT-DIRECTORY.")
   else
     {
       /* parse_opt checked this.  */
-      assert (info.output_file != NULL || info.output_dir != NULL);
+      assert (info.output_file != NULL || info.output_dir != NULL || info.list);
 
       handle_implicit_modules (&info);
 
index 054cb40..32e4efe 100644 (file)
@@ -1,3 +1,13 @@
+2007-10-04  Roland McGrath  <roland@redhat.com>
+
+       * run-readelf-test4.sh: New file.
+       * Makefile.am (TESTS, EXTRA_DIST): Add it.
+
+2007-10-03  Roland McGrath  <roland@redhat.com>
+
+       * run-readelf-test3.sh: New file.
+       * Makefile.am (TESTS, EXTRA_DIST): Add it.
+
 2007-10-01  Roland McGrath  <roland@redhat.com>
 
        * run-readelf-test2.sh: New file.
index d1d1dbb..1fce5b7 100644 (file)
@@ -78,7 +78,8 @@ TESTS = run-arextract.sh run-arsymtest.sh newfile test-nlist \
        run-ranlib-test2.sh run-ranlib-test3.sh run-ranlib-test4.sh \
        run-addrscopes.sh run-strings-test.sh run-funcscopes.sh \
        run-find-prologues.sh run-allregs.sh \
-       run-readelf-test1.sh run-readelf-test2.sh \
+       run-readelf-test1.sh run-readelf-test2.sh run-readelf-test3.sh \
+       run-readelf-test4.sh \
        run-native-test.sh run-bug1-test.sh \
        dwfl-bug-addr-overflow run-addrname-test.sh \
        dwfl-bug-fd-leak dwfl-bug-report \
@@ -122,7 +123,8 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh \
             testfile22.bz2 testfile23.bz2 testfile24.bz2 testfile25.bz2 \
             testfile26.bz2 testfile27.bz2 \
             coverage.sh test-subr.sh test-wrapper.sh \
-            run-readelf-test1.sh run-readelf-test2.sh \
+            run-readelf-test1.sh run-readelf-test2.sh run-readelf-test3.sh \
+            run-readelf-test4.sh \
             run-bug1-test.sh testfile28.bz2 testfile28.rdwr.bz2 \
             testfile29.bz2 testfile29.rdwr.bz2 \
             testfile30.bz2 testfile31.bz2 testfile32.bz2 testfile33.bz2 \
diff --git a/tests/run-readelf-test3.sh b/tests/run-readelf-test3.sh
new file mode 100755 (executable)
index 0000000..71dd8ae
--- /dev/null
@@ -0,0 +1,39 @@
+#! /bin/sh
+# Copyright (C) 2007 Red Hat, Inc.
+# This file is part of Red Hat elfutils.
+#
+# Red Hat elfutils 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; version 2 of the License.
+#
+# Red Hat 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 Red Hat elfutils; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
+#
+# Red Hat elfutils is an included package of the Open Invention Network.
+# An included package of the Open Invention Network is a package for which
+# Open Invention Network licensees cross-license their patents.  No patent
+# license is granted, either expressly or impliedly, by designation as an
+# included package.  Should you wish to participate in the Open Invention
+# Network licensing program, please visit www.openinventionnetwork.com
+# <http://www.openinventionnetwork.com>.
+
+. $srcdir/test-subr.sh
+
+testfiles testfile40.debug
+
+testrun_compare ../src/readelf -n testfile40.debug <<\EOF
+
+Note section [ 6] '.note' of 60 bytes at offset 0x120:
+  Owner          Data size  Type
+  GNU                   20  GNU_BUILD_ID
+    Build ID: 34072edcd87ef6728f4b4a7956167b2fcfc3f1d3
+  Linux                  4  <unknown>: 0
+EOF
+
+exit 0
diff --git a/tests/run-readelf-test4.sh b/tests/run-readelf-test4.sh
new file mode 100755 (executable)
index 0000000..85e76ed
--- /dev/null
@@ -0,0 +1,41 @@
+#! /bin/sh
+# Copyright (C) 2007 Red Hat, Inc.
+# This file is part of Red Hat elfutils.
+#
+# Red Hat elfutils 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; version 2 of the License.
+#
+# Red Hat 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 Red Hat elfutils; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
+#
+# Red Hat elfutils is an included package of the Open Invention Network.
+# An included package of the Open Invention Network is a package for which
+# Open Invention Network licensees cross-license their patents.  No patent
+# license is granted, either expressly or impliedly, by designation as an
+# included package.  Should you wish to participate in the Open Invention
+# Network licensing program, please visit www.openinventionnetwork.com
+# <http://www.openinventionnetwork.com>.
+
+. $srcdir/test-subr.sh
+
+testfiles testfile19.index
+
+testrun_compare ../src/readelf -c testfile19.index <<\EOF
+
+Index of archive 'testfile19.index' has 4 entries:
+Archive member 'u1.o' contains:
+       a
+Archive member 'u2.o' contains:
+       aa
+Archive member 'u3.o' contains:
+       a
+EOF
+
+exit 0