diff options
author | Roland McGrath <roland@redhat.com> | 2007-10-04 08:50:09 +0000 |
---|---|---|
committer | Roland McGrath <roland@redhat.com> | 2007-10-04 08:50:09 +0000 |
commit | 59ea7f33f781e6e3f8c9d81d457e5d99eee8f1ce (patch) | |
tree | 10a3dd35d3b568876f0edc6dd903fe8715a507e1 | |
parent | 057ec6b35ef97bd1cf6c1e96da3da399237e5224 (diff) | |
download | android_external_elfutils-59ea7f33f781e6e3f8c9d81d457e5d99eee8f1ce.tar.gz android_external_elfutils-59ea7f33f781e6e3f8c9d81d457e5d99eee8f1ce.tar.bz2 android_external_elfutils-59ea7f33f781e6e3f8c9d81d457e5d99eee8f1ce.zip |
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.
31 files changed, 1796 insertions, 382 deletions
@@ -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, ¬es); 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 |