summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRoland McGrath <roland@redhat.com>2007-10-04 08:50:09 +0000
committerRoland McGrath <roland@redhat.com>2007-10-04 08:50:09 +0000
commit59ea7f33f781e6e3f8c9d81d457e5d99eee8f1ce (patch)
tree10a3dd35d3b568876f0edc6dd903fe8715a507e1
parent057ec6b35ef97bd1cf6c1e96da3da399237e5224 (diff)
src/
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.
-rw-r--r--NEWS15
-rw-r--r--libdw/ChangeLog8
-rw-r--r--libdw/libdw.map11
-rw-r--r--libdwfl/ChangeLog41
-rw-r--r--libdwfl/Makefile.am3
-rw-r--r--libdwfl/dwfl_build_id_find_debuginfo.c (renamed from libelf/gelf_rawchunk.c)92
-rw-r--r--libdwfl/dwfl_build_id_find_elf.c152
-rw-r--r--libdwfl/dwfl_module.c3
-rw-r--r--libdwfl/dwfl_module_build_id.c164
-rw-r--r--libdwfl/dwfl_module_getdwarf.c232
-rw-r--r--libdwfl/dwfl_module_report_build_id.c (renamed from libelf/gelf_freechunk.c)74
-rw-r--r--libdwfl/find-debuginfo.c71
-rw-r--r--libdwfl/libdwfl.h89
-rw-r--r--libdwfl/libdwflP.h24
-rw-r--r--libdwfl/linux-kernel-modules.c194
-rw-r--r--libelf/ChangeLog24
-rw-r--r--libelf/Makefile.am2
-rw-r--r--libelf/elf_end.c20
-rw-r--r--libelf/elf_getdata_rawchunk.c188
-rw-r--r--libelf/gelf.h7
-rw-r--r--libelf/libelf.h17
-rw-r--r--libelf/libelf.map3
-rw-r--r--libelf/libelfP.h15
-rw-r--r--src/ChangeLog31
-rw-r--r--src/elflint.c253
-rw-r--r--src/readelf.c267
-rw-r--r--src/unstrip.c82
-rw-r--r--tests/ChangeLog10
-rw-r--r--tests/Makefile.am6
-rwxr-xr-xtests/run-readelf-test3.sh39
-rwxr-xr-xtests/run-readelf-test4.sh41
31 files changed, 1796 insertions, 382 deletions
diff --git a/NEWS b/NEWS
index d257c1fd..3120ce50 100644
--- 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:
diff --git a/libdw/ChangeLog b/libdw/ChangeLog
index 7b6a44cf..34c4e5b1 100644
--- a/libdw/ChangeLog
+++ b/libdw/ChangeLog
@@ -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
diff --git a/libdw/libdw.map b/libdw/libdw.map
index 654bdfba..8ef5f633 100644
--- a/libdw/libdw.map
+++ b/libdw/libdw.map
@@ -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;
diff --git a/libdwfl/ChangeLog b/libdwfl/ChangeLog
index 3a99753b..454d9342 100644
--- a/libdwfl/ChangeLog
+++ b/libdwfl/ChangeLog
@@ -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
diff --git a/libdwfl/Makefile.am b/libdwfl/Makefile.am
index e5cbb979..83834cdb 100644
--- a/libdwfl/Makefile.am
+++ b/libdwfl/Makefile.am
@@ -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 \
diff --git a/libelf/gelf_rawchunk.c b/libdwfl/dwfl_build_id_find_debuginfo.c
index ced6f9ef..97def072 100644
--- a/libelf/gelf_rawchunk.c
+++ b/libdwfl/dwfl_build_id_find_debuginfo.c
@@ -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
@@ -48,61 +47,46 @@
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
index 00000000..c6215012
--- /dev/null
+++ b/libdwfl/dwfl_build_id_find_elf.c
@@ -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)
diff --git a/libdwfl/dwfl_module.c b/libdwfl/dwfl_module.c
index b84a0a80..4cee37c2 100644
--- a/libdwfl/dwfl_module.c
+++ b/libdwfl/dwfl_module.c
@@ -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
index 00000000..f9a6357f
--- /dev/null
+++ b/libdwfl/dwfl_module_build_id.c
@@ -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)
diff --git a/libdwfl/dwfl_module_getdwarf.c b/libdwfl/dwfl_module_getdwarf.c
index c0aeadd1..90c77c86 100644
--- a/libdwfl/dwfl_module_getdwarf.c
+++ b/libdwfl/dwfl_module_getdwarf.c
@@ -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;
}
diff --git a/libelf/gelf_freechunk.c b/libdwfl/dwfl_module_report_build_id.c
index 7b293ab0..4886931b 100644
--- a/libelf/gelf_freechunk.c
+++ b/libdwfl/dwfl_module_report_build_id.c
@@ -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
@@ -48,31 +47,56 @@
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)
diff --git a/libdwfl/find-debuginfo.c b/libdwfl/find-debuginfo.c
index ca1fadcb..f1ff3a4b 100644
--- a/libdwfl/find-debuginfo.c
+++ b/libdwfl/find-debuginfo.c
@@ -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)
diff --git a/libdwfl/libdwfl.h b/libdwfl/libdwfl.h
index ee1b5054..5438ee2d 100644
--- a/libdwfl/libdwfl.h
+++ b/libdwfl/libdwfl.h
@@ -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 *,
diff --git a/libdwfl/libdwflP.h b/libdwfl/libdwflP.h
index 6de87abb..4b458e1d 100644
--- a/libdwfl/libdwflP.h
+++ b/libdwfl/libdwflP.h
@@ -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)
diff --git a/libdwfl/linux-kernel-modules.c b/libdwfl/linux-kernel-modules.c
index 26f185f3..6164ae74 100644
--- a/libdwfl/linux-kernel-modules.c
+++ b/libdwfl/linux-kernel-modules.c
@@ -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)
diff --git a/libelf/ChangeLog b/libelf/ChangeLog
index c879aede..1532a4cf 100644
--- a/libelf/ChangeLog
+++ b/libelf/ChangeLog
@@ -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.
diff --git a/libelf/Makefile.am b/libelf/Makefile.am
index 3e52bcec..58c9b5a8 100644
--- a/libelf/Makefile.am
+++ b/libelf/Makefile.am
@@ -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 \
diff --git a/libelf/elf_end.c b/libelf/elf_end.c
index 4b4e11fe..5112eaea 100644
--- a/libelf/elf_end.c
+++ b/libelf/elf_end.c
@@ -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
index 00000000..bea0f3f6
--- /dev/null
+++ b/libelf/elf_getdata_rawchunk.c
@@ -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;
+}
diff --git a/libelf/gelf.h b/libelf/gelf.h
index 9f81b7e9..533e15a9 100644
--- a/libelf/gelf.h
+++ b/libelf/gelf.h
@@ -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);
diff --git a/libelf/libelf.h b/libelf/libelf.h
index a5d744cf..bd8dcb09 100644
--- a/libelf/libelf.h
+++ b/libelf/libelf.h
@@ -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);
diff --git a/libelf/libelf.map b/libelf/libelf.map
index 9453c438..aaaf9164 100644
--- a/libelf/libelf.map
+++ b/libelf/libelf.map
@@ -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;
diff --git a/libelf/libelfP.h b/libelf/libelfP.h
index 291206ca..7e6305cd 100644
--- a/libelf/libelfP.h
+++ b/libelf/libelfP.h
@@ -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
diff --git a/src/ChangeLog b/src/ChangeLog
index 832c5b99..f4937cf0 100644
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -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.
diff --git a/src/elflint.c b/src/elflint.c
index 37936b1f..e855d483 100644
--- a/src/elflint.c
+++ b/src/elflint.c
@@ -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)
diff --git a/src/readelf.c b/src/readelf.c
index 823cf2b9..fee908e9 100644
--- a/src/readelf.c
+++ b/src/readelf.c
@@ -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);
+ }
+}
diff --git a/src/unstrip.c b/src/unstrip.c
index a8c30022..ec22aa91 100644
--- a/src/unstrip.c
+++ b/src/unstrip.c
@@ -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);
diff --git a/tests/ChangeLog b/tests/ChangeLog
index 054cb40c..32e4efe9 100644
--- a/tests/ChangeLog
+++ b/tests/ChangeLog
@@ -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.
diff --git a/tests/Makefile.am b/tests/Makefile.am
index d1d1dbb3..1fce5b72 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -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
index 00000000..71dd8aea
--- /dev/null
+++ b/tests/run-readelf-test3.sh
@@ -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
index 00000000..85e76ed0
--- /dev/null
+++ b/tests/run-readelf-test4.sh
@@ -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