diff options
author | Ulrich Drepper <drepper@redhat.com> | 2007-10-16 05:21:27 +0000 |
---|---|---|
committer | Ulrich Drepper <drepper@redhat.com> | 2007-10-16 05:21:27 +0000 |
commit | b597dfad924980dede10d7c19d87900b6172e599 (patch) | |
tree | 3c090b69070ad0056d479d90aa1f8829810140ba | |
parent | 3fc3d7bd6bd8485404a936f7354e781dc2be6a5a (diff) | |
download | elfutils-b597dfad924980dede10d7c19d87900b6172e599.tar.gz |
merge of '92c36bfdbc6468d1711c043b530e0dfe5abb6dec'
and 'c22c8c43f8f68b0bffd4d5ccdb2282c958268742'
41 files changed, 1665 insertions, 489 deletions
@@ -2,13 +2,16 @@ 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; - new option --archive-index (or -c) + new option --archive-index (or -c); + improved -n output for core files, on many machines 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 +elflint: stricter checks on debug sections + 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; @@ -16,6 +19,8 @@ libdwfl: new functions dwfl_build_id_find_elf, dwfl_build_id_find_debuginfo, unstrip: new option --list (or -n) +libebl: backend improvements for sparc, alpha, powerpc + Version 0.129: readelf: new options --hex-dump (or -x), --strings (or -p) diff --git a/backends/ChangeLog b/backends/ChangeLog index 9c51f986..082f4f39 100644 --- a/backends/ChangeLog +++ b/backends/ChangeLog @@ -1,3 +1,21 @@ +2007-10-09 Roland McGrath <roland@redhat.com> + + * sparc_auxv.c: New file. + * Makefile.am (sparc_SRCS): Add it. + * sparc_init.c (sparc_init): Initialize auxv_info hook. + +2007-10-08 Roland McGrath <roland@redhat.com> + + * linux-core-note.c (TIMEVAL_FIELD): New macro. + (prstatus_items): Use it. + * sparc_corenote.c: New file. + * sparc64_corenote.c: New file. + * Makefile.am (sparc_SRCS): Add them. + * sparc_init.c (sparc_init): Initialize core_note hook. + + * sparc_symbol.c (sparc_machine_flag_check): New function. + * sparc_init.c (sparc_init): Use it. + 2007-09-27 Roland McGrath <roland@redhat.com> * alpha_retval.c: Use dwarf_attr_integrate and dwarf_hasattr_integrate. diff --git a/backends/Makefile.am b/backends/Makefile.am index 84aa4785..4174f8e3 100644 --- a/backends/Makefile.am +++ b/backends/Makefile.am @@ -96,7 +96,8 @@ arm_SRCS = arm_init.c arm_symbol.c libebl_arm_pic_a_SOURCES = $(arm_SRCS) am_libebl_arm_pic_a_OBJECTS = $(arm_SRCS:.c=.os) -sparc_SRCS = sparc_init.c sparc_symbol.c sparc_regs.c sparc_retval.c +sparc_SRCS = sparc_init.c sparc_symbol.c sparc_regs.c sparc_retval.c \ + sparc_corenote.c sparc64_corenote.c sparc_auxv.c libebl_sparc_pic_a_SOURCES = $(sparc_SRCS) am_libebl_sparc_pic_a_OBJECTS = $(sparc_SRCS:.c=.os) diff --git a/backends/linux-core-note.c b/backends/linux-core-note.c index 0913cc97..c4a90b70 100644 --- a/backends/linux-core-note.c +++ b/backends/linux-core-note.c @@ -53,6 +53,15 @@ struct EBLHOOK(timeval) FIELD (ULONG, tv_usec); }; +/* On sparc64, tv_usec (suseconds_t) is actually 32 bits with 32 bits padding. + The 'T'|0x80 value for .format indicates this as a special kludge. */ +#if SUSECONDS_HALF +# define TIMEVAL_FIELD(name) FIELD (time, ULONG, name, 'T'|0x80, .count = 2) +#else +# define TIMEVAL_FIELD(name) FIELD (time, ULONG, name, 'T', .count = 2) +#endif + + struct EBLHOOK(prstatus) { struct EBLHOOK(siginfo) pr_info; @@ -115,10 +124,10 @@ static const Ebl_Core_Item prstatus_items[] = FIELD (identity, PID_T, ppid, 'd'), FIELD (identity, PID_T, pgrp, 'd'), FIELD (identity, PID_T, sid, 'd'), - FIELD (time, ULONG, utime, 'T', .count = 2), - FIELD (time, ULONG, stime, 'T', .count = 2), - FIELD (time, ULONG, cutime, 'T', .count = 2), - FIELD (time, ULONG, cstime, 'T', .count = 2), + TIMEVAL_FIELD (utime), + TIMEVAL_FIELD (stime), + TIMEVAL_FIELD (cutime), + TIMEVAL_FIELD (cstime), #ifdef PRSTATUS_REGSET_ITEMS PRSTATUS_REGSET_ITEMS, #endif diff --git a/backends/sparc64_corenote.c b/backends/sparc64_corenote.c new file mode 100644 index 00000000..cef6431e --- /dev/null +++ b/backends/sparc64_corenote.c @@ -0,0 +1,2 @@ +#define BITS 64 +#include "sparc_corenote.c" diff --git a/backends/sparc_auxv.c b/backends/sparc_auxv.c new file mode 100644 index 00000000..a22b3cea --- /dev/null +++ b/backends/sparc_auxv.c @@ -0,0 +1,43 @@ +/* SPARC-specific auxv handling. + 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>. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#define BACKEND sparc_ +#include "libebl_CPU.h" + +int +EBLHOOK(auxv_info) (GElf_Xword a_type, const char **name, const char **format) +{ + if (a_type != AT_HWCAP) + return 0; + + *name = "HWCAP"; + *format = "b" + "flush\0" "stbar\0" "swap\0" "muldiv\0" "v9\0" "ultra3\0" "v9v\0" "\0"; + return 1; +} diff --git a/backends/sparc_corenote.c b/backends/sparc_corenote.c new file mode 100644 index 00000000..55aed8f4 --- /dev/null +++ b/backends/sparc_corenote.c @@ -0,0 +1,109 @@ +/* PowerPC specific core note handling. + 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>. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <elf.h> +#include <inttypes.h> +#include <stddef.h> +#include <stdio.h> +#include <sys/time.h> + +#ifndef BITS +# define BITS 32 +# define BACKEND sparc_ +#else +# define BITS 64 +# define BACKEND sparc64_ +#endif +#include "libebl_CPU.h" + +#define GR(at, n, dwreg) \ + { .offset = at * BITS/8, .regno = dwreg, .count = n, .bits = BITS } + +static const Ebl_Register_Location prstatus_regs[] = + { + GR (0, 32, 0), /* %g0-%g7, %o0-%o7, %i0-%i7 */ +#if BITS == 32 + GR (32, 1, 65), /* %psr */ + GR (33, 2, 68), /* %pc, %npc */ + GR (35, 1, 64), /* %y */ + GR (36, 1, 66), /* %wim, %tbr */ +#else + GR (32, 1, 82), /* %state */ + GR (33, 2, 80), /* %pc, %npc */ + GR (35, 1, 85), /* %y */ +#endif + }; +#define PRSTATUS_REGS_SIZE (BITS / 8 * (32 + (BITS == 32 ? 6 : 4))) + +static const Ebl_Register_Location fpregset_regs[] = + { +#if BITS == 32 + GR (0, 32, 32), /* %f0-%f31 */ + /* padding word */ + GR (33, 1, 70), /* %fsr */ + /* qcnt, q_entrysize, en, q, padding */ +# define FPREGSET_SIZE (34 * 4 + 4 + 64 * 4 + 4) +#else + GR (0, 32, 32), /* %f0-%f31 */ + GR (32, 1, 83), /* %fsr */ + /* 33, 1, %gsr */ + GR (34, 1, 84), /* %fprs */ +# define FPREGSET_SIZE (35 * 8) +#endif + }; + +#if BITS == 32 +# define ULONG uint32_t +# define ALIGN_ULONG 4 +# define TYPE_ULONG ELF_T_WORD +# define TYPE_LONG ELF_T_SWORD +# define UID_T uint16_t +# define GID_T uint16_t +# define ALIGN_UID_T 2 +# define ALIGN_GID_T 2 +# define TYPE_UID_T ELF_T_HALF +# define TYPE_GID_T ELF_T_HALF +#else +# define ULONG uint64_t +# define ALIGN_ULONG 8 +# define TYPE_ULONG ELF_T_XWORD +# define TYPE_LONG ELF_T_SXWORD +# define UID_T uint32_t +# define GID_T uint32_t +# define ALIGN_UID_T 4 +# define ALIGN_GID_T 4 +# define TYPE_UID_T ELF_T_WORD +# define TYPE_GID_T ELF_T_WORD +# define SUSECONDS_HALF 1 +#endif +#define PID_T int32_t +#define ALIGN_PID_T 4 +#define TYPE_PID_T ELF_T_SWORD + +#include "linux-core-note.c" diff --git a/backends/sparc_init.c b/backends/sparc_init.c index ba0e08a3..8da845e2 100644 --- a/backends/sparc_init.c +++ b/backends/sparc_init.c @@ -1,5 +1,5 @@ /* Initialization of SPARC specific backend library. - Copyright (C) 2002, 2005, 2006 Red Hat, Inc. + Copyright (C) 2002, 2005, 2006, 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 @@ -34,6 +34,7 @@ /* This defines the common reloc hooks based on sparc_reloc.def. */ #include "common-reloc.c" +extern __typeof (EBLHOOK (core_note)) sparc64_core_note attribute_hidden; const char * sparc_init (elf, machine, eh, ehlen) @@ -55,7 +56,12 @@ sparc_init (elf, machine, eh, ehlen) eh->name = "SPARC"; sparc_init_reloc (eh); HOOK (eh, reloc_simple_type); - //HOOK (eh, core_note); + HOOK (eh, machine_flag_check); + if (eh->class == ELFCLASS64) + eh->core_note = sparc64_core_note; + else + HOOK (eh, core_note); + HOOK (eh, auxv_info); HOOK (eh, register_info); HOOK (eh, return_value_location); diff --git a/backends/sparc_symbol.c b/backends/sparc_symbol.c index 3a261a00..237620c9 100644 --- a/backends/sparc_symbol.c +++ b/backends/sparc_symbol.c @@ -1,5 +1,5 @@ /* SPARC specific symbolic name handling. - Copyright (C) 2002, 2003, 2005 Red Hat, Inc. + Copyright (C) 2002, 2003, 2005, 2007 Red Hat, Inc. This file is part of Red Hat elfutils. Written by Jakub Jelinek <jakub@redhat.com>, 2002. @@ -55,3 +55,14 @@ sparc_reloc_simple_type (Ebl *ebl __attribute__ ((unused)), int type) return ELF_T_NUM; } } + +/* Check whether machine flags are valid. */ +bool +sparc_machine_flag_check (GElf_Word flags) +{ + return ((flags &~ (EF_SPARCV9_MM + | EF_SPARC_LEDATA + | EF_SPARC_32PLUS + | EF_SPARC_SUN_US1 + | EF_SPARC_SUN_US3)) == 0); +} diff --git a/libdw/ChangeLog b/libdw/ChangeLog index 34c4e5b1..d21f5d64 100644 --- a/libdw/ChangeLog +++ b/libdw/ChangeLog @@ -1,3 +1,10 @@ +2007-10-05 Roland McGrath <roland@redhat.com> + + * dwarf_begin_elf.c (check_section): Punt on SHT_NOBITS sections. + + * libdw.h (__extern_inline): Rename to __libdw_extern_inline. + [__OPTIMIZE__] (dwarf_whatattr, dwarf_whatform): Update uses. + 2007-10-03 Roland McGrath <roland@redhat.com> * libdw.map (ELFUTILS_0.130: Add dwfl_build_id_find_elf diff --git a/libdw/dwarf_begin_elf.c b/libdw/dwarf_begin_elf.c index e6ead614..aaac3999 100644 --- a/libdw/dwarf_begin_elf.c +++ b/libdw/dwarf_begin_elf.c @@ -97,6 +97,11 @@ check_section (Dwarf *result, GElf_Ehdr *ehdr, Elf_Scn *scn, bool inscngrp) wrong in the libelf library. */ abort (); + /* Ignore any SHT_NOBITS sections. Debugging sections should not + have been stripped, but in case of a corrupt file we won't try + to look at the missing data. */ + if (unlikely (shdr->sh_type == SHT_NOBITS)) + return result; /* Make sure the section is part of a section group only iff we really need it. If we are looking for the global (= non-section diff --git a/libdw/libdw.h b/libdw/libdw.h index 53853127..70a35b04 100644 --- a/libdw/libdw.h +++ b/libdw/libdw.h @@ -62,9 +62,9 @@ #endif #ifdef __GNUC_STDC_INLINE__ -# define __extern_inline extern __inline __attribute__ ((__gnu_inline__)) +# define __libdw_extern_inline extern __inline __attribute__ ((__gnu_inline__)) #else -# define __extern_inline extern __inline +# define __libdw_extern_inline extern __inline #endif @@ -630,14 +630,14 @@ extern Dwarf_OOM dwarf_new_oom_handler (Dwarf *dbg, Dwarf_OOM handler); /* Inline optimizations. */ #ifdef __OPTIMIZE__ /* Return attribute code of given attribute. */ -__extern_inline unsigned int +__libdw_extern_inline unsigned int dwarf_whatattr (Dwarf_Attribute *attr) { return attr == NULL ? 0 : attr->code; } /* Return attribute code of given attribute. */ -__extern_inline unsigned int +__libdw_extern_inline unsigned int dwarf_whatform (Dwarf_Attribute *attr) { return attr == NULL ? 0 : attr->form; diff --git a/libdwfl/ChangeLog b/libdwfl/ChangeLog index d682f8f9..3c56f177 100644 --- a/libdwfl/ChangeLog +++ b/libdwfl/ChangeLog @@ -1,3 +1,68 @@ +2007-10-09 Roland McGrath <roland@redhat.com> + + * dwfl_report_elf.c (__libdwfl_report_elf): Clear SHDR->sh_offset when + caching SHDR->sh_addr = 0. + * offline.c (dwfl_offline_section_address): Never called for sh_addr + really at 0, don't check for it. Use MOD->debug directly, not symfile. + + * dwfl_module_getdwarf.c (load_symtab): Return success properly when + we've found SHT_SYMTAB. + + * relocate.c (relocate_getsym): New function. + (__libdwfl_relocate): Use it. + (__libdwfl_relocate_value): Take new Elf * argument. Make SYMSHSTRNDX + be a pointer instead of value; cache getshstrndx result there. + * libdwflP.h: Update decl. + * derelocate.c (cache_sections): Update caller. + Always work on the main file, not the symfile. + (dwfl_module_address_section): Likewise. + * dwfl_module_getsym.c (dwfl_module_getsym): Update caller. + +2007-10-07 Roland McGrath <roland@redhat.com> + + * offline.c (process_archive): Initialize MOD. + + * linux-kernel-modules.c (get_release): New function, broken out of ... + (report_kernel): ... here. Call it. + (try_kernel_name): Take new arg TRY_DEBUG, only try ".debug" if set. + (find_kernel_elf): Update caller. + (report_kernel_archive): New function. + (dwfl_linux_kernel_report_offline): Call it. + + * offline.c (process_file): Take new arg PREDICATE, pass it down. + (process_archive): Likewise. + (process_archive_member): Likewise. When nonnull, let the predicate + decide whether to use this member. + (__libdwfl_report_offline): New function, broken out of ... + (dwfl_report_offline): ... here. Call it. + * libdwflP.h: Declare it. + + * offline.c (process_archive, process_archive_member): New functions. + (process_elf, process_file): New functions, broken out of ... + (dwfl_report_offline): ... here. Call process_file, which recurses on + ELF_K_AR files. + + * dwfl_report_elf.c (__libdwfl_report_elf): New, broken out of ... + (dwfl_report_elf): ... here. Call it. + * libdwflP.h: Declare it. + +2007-10-06 Roland McGrath <roland@redhat.com> + + * derelocate.c (dwfl_module_relocations): Don't call + dwfl_module_getdwarf. + + * derelocate.c (find_section): Use __libdwfl_seterrno, not + __libdw_seterrno. + + * relocate.c (__libdwfl_relocate_value): Abuse sh_offset, not + SHF_ALLOC, to cache sh_addr resolved to 0. + + * dwfl_report_elf.c (dwfl_report_elf): When an ET_REL file has sh_addr + values nonzero already, just use its existing layout. + + * relocate.c (__libdwfl_relocate): Clear size of reloc section in its + in-core shdr after applying it. + 2007-10-04 Ulrich Drepper <drepper@redhat.com> * linux-kernel-modules.c (dwfl_linux_kernel_report_kernel): Fake diff --git a/libdwfl/derelocate.c b/libdwfl/derelocate.c index cd01a7e8..6da999d3 100644 --- a/libdwfl/derelocate.c +++ b/libdwfl/derelocate.c @@ -88,46 +88,48 @@ compare_secrefs (const void *a, const void *b) static int cache_sections (Dwfl_Module *mod) { - size_t symshstrndx; - if (elf_getshstrndx (mod->symfile->elf, &symshstrndx) < 0) + struct secref *refs = NULL; + size_t nrefs = 0; + + size_t shstrndx; + if (unlikely (elf_getshstrndx (mod->main.elf, &shstrndx) < 0)) { + elf_error: __libdwfl_seterrno (DWFL_E_LIBELF); return -1; } - struct secref *refs = NULL; - size_t nrefs = 0; - Elf_Scn *scn = NULL; - while ((scn = elf_nextscn (mod->symfile->elf, scn)) != NULL) + while ((scn = elf_nextscn (mod->main.elf, scn)) != NULL) { GElf_Shdr shdr_mem; GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); if (shdr == NULL) - return -1; + goto elf_error; if ((shdr->sh_flags & SHF_ALLOC) && shdr->sh_addr == 0) { /* This section might not yet have been looked at. */ - if (__libdwfl_relocate_value (mod, symshstrndx, elf_ndxscn (scn), + if (__libdwfl_relocate_value (mod, mod->main.elf, &shstrndx, + elf_ndxscn (scn), &shdr->sh_addr) != DWFL_E_NOERROR) continue; shdr = gelf_getshdr (scn, &shdr_mem); - if (shdr == NULL) - return -1; + if (unlikely (shdr == NULL)) + goto elf_error; } if (shdr->sh_flags & SHF_ALLOC) { - const char *name = elf_strptr (mod->symfile->elf, symshstrndx, + const char *name = elf_strptr (mod->main.elf, shstrndx, shdr->sh_name); - if (name == NULL) - return -1; + if (unlikely (name == NULL)) + goto elf_error; struct secref *newref = alloca (sizeof *newref); newref->scn = scn; newref->name = name; - newref->start = shdr->sh_addr + mod->symfile->bias; + newref->start = shdr->sh_addr + mod->main.bias; newref->end = newref->start + shdr->sh_size; newref->next = refs; refs = newref; @@ -171,13 +173,6 @@ dwfl_module_relocations (Dwfl_Module *mod) if (mod->reloc_info != NULL) return mod->reloc_info->count; - if (mod->dw == NULL) - { - Dwarf_Addr bias; - if (INTUSE(dwfl_module_getdwarf) (mod, &bias) == NULL) - return -1; - } - switch (mod->e_type) { case ET_REL: @@ -295,7 +290,7 @@ find_section (Dwfl_Module *mod, Dwarf_Addr *addr) } } - __libdw_seterrno (DWARF_E_NO_MATCH); + __libdwfl_seterrno (DWFL_E (LIBDW, DWARF_E_NO_MATCH)); return -1; } @@ -326,6 +321,6 @@ dwfl_module_address_section (Dwfl_Module *mod, Dwarf_Addr *address, if (idx < 0) return NULL; - *bias = mod->symfile->bias; + *bias = mod->main.bias; return mod->reloc_info->refs[idx].scn; } diff --git a/libdwfl/dwfl_module_getdwarf.c b/libdwfl/dwfl_module_getdwarf.c index 90c77c86..0f26e5dc 100644 --- a/libdwfl/dwfl_module_getdwarf.c +++ b/libdwfl/dwfl_module_getdwarf.c @@ -205,7 +205,9 @@ find_debuginfo (Dwfl_Module *mod) } -/* Try to find a symbol table in FILE. */ +/* Try to find a symbol table in FILE. + Returns DWFL_E_NOERROR if a proper one is found. + Returns DWFL_E_NO_SYMTAB if not, but still sets results for SHT_DYNSYM. */ static Dwfl_Error load_symtab (struct dwfl_file *file, struct dwfl_file **symfile, Elf_Scn **symscn, Elf_Scn **xndxscn, @@ -223,7 +225,7 @@ load_symtab (struct dwfl_file *file, struct dwfl_file **symfile, *symfile = file; *strshndx = shdr->sh_link; *syments = shdr->sh_size / shdr->sh_entsize; - if (*symscn != NULL && *xndxscn != NULL) + if (*xndxscn != NULL) return DWFL_E_NOERROR; break; @@ -237,12 +239,22 @@ load_symtab (struct dwfl_file *file, struct dwfl_file **symfile, case SHT_SYMTAB_SHNDX: *xndxscn = scn; + if (*symscn != NULL) + return DWFL_E_NOERROR; break; default: break; } } + + if (*symscn != NULL) + /* We found one, though no SHT_SYMTAB_SHNDX to go with it. */ + return DWFL_E_NOERROR; + + /* We found no SHT_SYMTAB, so any SHT_SYMTAB_SHNDX was bogus. + We might have found an SHT_DYNSYM and set *SYMSCN et al though. */ + *xndxscn = NULL; return DWFL_E_NO_SYMTAB; } diff --git a/libdwfl/dwfl_module_getsym.c b/libdwfl/dwfl_module_getsym.c index 0c076e87..04432a43 100644 --- a/libdwfl/dwfl_module_getsym.c +++ b/libdwfl/dwfl_module_getsym.c @@ -1,5 +1,5 @@ /* Find debugging and symbol information for a module in libdwfl. - Copyright (C) 2006 Red Hat, Inc. + Copyright (C) 2006,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 @@ -92,11 +92,10 @@ dwfl_module_getsym (Dwfl_Module *mod, int ndx, { /* In an ET_REL file, the symbol table values are relative to the section, not to the module's load base. */ - size_t symshstrndx; - Dwfl_Error result = DWFL_E_LIBELF; - if (elf_getshstrndx (mod->symfile->elf, &symshstrndx) == 0) - result = __libdwfl_relocate_value (mod, symshstrndx, - shndx, &sym->st_value); + size_t symshstrndx = SHN_UNDEF; + Dwfl_Error result = __libdwfl_relocate_value (mod, mod->symfile->elf, + &symshstrndx, + shndx, &sym->st_value); if (unlikely (result != DWFL_E_NOERROR)) { __libdwfl_seterrno (result); diff --git a/libdwfl/dwfl_report_elf.c b/libdwfl/dwfl_report_elf.c index 03835386..905ddd12 100644 --- a/libdwfl/dwfl_report_elf.c +++ b/libdwfl/dwfl_report_elf.c @@ -1,5 +1,5 @@ /* Report a module to libdwfl based on ELF program headers. - Copyright (C) 2005 Red Hat, Inc. + Copyright (C) 2005, 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 @@ -51,33 +51,16 @@ #include <fcntl.h> #include <unistd.h> - Dwfl_Module * -dwfl_report_elf (Dwfl *dwfl, const char *name, - const char *file_name, int fd, GElf_Addr base) +internal_function +__libdwfl_report_elf (Dwfl *dwfl, const char *name, const char *file_name, + int fd, Elf *elf, GElf_Addr base) { - bool closefd = false; - - if (fd < 0) - { - fd = open64 (file_name, O_RDONLY); - if (fd < 0) - { - __libdwfl_seterrno (DWFL_E_ERRNO); - return NULL; - } - closefd = true; - } - - Elf *elf = elf_begin (fd, ELF_C_READ_MMAP_PRIVATE, NULL); - GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr (elf, &ehdr_mem); if (ehdr == NULL) { elf_error: __libdwfl_seterrno (DWFL_E_LIBELF); - if (closefd) - close (fd); return NULL; } @@ -102,23 +85,39 @@ dwfl_report_elf (Dwfl *dwfl, const char *name, if (shdr->sh_flags & SHF_ALLOC) { const GElf_Xword align = shdr->sh_addralign ?: 1; - shdr->sh_addr = (end + align - 1) & -align; - if (end == base) - /* This is the first section assigned a location. - Use its aligned address as the module's base. */ - start = shdr->sh_addr; - end = shdr->sh_addr + shdr->sh_size; - if (! gelf_update_shdr (scn, shdr)) - goto elf_error; + if (shdr->sh_addr == 0 || (bias == 0 && end > start)) + { + shdr->sh_addr = (end + align - 1) & -align; + if (end == base) + /* This is the first section assigned a location. + Use its aligned address as the module's base. */ + start = shdr->sh_addr; + end = shdr->sh_addr + shdr->sh_size; + if (shdr->sh_addr == 0) + /* This is a marker that this was resolved to zero, + to prevent a callback. */ + shdr->sh_offset = 0; + if (! gelf_update_shdr (scn, shdr)) + goto elf_error; + } + else + { + if (bias == 0 || end < shdr->sh_addr + shdr->sh_size) + end = shdr->sh_addr + shdr->sh_size; + if (bias == 0 || bias > shdr->sh_addr) + bias = shdr->sh_addr; + } } } - if (end == start) + if (bias != 0) { - __libdwfl_seterrno (DWFL_E_BADELF); - if (closefd) - close (fd); - return NULL; + /* The section headers had nonzero sh_addr values. The layout + was already done. We've just collected the total span. + Now just compute the bias from the requested base. */ + start = base; + end = end - bias + start; + bias -= start; } break; @@ -161,8 +160,6 @@ dwfl_report_elf (Dwfl *dwfl, const char *name, if (end == 0) { __libdwfl_seterrno (DWFL_E_NO_PHDR); - if (closefd) - close (fd); return NULL; } break; @@ -181,8 +178,6 @@ dwfl_report_elf (Dwfl *dwfl, const char *name, { elf_end (elf); overlap: - if (closefd) - close (fd); m->gc = true; __libdwfl_seterrno (DWFL_E_OVERLAP); m = NULL; @@ -204,4 +199,33 @@ dwfl_report_elf (Dwfl *dwfl, const char *name, } return m; } + +Dwfl_Module * +dwfl_report_elf (Dwfl *dwfl, const char *name, + const char *file_name, int fd, GElf_Addr base) +{ + bool closefd = false; + if (fd < 0) + { + closefd = true; + fd = open64 (file_name, O_RDONLY); + if (fd < 0) + { + __libdwfl_seterrno (DWFL_E_ERRNO); + return NULL; + } + } + + Elf *elf = elf_begin (fd, ELF_C_READ_MMAP_PRIVATE, NULL); + Dwfl_Module *mod = __libdwfl_report_elf (dwfl, name, file_name, + fd, elf, base); + if (mod == NULL) + { + elf_end (elf); + if (closefd) + close (fd); + } + + return mod; +} INTDEF (dwfl_report_elf) diff --git a/libdwfl/libdwflP.h b/libdwfl/libdwflP.h index 4b458e1d..e75a3ab6 100644 --- a/libdwfl/libdwflP.h +++ b/libdwfl/libdwflP.h @@ -237,8 +237,8 @@ extern Dwfl_Error __libdwfl_relocate (Dwfl_Module *mod, Elf *debugfile) /* Adjust *VALUE from section-relative to absolute. MOD->dwfl->callbacks->section_address is called to determine the actual address of a loaded section. */ -extern Dwfl_Error __libdwfl_relocate_value (Dwfl_Module *mod, - size_t m_shstrndx, +extern Dwfl_Error __libdwfl_relocate_value (Dwfl_Module *mod, Elf *elf, + size_t *shstrndx_cache, Elf32_Word shndx, GElf_Addr *value) internal_function; @@ -277,6 +277,21 @@ extern uint32_t __libdwfl_crc32 (uint32_t crc, unsigned char *buf, size_t len) extern int __libdwfl_crc32_file (int fd, uint32_t *resp) attribute_hidden; +/* Meat of dwfl_report_elf, given elf_begin just called. + Consumes ELF on success, not on failure. */ +extern Dwfl_Module *__libdwfl_report_elf (Dwfl *dwfl, const char *name, + const char *file_name, int fd, + Elf *elf, GElf_Addr base) + internal_function; + +/* Meat of dwfl_report_offline. */ +extern Dwfl_Module *__libdwfl_report_offline (Dwfl *dwfl, const char *name, + const char *file_name, + int fd, bool closefd, + int (*predicate) (const char *, + const char *)) + internal_function; + /* Avoid PLT entries. */ INTDECL (dwfl_begin) diff --git a/libdwfl/linux-kernel-modules.c b/libdwfl/linux-kernel-modules.c index 8954b2fe..c15e0896 100644 --- a/libdwfl/linux-kernel-modules.c +++ b/libdwfl/linux-kernel-modules.c @@ -77,7 +77,7 @@ /* Try to open the given file as it is or under the debuginfo directory. */ static int -try_kernel_name (Dwfl *dwfl, char **fname) +try_kernel_name (Dwfl *dwfl, char **fname, bool try_debug) { if (*fname == NULL) return -1; @@ -97,7 +97,7 @@ try_kernel_name (Dwfl *dwfl, char **fname) fd = INTUSE(dwfl_standard_find_debuginfo) (&fakemod, NULL, NULL, 0, *fname, basename (*fname), 0, &debugfname); - if (fd < 0) + if (fd < 0 && try_debug) /* Next, let the call use the default of basename + ".debug", to look for "vmlinux.debug" files. */ fd = INTUSE(dwfl_standard_find_debuginfo) (&fakemod, NULL, NULL, 0, @@ -128,21 +128,20 @@ find_kernel_elf (Dwfl *dwfl, const char *release, char **fname) : asprintf (fname, "/boot/vmlinux-%s", release)) < 0) return -1; - int fd = try_kernel_name (dwfl, fname); + int fd = try_kernel_name (dwfl, fname, true); if (fd < 0 && release[0] != '/') { free (*fname); if (asprintf (fname, MODULEDIRFMT "/vmlinux", release) < 0) return -1; - fd = try_kernel_name (dwfl, fname); + fd = try_kernel_name (dwfl, fname, true); } return fd; } static int -report_kernel (Dwfl *dwfl, const char **release, - int (*predicate) (const char *module, const char *file)) +get_release (Dwfl *dwfl, const char **release) { if (dwfl == NULL) return -1; @@ -157,10 +156,20 @@ report_kernel (Dwfl *dwfl, const char **release, *release = release_string; } + return 0; +} + +static int +report_kernel (Dwfl *dwfl, const char **release, + int (*predicate) (const char *module, const char *file)) +{ + int result = get_release (dwfl, release); + if (unlikely (result != 0)) + return result; + char *fname; - int fd = find_kernel_elf (dwfl, release_string, &fname); + int fd = find_kernel_elf (dwfl, *release, &fname); - int result = 0; if (fd < 0) result = ((predicate != NULL && !(*predicate) (KERNEL_MODNAME, NULL)) ? 0 : errno ?: ENOENT); @@ -197,6 +206,34 @@ report_kernel (Dwfl *dwfl, const char **release, return result; } +/* Look for a kernel debug archive. If we find one, report all its modules. + If not, return ENOENT. */ +static int +report_kernel_archive (Dwfl *dwfl, const char **release, + int (*predicate) (const char *module, const char *file)) +{ + int result = get_release (dwfl, release); + if (unlikely (result != 0)) + return result; + + char *archive; + if (unlikely ((*release)[0] == '/' + ? asprintf (&archive, "%s/debug.a", *release) + : asprintf (&archive, MODULEDIRFMT "/debug.a", *release)) < 0) + return ENOMEM; + + int fd = try_kernel_name (dwfl, &archive, false); + if (fd < 0) + result = errno ?: ENOENT; + else + /* We have the archive file open! */ + result = __libdwfl_report_offline (dwfl, NULL, archive, fd, true, + predicate) == NULL ? -1 : 0; + + free (archive); + return result; +} + /* Report a kernel and all its modules found on disk, for offline use. If RELEASE starts with '/', it names a directory to look in; if not, it names a directory to find under /lib/modules/; @@ -208,8 +245,12 @@ dwfl_linux_kernel_report_offline (Dwfl *dwfl, const char *release, int (*predicate) (const char *module, const char *file)) { + int result = report_kernel_archive (dwfl, &release, predicate); + if (result != ENOENT) + return result; + /* First report the kernel. */ - int result = report_kernel (dwfl, &release, predicate); + result = report_kernel (dwfl, &release, predicate); if (result == 0) { /* Do "find /lib/modules/RELEASE -name *.ko". */ diff --git a/libdwfl/offline.c b/libdwfl/offline.c index 0a0645ef..916d263e 100644 --- a/libdwfl/offline.c +++ b/libdwfl/offline.c @@ -48,13 +48,13 @@ <http://www.openinventionnetwork.com>. */ #include "libdwflP.h" +#include <fcntl.h> #include <unistd.h> /* Since dwfl_report_elf lays out the sections already, this will only be called when the section headers of the debuginfo file are being - consulted instead, or for a section located at zero. With binutils - strip-to-debug, the symbol table is in the debuginfo file and relocation - looks there. */ + consulted instead. With binutils strip-to-debug, the symbol table is in + the debuginfo file and relocation looks there. */ int dwfl_offline_section_address (Dwfl_Module *mod, void **userdata __attribute__ ((unused)), @@ -69,21 +69,13 @@ dwfl_offline_section_address (Dwfl_Module *mod, assert (shdr->sh_addr == 0); assert (shdr->sh_flags & SHF_ALLOC); - if (mod->symfile == &mod->main) - { - /* Because the actual address is zero, we failed to notice - we in fact had the right address cached already. */ - *addr = 0; - return 0; - } - /* The section numbers might not match between the two files. The best we can rely on is the order of SHF_ALLOC sections. */ - Elf_Scn *ourscn = elf_getscn (mod->symfile->elf, shndx); + Elf_Scn *ourscn = elf_getscn (mod->debug.elf, shndx); Elf_Scn *scn = NULL; uint_fast32_t skip_alloc = 0; - while ((scn = elf_nextscn (mod->symfile->elf, scn)) != ourscn) + while ((scn = elf_nextscn (mod->debug.elf, scn)) != ourscn) { assert (scn != NULL); GElf_Shdr shdr_mem; @@ -98,8 +90,7 @@ dwfl_offline_section_address (Dwfl_Module *mod, while ((scn = elf_nextscn (mod->main.elf, scn)) != NULL) { GElf_Shdr shdr_mem; - GElf_Shdr *main_shdr = gelf_getshdr (elf_getscn (mod->main.elf, shndx), - &shdr_mem); + GElf_Shdr *main_shdr = gelf_getshdr (scn, &shdr_mem); if (unlikely (main_shdr == NULL)) return -1; if ((main_shdr->sh_flags & SHF_ALLOC) && skip_alloc-- == 0) @@ -115,15 +106,42 @@ dwfl_offline_section_address (Dwfl_Module *mod, } INTDEF (dwfl_offline_section_address) -Dwfl_Module * -dwfl_report_offline (Dwfl *dwfl, const char *name, - const char *file_name, int fd) +/* Forward declarations. */ +static Dwfl_Module *process_elf (Dwfl *dwfl, const char *name, + const char *file_name, int fd, Elf *elf); +static Dwfl_Module *process_archive (Dwfl *dwfl, const char *name, + const char *file_name, int fd, Elf *elf, + int (*predicate) (const char *module, + const char *file)); + +/* Report one module for an ELF file, or many for an archive. */ +static Dwfl_Module * +process_file (Dwfl *dwfl, const char *name, const char *file_name, int fd, + Elf *elf, int (*predicate) (const char *module, + const char *file)) { - if (dwfl == NULL) - return NULL; + switch (elf_kind (elf)) + { + default: + case ELF_K_NONE: + __libdwfl_seterrno (elf == NULL ? DWFL_E_LIBELF : DWFL_E_BADELF); + return NULL; + + case ELF_K_ELF: + return process_elf (dwfl, name, file_name, fd, elf); + + case ELF_K_AR: + return process_archive (dwfl, name, file_name, fd, elf, predicate); + } +} - Dwfl_Module *mod = INTUSE(dwfl_report_elf) (dwfl, name, file_name, fd, - dwfl->offline_next_address); +/* Report the open ELF file as a module. */ +static Dwfl_Module * +process_elf (Dwfl *dwfl, const char *name, const char *file_name, int fd, + Elf *elf) +{ + Dwfl_Module *mod = __libdwfl_report_elf (dwfl, name, file_name, fd, elf, + dwfl->offline_next_address); if (mod != NULL) { /* If this is an ET_EXEC file with fixed addresses, the address range @@ -147,4 +165,139 @@ dwfl_report_offline (Dwfl *dwfl, const char *name, return mod; } + +static bool +process_archive_member (Dwfl *dwfl, const char *name, const char *file_name, + int (*predicate) (const char *module, const char *file), + Elf *member, Dwfl_Module **mod) +{ + const Elf_Arhdr *h = elf_getarhdr (member); + if (unlikely (h == NULL)) + { + __libdwfl_seterrno (DWFL_E_LIBELF); + elf_end (member); + *mod = NULL; + return false; + } + + if (!strcmp (h->ar_name, "/") || !strcmp (h->ar_name, "//")) + { + elf_end (member); + return true; + } + + char *member_name; + if (unlikely (asprintf (&member_name, "%s(%s)", file_name, h->ar_name) < 0)) + { + nomem: + __libdwfl_seterrno (DWFL_E_NOMEM); + elf_end (member); + *mod = NULL; + return false; + } + + char *module_name = NULL; + if (name == NULL || name[0] == '\0') + name = h->ar_name; + else if (unlikely (asprintf (&module_name, "%s:%s", name, h->ar_name) < 0)) + { + free (member_name); + goto nomem; + } + else + name = module_name; + + if (predicate != NULL) + { + /* Let the predicate decide whether to use this one. */ + int want = (*predicate) (name, member_name); + if (want <= 0) + { + free (member_name); + free (module_name); + if (unlikely (want < 0)) + { + __libdwfl_seterrno (DWFL_E_CB); + elf_end (member); + *mod = NULL; + return false; + } + return true; + } + } + + *mod = process_file (dwfl, name, member_name, -1, member, predicate); + free (member_name); + free (module_name); + return *mod != NULL; +} + +/* Report each member of the archive as its own module. */ +static Dwfl_Module * +process_archive (Dwfl *dwfl, const char *name, const char *file_name, int fd, + Elf *archive, + int (*predicate) (const char *module, const char *file)) + +{ + Dwfl_Module *mod = NULL; + bool more = true; + do + { + Elf *member = elf_begin (-1, ELF_C_READ_MMAP_PRIVATE, archive); + if (process_archive_member (dwfl, name, file_name, predicate, + member, &mod)) + /* Advance the archive-reading offset for the next iteration. */ + more = elf_next (member) != ELF_C_NULL; + else if (mod == NULL) + { + elf_end (member); + break; + } + } + while (more); + elf_end (archive); + if (likely (mod != NULL)) + close (fd); + return mod; +} + +Dwfl_Module * +internal_function +__libdwfl_report_offline (Dwfl *dwfl, const char *name, + const char *file_name, int fd, bool closefd, + int (*predicate) (const char *module, + const char *file)) +{ + Elf *elf = elf_begin (fd, ELF_C_READ_MMAP_PRIVATE, NULL); + Dwfl_Module *mod = process_file (dwfl, name, file_name, fd, elf, predicate); + if (mod == NULL) + { + elf_end (elf); + if (closefd) + close (fd); + } + return mod; +} + +Dwfl_Module * +dwfl_report_offline (Dwfl *dwfl, const char *name, + const char *file_name, int fd) +{ + if (dwfl == NULL) + return NULL; + + bool closefd = false; + if (fd < 0) + { + closefd = true; + fd = open64 (file_name, O_RDONLY); + if (fd < 0) + { + __libdwfl_seterrno (DWFL_E_ERRNO); + return NULL; + } + } + + return __libdwfl_report_offline (dwfl, name, file_name, fd, closefd, NULL); +} INTDEF (dwfl_report_offline) diff --git a/libdwfl/relocate.c b/libdwfl/relocate.c index f37f8350..e355af0c 100644 --- a/libdwfl/relocate.c +++ b/libdwfl/relocate.c @@ -56,21 +56,27 @@ typedef uint8_t GElf_Byte; Dwfl_Error internal_function -__libdwfl_relocate_value (Dwfl_Module *mod, size_t symshstrndx, +__libdwfl_relocate_value (Dwfl_Module *mod, Elf *elf, size_t *shstrndx, Elf32_Word shndx, GElf_Addr *value) { - Elf_Scn *refscn = elf_getscn (mod->symfile->elf, shndx); + Elf_Scn *refscn = elf_getscn (elf, shndx); GElf_Shdr refshdr_mem, *refshdr = gelf_getshdr (refscn, &refshdr_mem); if (refshdr == NULL) return DWFL_E_LIBELF; - if ((refshdr->sh_flags & SHF_ALLOC) && refshdr->sh_addr == 0) + if (refshdr->sh_addr == 0 + && (refshdr->sh_flags & SHF_ALLOC) + && refshdr->sh_offset != 0) { /* This is a loaded section. Find its actual address and update the section header. */ - const char *name = elf_strptr (mod->symfile->elf, symshstrndx, - refshdr->sh_name); - if (name == NULL) + + if (*shstrndx == SHN_UNDEF + && unlikely (elf_getshstrndx (elf, shstrndx) < 0)) + return DWFL_E_LIBELF; + + const char *name = elf_strptr (elf, *shstrndx, refshdr->sh_name); + if (unlikely (name == NULL)) return DWFL_E_LIBELF; if ((*mod->dwfl->callbacks->section_address) (MODCB_ARGS (mod), @@ -79,18 +85,17 @@ __libdwfl_relocate_value (Dwfl_Module *mod, size_t symshstrndx, return CBFAIL; if (refshdr->sh_addr == (Dwarf_Addr) -1l) - { - /* The callback indicated this section wasn't really loaded but we - don't really care. Mark it so we don't check it again for the - next relocation. */ - refshdr->sh_flags &= ~SHF_ALLOC; - refshdr->sh_addr = 0; /* Make no adjustment below. */ - } + /* The callback indicated this section wasn't really loaded but we + don't really care. */ + refshdr->sh_addr = 0; /* Make no adjustment below. */ + + /* Mark it so we don't check it again for the next relocation. */ + refshdr->sh_offset = 0; /* Update the in-core file's section header to show the final load address (or unloadedness). This serves as a cache, so we won't get here again for the same section. */ - if (! gelf_update_shdr (refscn, refshdr)) + if (unlikely (! gelf_update_shdr (refscn, refshdr))) return DWFL_E_LIBELF; } @@ -99,6 +104,79 @@ __libdwfl_relocate_value (Dwfl_Module *mod, size_t symshstrndx, return DWFL_E_NOERROR; } +/* This is just doing dwfl_module_getsym, except that we must always use + the symbol table in RELOCATED itself when it has one, not MOD->symfile. */ +static Dwfl_Error +relocate_getsym (Elf **symelf, Elf_Data **symdata, Elf_Data **symxndxdata, + size_t *symshstrndx, + Dwfl_Module *mod, Elf *relocated, + int symndx, GElf_Sym *sym, GElf_Word *shndx) +{ + if (*symdata == NULL) + { + if (mod->symfile->elf != relocated) + { + /* We have to look up the symbol table in the file we are + relocating, if it has its own. These reloc sections refer to + the symbol table in this file, and a symbol table in the main + file might not match. However, some tools did produce ET_REL + .debug files with relocs but no symtab of their own. */ + Elf_Scn *scn = NULL; + while ((scn = elf_nextscn (relocated, scn)) != NULL) + { + GElf_Shdr shdr_mem, *shdr = gelf_getshdr (scn, &shdr_mem); + if (shdr != NULL) + switch (shdr->sh_type) + { + default: + continue; + case SHT_SYMTAB: + *symelf = relocated; + *symdata = elf_getdata (scn, NULL); + if (unlikely (*symdata == NULL)) + return DWFL_E_LIBELF; + break; + case SHT_SYMTAB_SHNDX: + *symxndxdata = elf_getdata (scn, NULL); + if (unlikely (*symxndxdata == NULL)) + return DWFL_E_LIBELF; + break; + } + if (*symdata != NULL && *symxndxdata != NULL) + break; + } + } + if (*symdata == NULL) + { + /* The symbol table we have already cached is the one from + the file being relocated, so it's what we need. Or else + this is an ET_REL .debug file with no .symtab of its own; + the symbols refer to the section indices in the main file. */ + *symelf = mod->symfile->elf; + *symdata = mod->symdata; + *symxndxdata = mod->symxndxdata; + } + } + + if (unlikely (gelf_getsymshndx (*symdata, *symxndxdata, + symndx, sym, shndx) == NULL)) + return DWFL_E_LIBELF; + + if (sym->st_shndx != SHN_XINDEX) + *shndx = sym->st_shndx; + + switch (*shndx) + { + case SHN_ABS: + case SHN_UNDEF: + case SHN_COMMON: + return DWFL_E_NOERROR; + } + + return __libdwfl_relocate_value (mod, *symelf, symshstrndx, + *shndx, &sym->st_value); +} + Dwfl_Error internal_function __libdwfl_relocate (Dwfl_Module *mod, Elf *debugfile) @@ -110,13 +188,17 @@ __libdwfl_relocate (Dwfl_Module *mod, Elf *debugfile) if (ehdr == NULL) return DWFL_E_LIBELF; - size_t symshstrndx, d_shstrndx; - if (elf_getshstrndx (mod->symfile->elf, &symshstrndx) < 0) + /* Cache used by relocate_getsym. */ + Elf *reloc_symelf = NULL; + Elf_Data *reloc_symdata = NULL; + Elf_Data *reloc_symxndxdata = NULL; + size_t reloc_symshstrndx = SHN_UNDEF; + + size_t d_shstrndx; + if (elf_getshstrndx (debugfile, &d_shstrndx) < 0) return DWFL_E_LIBELF; if (mod->symfile->elf == debugfile) - d_shstrndx = symshstrndx; - else if (elf_getshstrndx (debugfile, &d_shstrndx) < 0) - return DWFL_E_LIBELF; + reloc_symshstrndx = d_shstrndx; /* Look at each section in the debuginfo file, and process the relocation sections for debugging sections. */ @@ -127,7 +209,8 @@ __libdwfl_relocate (Dwfl_Module *mod, Elf *debugfile) GElf_Shdr shdr_mem; GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); - if (shdr->sh_type == SHT_REL || shdr->sh_type == SHT_RELA) + if ((shdr->sh_type == SHT_REL || shdr->sh_type == SHT_RELA) + && shdr->sh_size != 0) { /* It's a relocation section. First, fetch the name of the section these relocations apply to. */ @@ -172,10 +255,14 @@ __libdwfl_relocate (Dwfl_Module *mod, Elf *debugfile) { GElf_Sym sym; GElf_Word shndx; - - if (INTUSE(dwfl_module_getsym) (mod, symndx, - &sym, &shndx) == NULL) - return dwfl_errno (); + Dwfl_Error error = relocate_getsym (&reloc_symelf, + &reloc_symdata, + &reloc_symxndxdata, + &reloc_symshstrndx, + mod, debugfile, + symndx, &sym, &shndx); + if (unlikely (error != DWFL_E_NOERROR)) + return error; if (shndx == SHN_UNDEF || shndx == SHN_COMMON) return DWFL_E_RELUNDEF; @@ -307,6 +394,14 @@ __libdwfl_relocate (Dwfl_Module *mod, Elf *debugfile) } if (result != DWFL_E_NOERROR) break; + + /* Mark this relocation section as being empty now that we have + done its work. This affects unstrip -R, so e.g. it emits an + empty .rela.debug_info along with a .debug_info that has + already been fully relocated. */ + shdr->sh_size = 0; + reldata->d_size = 0; + gelf_update_shdr (scn, shdr); } } diff --git a/libebl/ChangeLog b/libebl/ChangeLog index c93e7047..ae31b8f2 100644 --- a/libebl/ChangeLog +++ b/libebl/ChangeLog @@ -1,3 +1,8 @@ +2007-10-11 Roland McGrath <roland@redhat.com> + + * eblobjnote.c (ebl_object_note): Translate target format (byte-swap) + for NT_GNU_ABI_TAG contents. + 2007-08-22 Roland McGrath <roland@redhat.com> * libebl.h (Ebl_Core_Item): New member `group'. diff --git a/libebl/eblobjnote.c b/libebl/eblobjnote.c index 747fb8e7..836ac8dc 100644 --- a/libebl/eblobjnote.c +++ b/libebl/eblobjnote.c @@ -81,48 +81,60 @@ ebl_object_note (ebl, name, type, descsz, desc) } break; - case NT_VERSION: - if (strcmp (name, "GNU") == 0 && descsz >= 8) + case NT_GNU_ABI_TAG: + if (strcmp (name, "GNU") == 0 && descsz >= 8 && descsz % 4 == 0) { - struct - { - uint32_t os; - uint32_t version[descsz / 4 - 1]; - } *tag = (__typeof (tag)) desc; - - const char *os; - switch (tag->os) + Elf_Data in = { - case ELF_NOTE_OS_LINUX: - os = "Linux"; - break; - - case ELF_NOTE_OS_GNU: - os = "GNU"; - break; - - case ELF_NOTE_OS_SOLARIS2: - os = "Solaris"; - break; - - case ELF_NOTE_OS_FREEBSD: - os = "FreeBSD"; - break; - - default: - os = "???"; - break; - } + .d_version = EV_CURRENT, + .d_type = ELF_T_WORD, + .d_size = descsz, + .d_buf = (void *) desc + }; + uint32_t buf[descsz / 4]; + Elf_Data out = + { + .d_version = EV_CURRENT, + .d_type = ELF_T_WORD, + .d_size = descsz, + .d_buf = buf + }; - printf (gettext (" OS: %s, ABI: "), os); - size_t cnt; - for (cnt = 0; cnt < descsz / 4 - 1; ++cnt) + if (elf32_xlatetom (&out, &in, ebl->data) != NULL) { - if (cnt != 0) - putchar_unlocked ('.'); - printf ("%" PRIu32, tag->version[cnt]); + const char *os; + switch (buf[0]) + { + case ELF_NOTE_OS_LINUX: + os = "Linux"; + break; + + case ELF_NOTE_OS_GNU: + os = "GNU"; + break; + + case ELF_NOTE_OS_SOLARIS2: + os = "Solaris"; + break; + + case ELF_NOTE_OS_FREEBSD: + os = "FreeBSD"; + break; + + default: + os = "???"; + break; + } + + printf (gettext (" OS: %s, ABI: "), os); + for (size_t cnt = 1; cnt < descsz / 4; ++cnt) + { + if (cnt > 1) + putchar_unlocked ('.'); + printf ("%" PRIu32, buf[cnt]); + } + putchar_unlocked ('\n'); } - putchar_unlocked ('\n'); break; } /* FALLTHROUGH */ diff --git a/libelf/ChangeLog b/libelf/ChangeLog index 1532a4cf..247598c7 100644 --- a/libelf/ChangeLog +++ b/libelf/ChangeLog @@ -1,3 +1,9 @@ +2007-10-07 Roland McGrath <roland@redhat.com> + + * elf_begin.c (__libelf_next_arhdr): Fix fencepost error and wrong + member access in terminating name with no trailing /. Trim trailing + spaces when there is no /. + 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. diff --git a/libelf/elf_begin.c b/libelf/elf_begin.c index b68410be..13f965f7 100644 --- a/libelf/elf_begin.c +++ b/libelf/elf_begin.c @@ -812,7 +812,14 @@ __libelf_next_arhdr (elf) if (endp != NULL) endp[-1] = '\0'; else - elf->state.ar.raw_name[16] = '\0'; + { + /* In the old BSD style of archive, there is no / terminator. + Instead, there is space padding at the end of the name. */ + size_t i = 15; + do + elf->state.ar.ar_name[i] = '\0'; + while (i > 0 && elf->state.ar.ar_name[--i] == ' '); + } elf_ar_hdr->ar_name = elf->state.ar.ar_name; } diff --git a/src/ChangeLog b/src/ChangeLog index 4d4fa9aa..ebd729fe 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,114 @@ +2007-10-15 Roland McGrath <roland@redhat.com> + + * make-debug-archive.in: New file. + * Makefile.am (EXTRA_DIST): Add it. + (make-debug-archive): New target. + (bin_SCRIPTS, CLEANFILES): Add it. + +2007-10-10 Roland McGrath <roland@redhat.com> + + * elflint.c (special_sections): Add new attrflag value exact_or_gnuld. + Use it to check MERGE|STRINGS for .debug_str. + (check_sections): Handle exact_or_gnuld. + +2007-10-08 Roland McGrath <roland@redhat.com> + + * readelf.c (handle_core_item): Handle 'T'|0x80 to indicate + 64-bit struct timeval with 32-bit tv_usec. + +2007-10-07 Roland McGrath <roland@redhat.com> + + * readelf.c (check_archive_index): New function. + (process_file): Call it. Change signature to take only fd and name. + Use libdwfl to open the file, then iterate on its modules (multiple + for an archive) to print file name and call process_elf_file. + (main): Update caller. Let process_file do elf_begin. + (count_dwflmod, process_dwflmod, find_no_debuginfo): New functions. + (process_elf_file): Take only Dwfl_Module * argument. + Don't print the file name here. + (print_debug_*_section): Take Dwfl_Module * argument. + (print_debug): Likewise. Update caller. + (format_dwarf_addr): New function. + (print_debug_ranges_section): Use it. + (attr_callback): Likewise. + (print_debug_line_section, print_debug_loc_section): Likewise. + + * readelf.c (print_debug_ranges_section): Translate all strings. + (print_debug_loc_section): Likewise. + + * unstrip.c (copy_elided_sections): Initialize SEC. + + * ar.c (do_oper_insert): Put trailing / on short names. + + * arlib.h (MAX_AR_NAME_LEN): Decrease by one. + + * arlib2.c (arlib_add_long_name): Adjust for header size. + + * arlib.c (arlib_finalize): Pad long name table to keep size even. + + * ar.c (do_oper_insert): Use write_retry for padding write. + + * ar.c (do_oper_insert): Initialize CUR_OFF in no_old case. + Unconditionally set FOUND[CNT]->elf when setting ->mem. + (remember_long_name): New function. + (do_oper_insert): Call it. Correctly use length of basename, + not original name. Don't store long name twice for new member. + +2007-10-06 Roland McGrath <roland@redhat.com> + + * elflint.c (check_note): Skip empty segment. + (check_note_section): Skip empty section. + + * unstrip.c (options, parse_opt, struct arg_info): Grok -R/--relocate. + (handle_output_dir_module, handle_implicit_modules): Pass it down. + (handle_dwfl_module): When set, use ET_REL already loaded by Dwfl. + (compare_alloc_sections): Take new arg REL, ignore address if true. + (compare_sections): Likewise, pass it down. + (compare_sections_rel, compare_sections_nonrel): New functions. + (find_alloc_sections_prelink, copy_elided_sections): Use them + instead of compare_sections. + (sections_match): New function, broken out of ... + (find_alloc_section): ... here. + (copy_elided_sections): Reorganize section match-up logic. + Use sections_match for SHF_ALLOC in ET_REL. + For ET_REL, let the nonzero sh_addr from the debug file dominate. + + * unstrip.c (add_new_section_symbols): Take new arg REL. + When true, do not update section symbol values. + (collect_symbols): Likewise. Update section symbols with address + of chosen output section, not original section. + (check_symtab_section_symbols, copy_elided_sections): Update callers. + + * unstrip.c (compare_alloc_sections): At the same address, preserve + original section order. + + * elflint.c (special_sections): Don't require MERGE|STRINGS for + .debug_str, it didn't always have them with older tools. + + * elflint.c (check_symtab, check_one_reloc): Ignore sh_addr in ET_REL. + +2007-10-05 Roland McGrath <roland@redhat.com> + + * elflint.c (check_symtab): Allow SHN_UNDEF _GLOBAL_OFFSET_TABLE_ in + ET_REL file. + + * elflint.c (check_symtab): For _GLOBAL_OFFSET_TABLE_, diagnose + SHN_UNDEF as "bad section". Use shndx value in messages. + + * elflint.c (special_sections): Add ".debug_str". Decrement namelen + for ".debug" so it matches as a prefix. + (IS_KNOWN_SPECIAL): New macro. + (check_sections): Use it for ".plt" match. Cite wrong SHT_NOBITS + type even under -d, for a .debug* or .shstrtab section. + + * readelf.c (print_ops): Use hex for address operand. + +2007-10-04 Roland McGrath <roland@redhat.com> + + * unstrip.c (copy_elided_sections): Initialize NDX_SECTION element for + .gnu_debuglink section to SHN_UNDEF. Drop STT_SECTION symbols for + sections mapped to SHN_UNDEF. + 2007-10-04 Ulrich Drepper <drepper@redhat.com> * readelf.c (dump_archive_index): Avoid warning about uninitialized diff --git a/src/Makefile.am b/src/Makefile.am index 6444cd13..138be5a3 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -84,6 +84,10 @@ noinst_HEADERS = ld.h symbolhash.h sectionhash.h versionhash.h \ EXTRA_DIST = elf32-i386.script libld_elf_i386.map $(ld_modules) arlib.h ld_modules = i386_ld.c +bin_SCRIPTS = make-debug-archive +EXTRA_DIST += make-debug-archive.in +CLEANFILES = make-debug-archive + if MUDFLAP libmudflap = -lmudflap endif @@ -176,6 +180,17 @@ installcheck-binPROGRAMS: $(bin_PROGRAMS) done; \ done; rm -f c$${pid}_.???; exit $$bad -CLEANFILES = none_ld.os $(ld_modules:.c=.os) *.gcno *.gcda *.gconv +CLEANFILES += none_ld.os $(ld_modules:.c=.os) *.gcno *.gcda *.gconv MAINTAINERCLEANFILES = ldlex.c ldscript.c ldscript.h + + +make-debug-archive: $(srcdir)/make-debug-archive.in + UNSTRIP=$(bindir)/`echo unstrip | sed '$(transform)'`; \ + AR=$(bindir)/`echo ar | sed '$(transform)'`; \ + sed -e "s,@UNSTRIP@,$$UNSTRIP,g" -e "s,@AR@,$$AR,g" \ + -e "s%[@]PACKAGE_NAME[@]%$(PACKAGE_NAME)%g" \ + -e "s%[@]PACKAGE_VERSION[@]%$(PACKAGE_VERSION)%g" \ + $(srcdir)/make-debug-archive.in > $@.new + chmod +x $@.new + mv -f $@.new $@ @@ -884,6 +884,15 @@ write_member (struct armem *memb, off_t *startp, off_t *lenp, Elf *elf, return 0; } +/* Store the name in the long name table if necessary. + Record its offset or -1 if we did not need to use the table. */ +static void +remember_long_name (struct armem *mem, const char *name, size_t namelen) +{ + mem->long_name_off = (namelen > MAX_AR_NAME_LEN + ? arlib_add_long_name (name, namelen) + : -1l); +} static int do_oper_delete (const char *arfname, char **argv, int argc, @@ -963,12 +972,7 @@ do_oper_delete (const char *arfname, char **argv, int argc, arlib_add_symbols (subelf, arfname, arhdr->ar_name, newp->off); /* Remember long file names. */ - size_t ar_namelen = strlen (arhdr->ar_name); - if (ar_namelen > MAX_AR_NAME_LEN) - newp->long_name_off = arlib_add_long_name (arhdr->ar_name, - ar_namelen); - else - newp->long_name_off = -1l; + remember_long_name (newp, arhdr->ar_name, strlen (arhdr->ar_name)); } next: @@ -1087,6 +1091,9 @@ do_oper_insert (int oper, const char *arfname, char **argv, int argc, arlib_init (); + /* Initialize early for no_old case. */ + off_t cur_off = SARMAG; + if (fd == -1) { if (!suppress_create_msg) @@ -1118,7 +1125,6 @@ do_oper_insert (int oper, const char *arfname, char **argv, int argc, /* While iterating over the current content of the archive we must determine a number of things: which archive members to keep, which are replaced, and where to insert the new members. */ - off_t cur_off = SARMAG; Elf_Cmd cmd = ELF_C_READ_MMAP; Elf *subelf; while ((subelf = elf_begin (fd, cmd, elf)) != NULL) @@ -1137,11 +1143,7 @@ do_oper_insert (int oper, const char *arfname, char **argv, int argc, newp->mem = NULL; /* Remember long file names. */ - size_t ar_namelen = strlen (arhdr->ar_name); - if (ar_namelen > MAX_AR_NAME_LEN) - newp->long_name_off = arlib_add_long_name (arhdr->ar_name, ar_namelen); - else - newp->long_name_off = -1l; + remember_long_name (newp, arhdr->ar_name, strlen (arhdr->ar_name)); /* Check whether this is a file we are looking for. */ if (oper != oper_qappend) @@ -1223,17 +1225,13 @@ do_oper_insert (int oper, const char *arfname, char **argv, int argc, for (int cnt = 0; cnt < argc; ++cnt) { const char *bname = basename (argv[cnt]); + size_t bnamelen = strlen (bname); if (found[cnt] == NULL) { found[cnt] = alloca (sizeof (struct armem)); found[cnt]->old_off = -1; - size_t ar_namelen = strlen (argv[cnt]); - if (ar_namelen > MAX_AR_NAME_LEN) - found[cnt]->long_name_off = arlib_add_long_name (bname, - ar_namelen); - else - found[cnt]->long_name_off = -1l; + remember_long_name (found[cnt], bname, bnamelen); } struct stat newst; @@ -1275,14 +1273,12 @@ do_oper_insert (int oper, const char *arfname, char **argv, int argc, printf ("%c - %s\n", found[cnt]->old_off == -1l ? 'a' : 'r', argv[cnt]); -#ifdef DEBUG found[cnt]->elf = newelf; -#endif found[cnt]->sec = newst.st_mtime; found[cnt]->uid = newst.st_uid; found[cnt]->gid = newst.st_gid; found[cnt]->mode = newst.st_mode; - found[cnt]->name = basename (argv[cnt]); + found[cnt]->name = bname; found[cnt]->mem = elf_rawfile (newelf, &found[cnt]->size); if (found[cnt] == NULL || elf_cntl (newelf, ELF_C_FDDONE) != 0) @@ -1291,13 +1287,9 @@ do_oper_insert (int oper, const char *arfname, char **argv, int argc, close (newfd); - /* Remember long file names. */ - size_t bnamelen = strlen (bname); - if (bnamelen > MAX_AR_NAME_LEN) - found[cnt]->long_name_off = arlib_add_long_name (bname, - bnamelen); - else - found[cnt]->long_name_off = -1l; + if (found[cnt]->old_off != -1l) + /* Remember long file names. */ + remember_long_name (found[cnt], bname, bnamelen); } } } @@ -1449,8 +1441,9 @@ do_oper_insert (int oper, const char *arfname, char **argv, int argc, if (all->long_name_off == -1) { size_t namelen = strlen (all->name); - memset (mempcpy (arhdr.ar_name, all->name, namelen), - ' ', sizeof (arhdr.ar_name) - namelen); + char *p = mempcpy (arhdr.ar_name, all->name, namelen); + *p++ = '/'; + memset (p, ' ', sizeof (arhdr.ar_name) - namelen - 1); } else { @@ -1480,7 +1473,7 @@ do_oper_insert (int oper, const char *arfname, char **argv, int argc, /* Pad the file if its size is odd. */ if ((all->size & 1) != 0) - if (write (newfd, "\n", 1) != 1) + if (unlikely (write_retry (newfd, "\n", 1) != 1)) goto nonew_unlink; } else diff --git a/src/arlib.c b/src/arlib.c index 1b8785e4..af98454c 100644 --- a/src/arlib.c +++ b/src/arlib.c @@ -118,6 +118,13 @@ arlib_finalize (void) symtab.longnameslen = obstack_object_size (&symtab.longnamesob); if (symtab.longnameslen != sizeof (struct ar_hdr)) { + if ((symtab.longnameslen & 1) != 0) + { + /* Add one more byte to make length even. */ + obstack_grow (&symtab.longnamesob, "\n", 1); + ++symtab.longnameslen; + } + symtab.longnames = obstack_finish (&symtab.longnamesob); memcpy (&((struct ar_hdr *) symtab.longnames)->ar_size, tmpbuf, diff --git a/src/arlib.h b/src/arlib.h index af8e8e42..fd26d248 100644 --- a/src/arlib.h +++ b/src/arlib.h @@ -35,8 +35,9 @@ #include <sys/types.h> -/* Maximum length of a file name that fits directly into the ar header. */ -#define MAX_AR_NAME_LEN (sizeof (((struct ar_hdr *) NULL)->ar_name)) +/* Maximum length of a file name that fits directly into the ar header. + We cannot use the final byte since a / goes there. */ +#define MAX_AR_NAME_LEN (sizeof (((struct ar_hdr *) NULL)->ar_name) - 1) /* Words matching in size to archive header. */ diff --git a/src/arlib2.c b/src/arlib2.c index 47edb356..7098fec1 100644 --- a/src/arlib2.c +++ b/src/arlib2.c @@ -41,10 +41,10 @@ long int arlib_add_long_name (const char *filename, size_t filenamelen) { - int retval = obstack_object_size (&symtab.longnamesob); + size_t size = obstack_object_size (&symtab.longnamesob); obstack_grow (&symtab.longnamesob, filename, filenamelen); obstack_grow (&symtab.longnamesob, "/\n", 2); - return retval; + return size - sizeof (struct ar_hdr); } diff --git a/src/elflint.c b/src/elflint.c index e855d483..3ddf670f 100644 --- a/src/elflint.c +++ b/src/elflint.c @@ -727,13 +727,14 @@ section [%2d] '%s': symbol %zu: function in COMMON section is nonsense\n"), destshdr = gelf_getshdr (elf_getscn (ebl->elf, xndx), &destshdr_mem); if (destshdr != NULL) { + GElf_Addr sh_addr = (ehdr->e_type == ET_REL ? 0 + : destshdr->sh_addr); if (GELF_ST_TYPE (sym->st_info) != STT_TLS) { if (! ebl_check_special_symbol (ebl, ehdr, sym, name, destshdr)) { - if ((sym->st_value - destshdr->sh_addr) - > destshdr->sh_size) + if (sym->st_value - sh_addr > destshdr->sh_size) { /* GNU ld has severe bugs. When it decides to remove empty sections it leaves symbols referencing them @@ -750,7 +751,7 @@ section [%2d] '%s': symbol %zu: function in COMMON section is nonsense\n"), section [%2d] '%s': symbol %zu: st_value out of bounds\n"), idx, section_name (ebl, idx), cnt); } - else if ((sym->st_value - destshdr->sh_addr + else if ((sym->st_value - sh_addr + sym->st_size) > destshdr->sh_size) ERROR (gettext ("\ section [%2d] '%s': symbol %zu does not fit completely in referenced section [%2d] '%s'\n"), @@ -890,18 +891,24 @@ section [%2d] '%s': symbol %zu: non-local section symbol\n"), destshdr = gelf_getshdr (gotscn, &destshdr_mem); } - const char *sname = (destshdr == NULL ? NULL + const char *sname = ((destshdr == NULL || xndx == SHN_UNDEF) + ? NULL : elf_strptr (ebl->elf, ehdr->e_shstrndx, destshdr->sh_name)); if (sname == NULL) - ERROR (gettext ("\ -section [%2d] '%s': _GLOBAL_OFFSET_TABLE_ symbol refers to bad section\n"), - idx, section_name (ebl, idx)); + { + if (xndx != SHN_UNDEF || ehdr->e_type != ET_REL) + ERROR (gettext ("\ +section [%2d] '%s': _GLOBAL_OFFSET_TABLE_ symbol refers to \ +bad section [%2d]\n"), + idx, section_name (ebl, idx), xndx); + } else if (strcmp (sname, ".got.plt") != 0 && strcmp (sname, ".got") != 0) ERROR (gettext ("\ -section [%2d] '%s': _GLOBAL_OFFSET_TABLE_ symbol refers to '%s' section\n"), - idx, section_name (ebl, idx), sname); +section [%2d] '%s': _GLOBAL_OFFSET_TABLE_ symbol refers to \ +section [%2d] '%s'\n"), + idx, section_name (ebl, idx), xndx, sname); if (destshdr != NULL) { @@ -909,7 +916,8 @@ section [%2d] '%s': _GLOBAL_OFFSET_TABLE_ symbol refers to '%s' section\n"), if (!ebl_check_special_symbol (ebl, ehdr, sym, name, destshdr)) { - if (sym->st_value != destshdr->sh_addr) + if (ehdr->e_type != ET_REL + && sym->st_value != destshdr->sh_addr) /* This test is more strict than the psABIs which usually allow the symbol to be in the middle of the .got section, allowing negative offsets. */ @@ -1307,7 +1315,8 @@ section [%2d] '%s': relocation %zu: only symbol '_GLOBAL_OFFSET_TABLE_' can be u { if (destshdr != NULL && GELF_R_TYPE (r_info) != 0 - && (r_offset - destshdr->sh_addr) >= destshdr->sh_size) + && (r_offset - (ehdr->e_type == ET_REL ? 0 + : destshdr->sh_addr)) >= destshdr->sh_size) ERROR (gettext ("\ section [%2d] '%s': relocation %zu: offset out of bounds\n"), idx, section_name (ebl, idx), cnt); @@ -3086,7 +3095,7 @@ static const struct const char *name; size_t namelen; GElf_Word type; - enum { unused, exact, atleast } attrflag; + enum { unused, exact, atleast, exact_or_gnuld } attrflag; GElf_Word attr; GElf_Word attr2; } special_sections[] = @@ -3096,7 +3105,8 @@ static const struct { ".comment", 8, SHT_PROGBITS, exact, 0, 0 }, { ".data", 6, SHT_PROGBITS, exact, SHF_ALLOC | SHF_WRITE, 0 }, { ".data1", 7, SHT_PROGBITS, exact, SHF_ALLOC | SHF_WRITE, 0 }, - { ".debug", 7, SHT_PROGBITS, exact, 0, 0 }, + { ".debug_str", 11, SHT_PROGBITS, exact_or_gnuld, SHF_MERGE | SHF_STRINGS, 0 }, + { ".debug", 6, SHT_PROGBITS, exact, 0, 0 }, { ".dynamic", 9, SHT_DYNAMIC, atleast, SHF_ALLOC, SHF_WRITE }, { ".dynstr", 8, SHT_STRTAB, exact, SHF_ALLOC, 0 }, { ".dynsym", 8, SHT_DYNSYM, exact, SHF_ALLOC, 0 }, @@ -3132,6 +3142,10 @@ static const struct #define nspecial_sections \ (sizeof (special_sections) / sizeof (special_sections[0])) +#define IS_KNOWN_SPECIAL(idx, string, prefix) \ + (special_sections[idx].namelen == sizeof string - (prefix ? 1 : 0) \ + && !memcmp (special_sections[idx].name, string, \ + sizeof string - (prefix ? 1 : 0))) static void check_sections (Ebl *ebl, GElf_Ehdr *ehdr) @@ -3213,13 +3227,18 @@ cannot get section header for section [%2zu] '%s': %s\n"), char stbuf3[100]; GElf_Word good_type = special_sections[s].type; - if (special_sections[s].namelen == sizeof ".plt" && - !memcmp (special_sections[s].name, ".plt", sizeof ".plt") + if (IS_KNOWN_SPECIAL (s, ".plt", false) && ebl_bss_plt_p (ebl, ehdr)) good_type = SHT_NOBITS; + /* In a debuginfo file, any normal section can be SHT_NOBITS. + This is only invalid for DWARF sections and .shstrtab. */ if (shdr->sh_type != good_type - && !(is_debuginfo && shdr->sh_type == SHT_NOBITS)) + && (shdr->sh_type != SHT_NOBITS + || !is_debuginfo + || IS_KNOWN_SPECIAL (s, ".debug_str", false) + || IS_KNOWN_SPECIAL (s, ".debug", true) + || IS_KNOWN_SPECIAL (s, ".shstrtab", false))) ERROR (gettext ("\ section [%2d] '%s' has wrong type: expected %s, is %s\n"), (int) cnt, scnname, @@ -3228,12 +3247,14 @@ section [%2d] '%s' has wrong type: expected %s, is %s\n"), ebl_section_type_name (ebl, shdr->sh_type, stbuf2, sizeof (stbuf2))); - if (special_sections[s].attrflag == exact) + if (special_sections[s].attrflag == exact + || special_sections[s].attrflag == exact_or_gnuld) { /* Except for the link order and group bit all the other bits should match exactly. */ if ((shdr->sh_flags & ~(SHF_LINK_ORDER | SHF_GROUP)) - != special_sections[s].attr) + != special_sections[s].attr + && (special_sections[s].attrflag == exact || !gnuld)) ERROR (gettext ("\ section [%2zu] '%s' has wrong flags: expected %s, is %s\n"), cnt, scnname, @@ -3665,6 +3686,9 @@ phdr[%d]: no note entries defined for the type of file\n"), /* The p_offset values in a separate debug file are bogus. */ return; + if (phdr->p_filesz == 0) + return; + GElf_Off notes_size = 0; Elf_Data *data = elf_getdata_rawchunk (ebl->elf, phdr->p_offset, phdr->p_filesz, @@ -3683,6 +3707,9 @@ phdr[%d]: no note entries defined for the type of file\n"), static void check_note_section (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Shdr *shdr, int idx) { + if (shdr->sh_size == 0) + return; + Elf_Data *data = elf_getdata (elf_getscn (ebl->elf, idx), NULL); if (data == NULL) { diff --git a/src/make-debug-archive.in b/src/make-debug-archive.in new file mode 100644 index 00000000..c3fcbce4 --- /dev/null +++ b/src/make-debug-archive.in @@ -0,0 +1,132 @@ +#!/bin/sh +# +# Script to make an offline archive for debugging with libdwfl-based tools. +# +# make-debug-archive ARCHIVE {options} +# make-debug-archive --kernel [--force] [RELEASE] +# +# Valid options are those listed under 'Input selection options' +# by running @UNSTRIP@ --help. +# +# The archive installed by --kernel be used automatically by -K. +# An offline archive can be used via -e in any tool that accepts those options. +# + +UNSTRIP=${UNSTRIP:-@UNSTRIP@} +AR=${AR:-@AR@} +SUDO=${SUDO:-/usr/bin/sudo} + +LS=/bin/ls +RM=/bin/rm +MV=/bin/mv +MKDIR=/bin/mkdir +XARGS=/usr/bin/xargs + +outdir=${TMPDIR:-/tmp}/debugar$$ + +usage() +{ + echo "Usage: $0 ARCHIVE {options}" + echo " or: $0 --kernel [--sudo] [--force] [RELEASE]" + echo + echo "Valid options are listed under 'Input selection options'" + echo "when running: $UNSTRIP --help" + echo + echo "The --kernel form updates the file used by -K if the" + echo "kernel installation has changed, or always with --force." + echo "With --sudo, touches the installed file via $SUDO." +} + +fatal_usage() +{ + usage >&2 + exit 2 +} + +script_version() +{ + echo "`basename $0` (@PACKAGE_NAME@) @PACKAGE_VERSION@" + echo "Copyright (C) 2007 Red Hat, Inc." + echo "This is free software; see the source for copying conditions." + echo "There is NO warranty; not even for MERCHANTABILITY or" + echo "FITNESS FOR A PARTICULAR PURPOSE." + echo "Written by Roland McGrath." +} + +sudo= +kernel=no +force_kernel=no +while [ $# -gt 0 ]; do + case "x$1" in + x--help) usage; exit 0 ;; + x--version) script_version; exit 0 ;; + x--kernel) kernel=yes ;; + x--force) force_kernel=yes ;; + x--sudo) sudo=$SUDO ;; + *) break ;; + esac + shift +done + +if [ $kernel = no ] && [ $force_kernel = yes -o -n "$sudo" ]; then + usage +fi + +if [ $kernel = yes ]; then + if [ $# -eq 0 ]; then + release=`uname -r` + elif [ $# -eq 1 ]; then + release=$1 + else + fatal_usage + fi + + dir=/usr/lib/debug/lib/modules/$release + archive=$dir/debug.a + dep=/lib/modules/$release/modules.dep + + if [ ! -d $dir ]; then + echo >&2 "$0: $dir not installed" + exit 1 + fi + + # Without --force, bail if the kernel installation is not newer. + # This file is normally touched by installing new kernels or modules. + if [ $force_kernel = no -a "$archive" -nt "$dep" ]; then + exit 0 + fi + + # We have to kill the old one first, because our own -K would use it. + [ ! -e "$archive" ] || $sudo $RM -f "$archive" || exit + + set "$archive" "-K$release" +fi + +if [ $# -lt 2 ]; then + fatal_usage +fi + +archive="$1" +shift + +case "$archive" in +/*) ;; +*) archive="`/bin/pwd`/$archive" ;; +esac + +if [ -z "$sudo" ]; then + new_archive="$archive.new" +else + new_archive="$outdir.a" +fi + +$RM -f "$new_archive" || exit + +trap '$RM -rf "$outdir" "$new_archive"' 0 1 2 15 + +$MKDIR "$outdir" && +$UNSTRIP -d "$outdir" -m -a -R "$@" && +(cd "$outdir" && $LS | $XARGS $AR cq "$new_archive") && +$sudo $MV -f "$new_archive" "$archive" + +exit diff --git a/src/readelf.c b/src/readelf.c index ffcb0a1c..c591b322 100644 --- a/src/readelf.c +++ b/src/readelf.c @@ -39,6 +39,7 @@ #include <inttypes.h> #include <langinfo.h> #include <libdw.h> +#include <libdwfl.h> #include <libintl.h> #include <locale.h> #include <stdarg.h> @@ -198,11 +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, - bool will_print_archive_index); -static void process_elf_file (Elf *elf, const char *prefix, const char *fname, - bool only_one); +static void process_file (int fd, const char *fname, bool only_one); +static void process_elf_file (Dwfl_Module *dwflmod); static void print_ehdr (Ebl *ebl, GElf_Ehdr *ehdr); static void print_shdr (Ebl *ebl, GElf_Ehdr *ehdr); static void print_phdr (Ebl *ebl, GElf_Ehdr *ehdr); @@ -218,7 +216,7 @@ static void handle_verneed (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr); static void handle_verdef (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr); static void handle_versym (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr); -static void print_debug (Ebl *ebl, GElf_Ehdr *ehdr); +static void print_debug (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr); static void handle_hash (Ebl *ebl); static void handle_notes (Ebl *ebl, GElf_Ehdr *ehdr); static void print_liblist (Ebl *ebl); @@ -256,21 +254,7 @@ main (int argc, char *argv[]) continue; } - /* Create an `Elf' descriptor. */ - Elf *elf = elf_begin (fd, ELF_C_READ_MMAP, NULL); - if (elf == NULL) - error (0, 0, gettext ("cannot generate Elf descriptor: %s\n"), - elf_errmsg (-1)); - else - { - process_file (fd, elf, NULL, argv[remaining], only_one, - print_archive_index); - - /* Now we can close the descriptor. */ - if (elf_end (elf) != 0) - error (0, 0, gettext ("error while closing Elf descriptor: %s"), - elf_errmsg (-1)); - } + process_file (fd, argv[remaining], only_one); close (fd); } @@ -435,103 +419,138 @@ warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\ } -/* Process one file. */ +/* Check if the file is an archive, and if so dump its index. */ static void -process_file (int fd, Elf *elf, const char *prefix, const char *fname, - bool only_one, bool will_print_archive_index) +check_archive_index (int fd, const char *fname, bool only_one) { - /* We can handle two types of files: ELF files and archives. */ - Elf_Kind kind = elf_kind (elf); - struct stat64 st; - - switch (kind) + /* Create an `Elf' descriptor. */ + Elf *elf = elf_begin (fd, ELF_C_READ_MMAP, NULL); + if (elf == NULL) + error (0, 0, gettext ("cannot generate Elf descriptor: %s"), + elf_errmsg (-1)); + else { - case ELF_K_ELF: - if (unlikely (will_print_archive_index)) + if (elf_kind (elf) == ELF_K_AR) + { + if (!only_one) + printf ("\n%s:\n\n", fname); + dump_archive_index (elf, fname); + } + else error (0, 0, gettext ("'%s' is not an archive, cannot print archive index"), fname); - /* Yes! It's an ELF file. */ - if (any_control_option) - process_elf_file (elf, prefix, fname, only_one); - break; - case ELF_K_AR: - { - size_t prefix_len = prefix == NULL ? 0 : strlen (prefix); - size_t fname_len = strlen (fname) + 1; - char new_prefix[prefix_len + 1 + fname_len]; - char *cp = new_prefix; + /* Now we can close the descriptor. */ + if (elf_end (elf) != 0) + error (0, 0, gettext ("error while closing Elf descriptor: %s"), + elf_errmsg (-1)); + } +} - /* Create the full name of the file. */ - if (prefix != NULL) - { - cp = mempcpy (cp, prefix, prefix_len); - *cp++ = ':'; - } - memcpy (cp, fname, fname_len); +/* Trivial callback used for checking if we opened an archive. */ +static int +count_dwflmod (Dwfl_Module *dwflmod __attribute__ ((unused)), + void **userdata __attribute__ ((unused)), + const char *name __attribute__ ((unused)), + Dwarf_Addr base __attribute__ ((unused)), + void *arg) +{ + *(bool *) arg = false; + return DWARF_CB_ABORT; +} - if (will_print_archive_index) - dump_archive_index (elf, new_prefix); +static int +process_dwflmod (Dwfl_Module *dwflmod, + void **userdata __attribute__ ((unused)), + const char *name __attribute__ ((unused)), + Dwarf_Addr base __attribute__ ((unused)), + void *arg) +{ + bool only_one = *(bool *) arg; - /* It's an archive. We process each file in it. */ - Elf *subelf; - Elf_Cmd cmd = ELF_C_READ_MMAP; - while ((subelf = elf_begin (fd, cmd, elf)) != NULL) - { - kind = elf_kind (subelf); + /* Print the file name. */ + if (!only_one) + { + const char *fname; + dwfl_module_info (dwflmod, NULL, NULL, NULL, NULL, NULL, &fname, NULL); - /* Call this function recursively. */ - if (kind == ELF_K_ELF || kind == ELF_K_AR) - { - Elf_Arhdr *arhdr = elf_getarhdr (subelf); - assert (arhdr != NULL); + printf ("\n%s:\n\n", fname); + } - process_file (fd, subelf, new_prefix, arhdr->ar_name, false, - kind == ELF_K_AR && will_print_archive_index); - } + process_elf_file (dwflmod); - /* Get next archive element. */ - cmd = elf_next (subelf); - if (elf_end (subelf) != 0) - error (0, 0, - gettext (" error while freeing sub-ELF descriptor: %s\n"), - elf_errmsg (-1)); - } - } - break; + return DWARF_CB_OK; +} - default: +/* Stub libdwfl callback, only the ELF handle already open is ever used. */ +static int +find_no_debuginfo (Dwfl_Module *mod __attribute__ ((unused)), + void **userdata __attribute__ ((unused)), + const char *modname __attribute__ ((unused)), + Dwarf_Addr base __attribute__ ((unused)), + const char *file_name __attribute__ ((unused)), + const char *debuglink_file __attribute__ ((unused)), + GElf_Word debuglink_crc __attribute__ ((unused)), + char **debuginfo_file_name __attribute__ ((unused))) +{ + return -1; +} + +/* Process one input file. */ +static void +process_file (int fd, const char *fname, bool only_one) +{ + if (print_archive_index) + check_archive_index (fd, fname, only_one); + + if (!any_control_option) + return; + + /* Use libdwfl in a trivial way to open the libdw handle for us. + This takes care of applying relocations to DWARF data in ET_REL files. */ + static const Dwfl_Callbacks callbacks = + { + .section_address = dwfl_offline_section_address, + .find_debuginfo = find_no_debuginfo + }; + Dwfl *dwfl = dwfl_begin (&callbacks); + if (dwfl_report_offline (dwfl, fname, fname, fd) == NULL) + { + struct stat64 st; if (fstat64 (fd, &st) != 0) error (0, errno, gettext ("cannot stat input file")); else if (st.st_size == 0) error (0, 0, gettext ("input file is empty")); else - /* We cannot do anything. */ - error (0, 0, gettext ("\ -Not an ELF file - it has the wrong magic bytes at the start")); - break; + error (0, 0, gettext ("failed reading '%s': %s"), + fname, dwfl_errmsg (-1)); + } + else + { + dwfl_report_end (dwfl, NULL, NULL); + + if (only_one) + /* Clear ONLY_ONE if we have multiple modules, from an archive. */ + dwfl_getmodules (dwfl, &count_dwflmod, &only_one, 1); + + /* Process the one or more modules gleaned from this file. */ + dwfl_getmodules (dwfl, &process_dwflmod, &only_one, 0); } + dwfl_end (dwfl); } -/* Process one file. */ +/* Process one ELF file. */ static void -process_elf_file (Elf *elf, const char *prefix, const char *fname, - bool only_one) +process_elf_file (Dwfl_Module *dwflmod) { + GElf_Addr dwflbias; + Elf *elf = dwfl_module_getelf (dwflmod, &dwflbias); + GElf_Ehdr ehdr_mem; GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem); - /* Print the file name. */ - if (!only_one) - { - if (prefix != NULL) - printf ("\n%s(%s):\n\n", prefix, fname); - else - printf ("\n%s:\n\n", fname); - } - if (ehdr == NULL) { error (0, 0, gettext ("cannot read ELF header: %s"), elf_errmsg (-1)); @@ -578,7 +597,7 @@ process_elf_file (Elf *elf, const char *prefix, const char *fname, if (string_sections != NULL) dump_strings (ebl); if (print_debug_sections != 0) - print_debug (ebl, ehdr); + print_debug (dwflmod, ebl, ehdr); if (print_notes) handle_notes (ebl, ehdr); if (print_string_sections) @@ -2721,6 +2740,79 @@ print_liblist (Ebl *ebl) } +static char * +format_dwarf_addr (Dwfl_Module *dwflmod, + int address_size, Dwarf_Addr address) +{ + /* See if there is a name we can give for this address. */ + GElf_Sym sym; + const char *name = dwfl_module_addrsym (dwflmod, address, &sym, NULL); + if (name != NULL) + sym.st_value = address - sym.st_value; + + /* Relativize the address. */ + int n = dwfl_module_relocations (dwflmod); + int i = n < 1 ? -1 : dwfl_module_relocate_address (dwflmod, &address); + + /* In an ET_REL file there is a section name to refer to. */ + const char *scn = (i < 0 ? NULL + : dwfl_module_relocation_info (dwflmod, i, NULL)); + + char *result; + if ((name != NULL + ? (sym.st_value != 0 + ? (scn != NULL + ? (address_size == 0 + ? asprintf (&result, + gettext ("%s+%#" PRIx64 " <%s+%#" PRIx64 ">"), + scn, address, name, sym.st_value) + : asprintf (&result, + gettext ("%s+%#0*" PRIx64 " <%s+%#" PRIx64 ">"), + scn, 2 + address_size * 2, address, + name, sym.st_value)) + : (address_size == 0 + ? asprintf (&result, + gettext ("%#" PRIx64 " <%s+%#" PRIx64 ">"), + address, name, sym.st_value) + : asprintf (&result, + gettext ("%#0*" PRIx64 " <%s+%#" PRIx64 ">"), + 2 + address_size * 2, address, + name, sym.st_value))) + : (scn != NULL + ? (address_size == 0 + ? asprintf (&result, + gettext ("%s+%#" PRIx64 " <%s>"), + scn, address, name) + : asprintf (&result, + gettext ("%s+%#0*" PRIx64 " <%s>"), + scn, 2 + address_size * 2, address, name)) + : (address_size == 0 + ? asprintf (&result, + gettext ("%#" PRIx64 " <%s>"), + address, name) + : asprintf (&result, + gettext ("%#0*" PRIx64 " <%s>"), + 2 + address_size * 2, address, name)))) + : (scn != NULL + ? (address_size == 0 + ? asprintf (&result, + gettext ("%s+%#" PRIx64), + scn, address) + : asprintf (&result, + gettext ("%s+%#0*" PRIx64), + scn, 2 + address_size * 2, address)) + : (address_size == 0 + ? asprintf (&result, + "%#" PRIx64, + address) + : asprintf (&result, + "%#0*" PRIx64, + 2 + address_size * 2, address)))) < 0) + error (EXIT_FAILURE, 0, _("memory exhausted")); + + return result; +} + static const char * dwarf_tag_string (unsigned int tag) { @@ -3307,7 +3399,7 @@ dwarf_discr_list_string (unsigned int code) static void -print_ops (Dwarf *dbg, int indent, int indentrest, +print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest, unsigned int addrsize, Dwarf_Word len, const unsigned char *data) { static const char *const known[] = @@ -3487,9 +3579,18 @@ print_ops (Dwarf *dbg, int indent, int indentrest, data += addrsize; len -= addrsize; - printf ("%*s[%4" PRIuMAX "] %s %" PRIuMAX "\n", - indent, "", (uintmax_t) offset, - known[op] ?: "???", (uintmax_t) addr); + if (op == DW_OP_addr) + { + char *a = format_dwarf_addr (dwflmod, 0, addr); + printf ("%*s[%4" PRIuMAX "] %s %s\n", + indent, "", (uintmax_t) offset, + known[op] ?: "???", a); + free (a); + } + else + printf ("%*s[%4" PRIuMAX "] %s %#" PRIxMAX "\n", + indent, "", (uintmax_t) offset, + known[op] ?: "???", (uintmax_t) addr); offset += 1 + addrsize; break; @@ -3646,7 +3747,8 @@ print_ops (Dwarf *dbg, int indent, int indentrest, static void -print_debug_abbrev_section (Ebl *ebl __attribute__ ((unused)), +print_debug_abbrev_section (Dwfl_Module *dwflmod __attribute__ ((unused)), + Ebl *ebl __attribute__ ((unused)), GElf_Ehdr *ehdr __attribute__ ((unused)), Elf_Scn *scn __attribute__ ((unused)), GElf_Shdr *shdr, Dwarf *dbg) @@ -3717,7 +3819,8 @@ print_debug_abbrev_section (Ebl *ebl __attribute__ ((unused)), not have to know a bit about the structure of the section, libdwarf takes care of it. */ static void -print_debug_aranges_section (Ebl *ebl __attribute__ ((unused)), +print_debug_aranges_section (Dwfl_Module *dwflmod __attribute__ ((unused)), + Ebl *ebl __attribute__ ((unused)), GElf_Ehdr *ehdr __attribute__ ((unused)), Elf_Scn *scn __attribute__ ((unused)), GElf_Shdr *shdr, Dwarf *dbg) @@ -3773,7 +3876,8 @@ print_debug_aranges_section (Ebl *ebl __attribute__ ((unused)), /* Print content of DWARF .debug_ranges section. */ static void -print_debug_ranges_section (Ebl *ebl __attribute__ ((unused)), +print_debug_ranges_section (Dwfl_Module *dwflmod, + Ebl *ebl __attribute__ ((unused)), GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr, Dwarf *dbg) { @@ -3800,7 +3904,7 @@ print_debug_ranges_section (Ebl *ebl __attribute__ ((unused)), if (data->d_size - offset < address_size * 2) { - printf (" [%6tx] <INVALID DATA>\n", offset); + printf (gettext (" [%6tx] <INVALID DATA>\n"), offset); break; } @@ -3820,21 +3924,24 @@ print_debug_ranges_section (Ebl *ebl __attribute__ ((unused)), } if (begin == (Dwarf_Addr) -1l) /* Base address entry. */ - printf (" [%6tx] base address %#0*" PRIxMAX "\n", offset, - 2 + (int) (address_size * 2), (uintmax_t) end); + { + char *b = format_dwarf_addr (dwflmod, address_size, end); + printf (gettext (" [%6tx] base address %s\n"), offset, b); + free (b); + } else if (begin == 0 && end == 0) /* End of list entry. */ first = true; else { + char *b = format_dwarf_addr (dwflmod, address_size, begin); + char *e = format_dwarf_addr (dwflmod, address_size, end); /* We have an address range entry. */ if (first) /* First address range entry in a list. */ - printf (" [%6tx] %#0*" PRIxMAX "..%#0*" PRIxMAX "\n", offset, - 2 + (int) (address_size * 2), (uintmax_t) begin, - 2 + (int) (address_size * 2), (uintmax_t) end); + printf (gettext (" [%6tx] %s..%s\n"), offset, b, e); else - printf (" %#0*" PRIxMAX "..%#0*" PRIxMAX "\n", - 2 + (int) (address_size * 2), (uintmax_t) begin, - 2 + (int) (address_size * 2), (uintmax_t) end); + printf (gettext (" %s..%s\n"), b, e); + free (b); + free (e); first = false; } @@ -3843,7 +3950,8 @@ print_debug_ranges_section (Ebl *ebl __attribute__ ((unused)), static void -print_debug_frame_section (Ebl *ebl __attribute__ ((unused)), +print_debug_frame_section (Dwfl_Module *dwflmod __attribute__ ((unused)), + Ebl *ebl __attribute__ ((unused)), GElf_Ehdr *ehdr __attribute__ ((unused)), Elf_Scn *scn __attribute__ ((unused)), GElf_Shdr *shdr __attribute__ ((unused)), @@ -3854,6 +3962,7 @@ print_debug_frame_section (Ebl *ebl __attribute__ ((unused)), struct attrcb_args { + Dwfl_Module *dwflmod; Dwarf *dbg; int level; unsigned int addrsize; @@ -3885,18 +3994,21 @@ attr_callback (Dwarf_Attribute *attrp, void *arg) switch (form) { - case DW_FORM_addr:; - Dwarf_Addr addr; - if (unlikely (dwarf_formaddr (attrp, &addr) != 0)) - { - attrval_out: - error (0, 0, gettext ("cannot get attribute value: %s"), - dwarf_errmsg (-1)); - return DWARF_CB_ABORT; - } - printf (" %*s%-20s %#0*" PRIxMAX "\n", - (int) (level * 2), "", dwarf_attr_string (attr), - 2 + (int) (cbargs->addrsize * 2), (uintmax_t) addr); + case DW_FORM_addr: + { + Dwarf_Addr addr; + if (unlikely (dwarf_formaddr (attrp, &addr) != 0)) + { + attrval_out: + error (0, 0, gettext ("cannot get attribute value: %s"), + dwarf_errmsg (-1)); + return DWARF_CB_ABORT; + } + char *a = format_dwarf_addr (cbargs->dwflmod, cbargs->addrsize, addr); + printf (" %*s%-20s %s\n", + (int) (level * 2), "", dwarf_attr_string (attr), a); + free (a); + } break; case DW_FORM_indirect: @@ -4033,7 +4145,8 @@ attr_callback (Dwarf_Attribute *attrp, void *arg) case DW_AT_frame_base: case DW_AT_return_addr: case DW_AT_static_link: - print_ops (cbargs->dbg, 12 + level * 2, 12 + level * 2, + print_ops (cbargs->dwflmod, cbargs->dbg, + 12 + level * 2, 12 + level * 2, cbargs->addrsize, block.length, block.data); break; } @@ -4051,7 +4164,8 @@ attr_callback (Dwarf_Attribute *attrp, void *arg) static void -print_debug_info_section (Ebl *ebl __attribute__ ((unused)), +print_debug_info_section (Dwfl_Module *dwflmod, + Ebl *ebl __attribute__ ((unused)), GElf_Ehdr *ehdr __attribute__ ((unused)), Elf_Scn *scn __attribute__ ((unused)), GElf_Shdr *shdr, Dwarf *dbg) @@ -4088,6 +4202,7 @@ print_debug_info_section (Ebl *ebl __attribute__ ((unused)), struct attrcb_args args; + args.dwflmod = dwflmod; args.dbg = dbg; args.addrsize = addrsize; args.cu_offset = offset; @@ -4172,7 +4287,8 @@ print_debug_info_section (Ebl *ebl __attribute__ ((unused)), static void -print_debug_line_section (Ebl *ebl, GElf_Ehdr *ehdr __attribute__ ((unused)), +print_debug_line_section (Dwfl_Module *dwflmod, Ebl *ebl, + GElf_Ehdr *ehdr __attribute__ ((unused)), Elf_Scn *scn, GElf_Shdr *shdr, Dwarf *dbg) { printf (gettext ("\ @@ -4391,10 +4507,11 @@ print_debug_line_section (Ebl *ebl, GElf_Ehdr *ehdr __attribute__ ((unused)), line += line_increment; address += address_increment; + char *a = format_dwarf_addr (dwflmod, 0, address); printf (gettext ("\ - special opcode %u: address+%u = %#" PRIx64 ", line%+d = %zu\n"), - opcode, address_increment, (uint64_t) address, - line_increment, line); + special opcode %u: address+%u = %s, line%+d = %zu\n"), + opcode, address_increment, a, line_increment, line); + free (a); } else if (opcode == 0) { @@ -4429,8 +4546,11 @@ print_debug_line_section (Ebl *ebl, GElf_Ehdr *ehdr __attribute__ ((unused)), address = read_4ubyte_unaligned_inc (dbg, linep); else address = read_8ubyte_unaligned_inc (dbg, linep); - printf (gettext ("set address to %#" PRIx64 "\n"), - (uint64_t) address); + { + char *a = format_dwarf_addr (dwflmod, 0, address); + printf (gettext ("set address to %s\n"), a); + free (a); + } break; case DW_LNE_define_file: @@ -4478,9 +4598,12 @@ define new file: dir=%u, mtime=%" PRIu64 ", length=%" PRIu64 ", name=%s\n"), address. */ get_uleb128 (u128, linep); address += minimum_instr_len * u128; - printf (gettext ("\ - advance address by %u to %#" PRIx64 "\n"), - u128, (uint64_t) address); + { + char *a = format_dwarf_addr (dwflmod, 0, address); + printf (gettext ("advance address by %u to %s\n"), + u128, a); + free (a); + } break; case DW_LNS_advance_line: @@ -4527,9 +4650,12 @@ define new file: dir=%u, mtime=%" PRIu64 ", length=%" PRIu64 ", name=%s\n"), u128 = (minimum_instr_len * ((255 - opcode_base) / line_range)); address += u128; - printf (gettext ("\ - advance address by constant %u to %#" PRIx64 "\n"), - u128, (uint64_t) address); + { + char *a = format_dwarf_addr (dwflmod, 0, address); + printf (gettext ("advance address by constant %u to %s\n"), + u128, a); + free (a); + } break; case DW_LNS_fixed_advance_pc: @@ -4540,9 +4666,13 @@ define new file: dir=%u, mtime=%" PRIu64 ", length=%" PRIu64 ", name=%s\n"), u128 = read_2ubyte_unaligned_inc (dbg, linep); address += u128; - printf (gettext ("\ - advance address by fixed value %u to %#" PRIx64 "\n"), - u128, (uint64_t) address); + { + char *a = format_dwarf_addr (dwflmod, 0, address); + printf (gettext ("\ +advance address by fixed value %u to %s\n"), + u128, a); + free (a); + } break; case DW_LNS_set_prologue_end: @@ -4585,7 +4715,8 @@ define new file: dir=%u, mtime=%" PRIu64 ", length=%" PRIu64 ", name=%s\n"), static void -print_debug_loc_section (Ebl *ebl __attribute__ ((unused)), +print_debug_loc_section (Dwfl_Module *dwflmod, + Ebl *ebl __attribute__ ((unused)), GElf_Ehdr *ehdr __attribute__ ((unused)), Elf_Scn *scn __attribute__ ((unused)), GElf_Shdr *shdr, @@ -4614,7 +4745,7 @@ print_debug_loc_section (Ebl *ebl __attribute__ ((unused)), if (data->d_size - offset < address_size * 2) { - printf (" [%6tx] <INVALID DATA>\n", offset); + printf (gettext (" [%6tx] <INVALID DATA>\n"), offset); break; } @@ -4634,8 +4765,11 @@ print_debug_loc_section (Ebl *ebl __attribute__ ((unused)), } if (begin == (Dwarf_Addr) -1l) /* Base address entry. */ - printf (" [%6tx] base address %#0*" PRIxMAX "\n", offset, - 2 + (int) (address_size * 2), (uintmax_t) end); + { + char *b = format_dwarf_addr (dwflmod, address_size, end); + printf (gettext (" [%6tx] base address %s\n"), offset, b); + free (b); + } else if (begin == 0 && end == 0) /* End of list entry. */ first = true; else @@ -4643,17 +4777,18 @@ print_debug_loc_section (Ebl *ebl __attribute__ ((unused)), /* We have a location expression entry. */ uint_fast16_t len = read_2ubyte_unaligned_inc (dbg, readp); + char *b = format_dwarf_addr (dwflmod, address_size, begin); + char *e = format_dwarf_addr (dwflmod, address_size, end); + if (first) /* First entry in a list. */ - printf (" [%6tx] %#0*" PRIxMAX "..%#0*" PRIxMAX, - offset, - 2 + (int) (address_size * 2), (uintmax_t) begin, - 2 + (int) (address_size * 2), (uintmax_t) end); + printf (gettext (" [%6tx] %s..%s"), offset, b, e); else - printf (" %#0*" PRIxMAX "..%#0*" PRIxMAX, - 2 + (int) (address_size * 2), (uintmax_t) begin, - 2 + (int) (address_size * 2), (uintmax_t) end); + printf (gettext (" %s..%s"), b, e); - print_ops (dbg, 1, 18 + (address_size * 4), + free (b); + free (e); + + print_ops (dwflmod, dbg, 1, 18 + (address_size * 4), address_size, len, readp); first = false; @@ -4686,7 +4821,8 @@ mac_compare (const void *p1, const void *p2) static void -print_debug_macinfo_section (Ebl *ebl __attribute__ ((unused)), +print_debug_macinfo_section (Dwfl_Module *dwflmod __attribute__ ((unused)), + Ebl *ebl __attribute__ ((unused)), GElf_Ehdr *ehdr __attribute__ ((unused)), Elf_Scn *scn, GElf_Shdr *shdr, Dwarf *dbg) { @@ -4857,7 +4993,8 @@ print_pubnames (Dwarf *dbg __attribute__ ((unused)), Dwarf_Global *global, /* Print the known exported symbols in the DWARF section '.debug_pubnames'. */ static void -print_debug_pubnames_section (Ebl *ebl __attribute__ ((unused)), +print_debug_pubnames_section (Dwfl_Module *dwflmod __attribute__ ((unused)), + Ebl *ebl __attribute__ ((unused)), GElf_Ehdr *ehdr __attribute__ ((unused)), Elf_Scn *scn __attribute__ ((unused)), GElf_Shdr *shdr, Dwarf *dbg) @@ -4871,7 +5008,8 @@ print_debug_pubnames_section (Ebl *ebl __attribute__ ((unused)), /* Print the content of the DWARF string section '.debug_str'. */ static void -print_debug_str_section (Ebl *ebl __attribute__ ((unused)), +print_debug_str_section (Dwfl_Module *dwflmod __attribute__ ((unused)), + Ebl *ebl __attribute__ ((unused)), GElf_Ehdr *ehdr __attribute__ ((unused)), Elf_Scn *scn __attribute__ ((unused)), GElf_Shdr *shdr, Dwarf *dbg) @@ -4910,34 +5048,29 @@ print_debug_str_section (Ebl *ebl __attribute__ ((unused)), } } - static void -print_debug (Ebl *ebl, GElf_Ehdr *ehdr) +print_debug (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr) { - /* Find the version information sections. For this we have to - search through the section table. */ - Dwarf *dbg; - Elf_Scn *scn; - size_t shstrndx; - /* Before we start the real work get a debug context descriptor. */ - dbg = dwarf_begin_elf (ebl->elf, DWARF_C_READ, NULL); + Dwarf_Addr dwbias; + Dwarf *dbg = dwfl_module_getdwarf (dwflmod, &dwbias); if (dbg == NULL) { error (0, 0, gettext ("cannot get debug context descriptor: %s"), - dwarf_errmsg (-1)); + dwfl_errmsg (-1)); return; } /* 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")); - scn = NULL; + /* Look through all the sections for the debugging sections to print. */ + Elf_Scn *scn = NULL; while ((scn = elf_nextscn (ebl->elf, scn)) != NULL) { - /* Handle the section if it is part of the versioning handling. */ GElf_Shdr shdr_mem; GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); @@ -4947,7 +5080,8 @@ print_debug (Ebl *ebl, GElf_Ehdr *ehdr) { const char *name; enum section_e bitmask; - void (*fp) (Ebl *, GElf_Ehdr *, Elf_Scn *, GElf_Shdr *, Dwarf *); + void (*fp) (Dwfl_Module *, Ebl *, + GElf_Ehdr *, Elf_Scn *, GElf_Shdr *, Dwarf *); } debug_sections[] = { #define NEW_SECTION(name) \ @@ -4974,14 +5108,11 @@ print_debug (Ebl *ebl, GElf_Ehdr *ehdr) if (strcmp (name, debug_sections[n].name) == 0) { if (print_debug_sections & debug_sections[n].bitmask) - debug_sections[n].fp (ebl, ehdr, scn, shdr, dbg); + debug_sections[n].fp (dwflmod, ebl, ehdr, scn, shdr, dbg); break; } } } - - /* We are done with the DWARF handling. */ - dwarf_end (dbg); } @@ -5144,6 +5275,7 @@ handle_core_item (Elf *core, const Ebl_Core_Item *item, const void *desc, break; case 'T': + case (char) ('T'|0x80): assert (count == 2); Dwarf_Word sec; Dwarf_Word usec; @@ -5161,6 +5293,22 @@ handle_core_item (Elf *core, const Ebl_Core_Item *item, const void *desc, default: abort (); } + if (unlikely (item->format == (char) ('T'|0x80))) + { + /* This is a hack for an ill-considered 64-bit ABI where + tv_usec is actually a 32-bit field with 32 bits of padding + rounding out struct timeval. We've already converted it as + a 64-bit field. For little-endian, this just means the + high half is the padding; it's presumably zero, but should + be ignored anyway. For big-endian, it means the 32-bit + field went into the high half of USEC. */ + GElf_Ehdr ehdr_mem; + GElf_Ehdr *ehdr = gelf_getehdr (core, &ehdr_mem); + if (likely (ehdr->e_ident[EI_DATA] == ELFDATA2MSB)) + usec >>= 32; + else + usec &= UINT32_MAX; + } colno = print_core_item (colno, ',', ITEM_WRAP_COLUMN, 0, item->name, maxfmt, "%" PRIu64 ".%.6" PRIu64, sec, usec); break; diff --git a/src/unstrip.c b/src/unstrip.c index ec22aa91..d19ad27e 100644 --- a/src/unstrip.c +++ b/src/unstrip.c @@ -30,11 +30,6 @@ * prelink vs .debug_* linked addresses - * merge many inputs? -> ET_EXEC with union phdrs + new phdrs for ET_REL mods - ** with applied relocs to ET_REL mods, use data modified by dwfl - *** still must apply relocs to SHF_ALLOC - ** useless unless merge all symtabs and dwarf sections - */ #ifdef HAVE_CONFIG_H @@ -91,6 +86,8 @@ static const struct argp_option options[] = { "all", 'a', NULL, 0, N_("Create output for modules that have no separate debug information"), 0 }, + { "relocate", 'R', NULL, 0, + N_("Apply relocations to DWARF sections in ET_REL files"), 0 }, { "list-only", 'n', NULL, 0, N_("Only list module and file names, build IDs"), 0 }, { NULL, 0, NULL, 0, NULL, 0 } @@ -107,6 +104,7 @@ struct arg_info bool ignore; bool modnames; bool match_files; + bool relocate; }; /* Handle program arguments. */ @@ -154,6 +152,9 @@ parse_opt (int key, char *arg, struct argp_state *state) case 'n': info->list = true; break; + case 'R': + info->relocate = true; + break; case ARGP_KEY_ARGS: case ARGP_KEY_NO_ARGS: @@ -199,10 +200,10 @@ parse_opt (int key, char *arg, struct argp_state *state) return EINVAL; } - if (info->ignore || info->all || info->modnames) + if (info->ignore || info->all || info->modnames || info->relocate) { argp_error (state, _("\ --m, -a, and -i options not allowed with explicit files")); +-m, -a, -R, and -i options not allowed with explicit files")); return EINVAL; } @@ -543,7 +544,7 @@ adjust_all_relocs (Elf *elf, Elf_Scn *symtab, const GElf_Shdr *symshdr, possible, add in section symbols for the added sections. */ static Elf_Data * add_new_section_symbols (Elf_Scn *old_symscn, size_t old_shnum, - Elf *elf, Elf_Scn *symscn, size_t shnum) + Elf *elf, bool rel, Elf_Scn *symscn, size_t shnum) { const size_t added = shnum - old_shnum; @@ -590,7 +591,7 @@ add_new_section_symbols (Elf_Scn *old_symscn, size_t old_shnum, ELF_CHECK (i_shdr != NULL, _("cannot get section header: %s")); GElf_Sym sym = { - .st_value = i_shdr->sh_addr, + .st_value = rel ? 0 : i_shdr->sh_addr, .st_info = GELF_ST_INFO (STB_LOCAL, STT_SECTION), .st_shndx = i < SHN_LORESERVE ? i : SHN_XINDEX }; @@ -623,7 +624,7 @@ add_new_section_symbols (Elf_Scn *old_symscn, size_t old_shnum, /* This has the side effect of updating STT_SECTION symbols' values, in case of prelink adjustments. */ static Elf_Data * -check_symtab_section_symbols (Elf *elf, Elf_Scn *scn, +check_symtab_section_symbols (Elf *elf, bool rel, Elf_Scn *scn, size_t shnum, size_t shstrndx, Elf_Scn *oscn, size_t oshnum, size_t oshstrndx, size_t debuglink) @@ -632,10 +633,10 @@ check_symtab_section_symbols (Elf *elf, Elf_Scn *scn, elf_getdata (scn, NULL)); if (n == oshnum) - return add_new_section_symbols (oscn, n, elf, scn, shnum); + return add_new_section_symbols (oscn, n, elf, rel, scn, shnum); if (n == oshstrndx || (n == debuglink && n == oshstrndx - 1)) - return add_new_section_symbols (oscn, n, elf, scn, shstrndx); + return add_new_section_symbols (oscn, n, elf, rel, scn, shstrndx); return NULL; } @@ -650,15 +651,20 @@ struct section }; static int -compare_alloc_sections (const struct section *s1, const struct section *s2) +compare_alloc_sections (const struct section *s1, const struct section *s2, + bool rel) { - /* Sort by address. */ - if (s1->shdr.sh_addr < s2->shdr.sh_addr) - return -1; - if (s1->shdr.sh_addr > s2->shdr.sh_addr) - return 1; + if (!rel) + { + /* Sort by address. */ + if (s1->shdr.sh_addr < s2->shdr.sh_addr) + return -1; + if (s1->shdr.sh_addr > s2->shdr.sh_addr) + return 1; + } - return 0; + /* At the same address, preserve original section order. */ + return (ssize_t) elf_ndxscn (s1->scn) - (ssize_t) elf_ndxscn (s2->scn); } static int @@ -676,7 +682,7 @@ compare_unalloc_sections (const GElf_Shdr *shdr1, const GElf_Shdr *shdr2, } static int -compare_sections (const void *a, const void *b) +compare_sections (const void *a, const void *b, bool rel) { const struct section *s1 = a; const struct section *s2 = b; @@ -686,11 +692,23 @@ compare_sections (const void *a, const void *b) return (s1->shdr.sh_flags & SHF_ALLOC) ? -1 : 1; return ((s1->shdr.sh_flags & SHF_ALLOC) - ? compare_alloc_sections (s1, s2) + ? compare_alloc_sections (s1, s2, rel) : compare_unalloc_sections (&s1->shdr, &s2->shdr, s1->name, s2->name)); } +static int +compare_sections_rel (const void *a, const void *b) +{ + return compare_sections (a, b, true); +} + +int +compare_sections_nonrel (const void *a, const void *b) +{ + return compare_sections (a, b, false); +} + struct symbol { @@ -717,7 +735,7 @@ struct symbol /* Collect input symbols into our internal form. */ static void -collect_symbols (Elf *outelf, Elf_Scn *symscn, Elf_Scn *strscn, +collect_symbols (Elf *outelf, bool rel, Elf_Scn *symscn, Elf_Scn *strscn, const size_t nent, const GElf_Addr bias, const size_t scnmap[], struct symbol *table, size_t *map, struct section *split_bss) @@ -752,16 +770,14 @@ collect_symbols (Elf *outelf, Elf_Scn *symscn, Elf_Scn *strscn, if (scnmap != NULL && shndx != SHN_UNDEF && shndx < SHN_LORESERVE) s->shndx = scnmap[shndx - 1]; - if (GELF_ST_TYPE (s->info.info) == STT_SECTION) + if (GELF_ST_TYPE (s->info.info) == STT_SECTION && !rel) { + /* Update the value to match the output section. */ GElf_Shdr shdr_mem; - GElf_Shdr *shdr = gelf_getshdr (elf_getscn (outelf, shndx), + GElf_Shdr *shdr = gelf_getshdr (elf_getscn (outelf, s->shndx), &shdr_mem); ELF_CHECK (shdr != NULL, _("cannot get section header: %s")); - - if (GELF_ST_TYPE (s->info.info) == STT_SECTION) - /* Update the value to match the output section. */ - s->value = shdr->sh_addr; + s->value = shdr->sh_addr; } else if (split_bss != NULL && s->value < split_bss->shdr.sh_addr @@ -836,6 +852,18 @@ compare_symbols_output (const void *a, const void *b) #undef CMP +/* Return true iff the flags, size, and name match. */ +static bool +sections_match (const struct section *sections, size_t i, + const GElf_Shdr *shdr, const char *name) +{ + return (sections[i].shdr.sh_flags == shdr->sh_flags + && (sections[i].shdr.sh_size == shdr->sh_size + || (sections[i].shdr.sh_size < shdr->sh_size + && section_can_shrink (§ions[i].shdr))) + && !strcmp (sections[i].name, name)); +} + /* Locate a matching allocated section in SECTIONS. */ static struct section * find_alloc_section (const GElf_Shdr *shdr, GElf_Addr bias, const char *name, @@ -858,11 +886,7 @@ find_alloc_section (const GElf_Shdr *shdr, GElf_Addr bias, const char *name, --i; for (; i < nalloc && sections[i].shdr.sh_addr == addr; ++i) - if (sections[i].shdr.sh_flags == shdr->sh_flags - && (sections[i].shdr.sh_size == shdr->sh_size - || (sections[i].shdr.sh_size < shdr->sh_size - && section_can_shrink (§ions[i].shdr))) - && !strcmp (sections[i].name, name)) + if (sections_match (sections, i, shdr, name)) return §ions[i]; break; } @@ -1001,7 +1025,7 @@ find_alloc_sections_prelink (Elf *debug, Elf_Data *debug_shstrtab, } } qsort (undo_sections, undo_nalloc, - sizeof undo_sections[0], compare_sections); + sizeof undo_sections[0], compare_sections_nonrel); } bool fail = false; @@ -1221,7 +1245,9 @@ copy_elided_sections (Elf *unstripped, Elf *stripped, const struct section *stripped_symtab = NULL; /* Sort the sections, allocated by address and others after. */ - qsort (sections, stripped_shnum - 1, sizeof sections[0], compare_sections); + qsort (sections, stripped_shnum - 1, sizeof sections[0], + stripped_ehdr->e_type == ET_REL + ? compare_sections_rel : compare_sections_nonrel); size_t nalloc = stripped_shnum - 1; while (nalloc > 0 && !(sections[nalloc - 1].shdr.sh_flags & SHF_ALLOC)) { @@ -1260,6 +1286,7 @@ copy_elided_sections (Elf *unstripped, Elf *stripped, bool check_prelink = false; Elf_Scn *unstripped_symtab = NULL; size_t unstripped_strtab_ndx = SHN_UNDEF; + size_t alloc_avail = 0; scn = NULL; while ((scn = elf_nextscn (unstripped, scn)) != NULL) { @@ -1280,36 +1307,59 @@ copy_elided_sections (Elf *unstripped, Elf *stripped, const char *name = get_section_name (ndx, shdr, shstrtab); - /* Look for the section that matches. */ - struct section *sec = ((shdr->sh_flags & SHF_ALLOC) - ? find_alloc_section (shdr, bias, name, - sections, nalloc) - : find_unalloc_section (shdr, name)); - if (sec == NULL) + struct section *sec = NULL; + if (shdr->sh_flags & SHF_ALLOC) { - if ((shdr->sh_flags & SHF_ALLOC) && stripped_ehdr->e_type != ET_REL) + if (stripped_ehdr->e_type != ET_REL) { - /* If we couldn't figure it out, it may be a prelink issue. */ - check_prelink = true; - continue; + /* Look for the section that matches. */ + sec = find_alloc_section (shdr, bias, name, sections, nalloc); + if (sec == NULL) + { + /* We couldn't figure it out. It may be a prelink issue. */ + check_prelink = true; + continue; + } + } + else + { + /* The sh_addr of allocated sections does not help us, + but the order usually matches. */ + if (likely (sections_match (sections, alloc_avail, shdr, name))) + sec = §ions[alloc_avail++]; + else + for (size_t i = alloc_avail + 1; i < nalloc; ++i) + if (sections_match (sections, i, shdr, name)) + { + sec = §ions[i]; + break; + } + } + } + else + { + /* Look for the section that matches. */ + sec = find_unalloc_section (shdr, name); + if (sec == NULL) + { + /* An additional unallocated section is fine if not SHT_NOBITS. + We looked it up anyway in case it's an unallocated section + copied in both files (e.g. SHT_NOTE), and don't keep both. */ + if (shdr->sh_type != SHT_NOBITS) + continue; + + /* Somehow some old .debug files wound up with SHT_NOBITS + .comment sections, so let those pass. */ + if (!strcmp (name, ".comment")) + continue; } - - /* An additional unallocated section is fine if not SHT_NOBITS. - We looked it up anyway in case it's an unallocated section - copied in both files (e.g. SHT_NOTE), so we don't keep both. */ - if (shdr->sh_type != SHT_NOBITS && !(shdr->sh_flags & SHF_ALLOC)) - continue; - - /* Somehow some old .debug files wound up with SHT_NOBITS - .comment sections, so let those pass. */ - if (!(shdr->sh_flags & SHF_ALLOC) && !strcmp (name, ".comment")) - continue; - - error (EXIT_FAILURE, 0, - _("cannot find matching section for [%Zu] '%s'"), - elf_ndxscn (scn), name); } + if (sec == NULL) + error (EXIT_FAILURE, 0, + _("cannot find matching section for [%Zu] '%s'"), + elf_ndxscn (scn), name); + sec->outscn = scn; } @@ -1374,6 +1424,7 @@ copy_elided_sections (Elf *unstripped, Elf *stripped, { /* This was created by stripping. We don't want it. */ debuglink = secndx; + ndx_section[secndx - 1] = SHN_UNDEF; continue; } @@ -1418,7 +1469,16 @@ copy_elided_sections (Elf *unstripped, Elf *stripped, GElf_Shdr *shdr = gelf_getshdr (sec->outscn, &shdr_mem); ELF_CHECK (shdr != NULL, _("cannot get section header: %s")); - shdr_mem.sh_addr = sec->shdr.sh_addr; + /* In an ET_REL file under --relocate, the sh_addr of SHF_ALLOC + sections will have been set nonzero by relocation. This + touched the shdrs of whichever file had the symtab. sh_addr + is still zero in the corresponding shdr. The relocated + address is what we want to use. */ + if (stripped_ehdr->e_type != ET_REL + || !(shdr_mem.sh_flags & SHF_ALLOC) + || shdr_mem.sh_addr == 0) + shdr_mem.sh_addr = sec->shdr.sh_addr; + shdr_mem.sh_type = sec->shdr.sh_type; shdr_mem.sh_size = sec->shdr.sh_size; shdr_mem.sh_info = sec->shdr.sh_info; @@ -1525,13 +1585,14 @@ copy_elided_sections (Elf *unstripped, Elf *stripped, size_t symndx_map[total_syms]; if (stripped_symtab != NULL) - collect_symbols (unstripped, stripped_symtab->scn, + collect_symbols (unstripped, stripped_ehdr->e_type == ET_REL, + stripped_symtab->scn, elf_getscn (stripped, stripped_symtab->shdr.sh_link), stripped_nsym, 0, ndx_section, symbols, symndx_map, NULL); Elf_Scn *unstripped_strtab = elf_getscn (unstripped, shdr->sh_link); - collect_symbols (unstripped, + collect_symbols (unstripped, stripped_ehdr->e_type == ET_REL, unstripped_symtab, unstripped_strtab, unstripped_nsym, stripped_ehdr->e_type == ET_REL ? 0 : bias, NULL, &symbols[stripped_nsym - 1], @@ -1542,11 +1603,15 @@ copy_elided_sections (Elf *unstripped, Elf *stripped, /* Now we can weed out the duplicates. Assign remaining symbols new slots, collecting a map from old indices to new. */ - size_t nsym = *symbols[0].map = 1; - for (size_t i = 1; i < total_syms; ++i) - *symbols[i].map = (!compare_symbols (&symbols[i - 1], &symbols[i]) - ? 0 /* This is a duplicate. */ - : ++nsym); /* Allocate the next slot. */ + size_t nsym = 0; + for (struct symbol *s = symbols; s < &symbols[total_syms]; ++s) + /* Skip a section symbol for a removed section, or a duplicate. */ + *s->map = (((s->shndx == SHN_UNDEF + && GELF_ST_TYPE (s->info.info) == STT_SECTION) + || (s + 1 < &symbols[total_syms] + && !compare_symbols (s, s + 1))) ? 0 + /* Allocate the next slot. */ + : ++nsym); /* Now we sort again, to determine the order in the output. */ qsort (symbols, total_syms, sizeof symbols[0], compare_symbols_output); @@ -1627,14 +1692,18 @@ copy_elided_sections (Elf *unstripped, Elf *stripped, &symndx_map[stripped_nsym - 1]); } else if (stripped_symtab != NULL && stripped_shnum != unstripped_shnum) - check_symtab_section_symbols (unstripped, stripped_symtab->scn, + check_symtab_section_symbols (unstripped, + stripped_ehdr->e_type == ET_REL, + stripped_symtab->scn, unstripped_shnum, unstripped_shstrndx, stripped_symtab->outscn, stripped_shnum, stripped_shstrndx, debuglink); if (stripped_dynsym != NULL) - (void) check_symtab_section_symbols (unstripped, stripped_dynsym->outscn, + (void) check_symtab_section_symbols (unstripped, + stripped_ehdr->e_type == ET_REL, + stripped_dynsym->outscn, unstripped_shnum, unstripped_shstrndx, stripped_dynsym->scn, stripped_shnum, @@ -1861,7 +1930,7 @@ handle_explicit_files (const char *output_file, bool create_dirs, /* Handle a pair of files opened implicitly by libdwfl for one module. */ static void handle_dwfl_module (const char *output_file, bool create_dirs, - Dwfl_Module *mod, bool all, bool ignore) + Dwfl_Module *mod, bool all, bool ignore, bool relocate) { GElf_Addr bias; Elf *stripped = dwfl_module_getelf (mod, &bias); @@ -1922,25 +1991,38 @@ handle_dwfl_module (const char *output_file, bool create_dirs, if (stripped_ehdr.e_type == ET_REL) { - /* We can't use the Elf handles already open, - because the DWARF sections have been relocated. */ + if (!relocate) + { + /* We can't use the Elf handles already open, + because the DWARF sections have been relocated. */ - const char *stripped_file = NULL; - const char *unstripped_file = NULL; - (void) dwfl_module_info (mod, NULL, NULL, NULL, NULL, NULL, - &stripped_file, &unstripped_file); + const char *stripped_file = NULL; + const char *unstripped_file = NULL; + (void) dwfl_module_info (mod, NULL, NULL, NULL, NULL, NULL, + &stripped_file, &unstripped_file); - handle_explicit_files (output_file, create_dirs, - stripped_file, unstripped_file); + handle_explicit_files (output_file, create_dirs, + stripped_file, unstripped_file); + return; + } + + /* Relocation is what we want! This ensures that all sections that can + get sh_addr values assigned have them, even ones not used in DWARF. + They might still be used in the symbol table. */ + if (dwfl_module_relocations (mod) < 0) + error (EXIT_FAILURE, 0, + _("cannot cache section addresses for module '%s': %s"), + dwfl_module_info (mod, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + dwfl_errmsg (-1)); } - else - handle_file (output_file, create_dirs, stripped, &stripped_ehdr, debug); + + handle_file (output_file, create_dirs, stripped, &stripped_ehdr, debug); } /* Handle one module being written to the output directory. */ static void handle_output_dir_module (const char *output_dir, Dwfl_Module *mod, - bool all, bool ignore, bool modnames) + bool all, bool ignore, bool modnames, bool relocate) { if (! modnames) { @@ -1960,7 +2042,7 @@ handle_output_dir_module (const char *output_dir, Dwfl_Module *mod, if (asprintf (&output_file, "%s/%s", output_dir, modnames ? name : file) < 0) error (EXIT_FAILURE, 0, _("memory exhausted")); - handle_dwfl_module (output_file, true, mod, all, ignore); + handle_dwfl_module (output_file, true, mod, all, ignore, relocate); } @@ -2073,12 +2155,13 @@ handle_implicit_modules (const struct arg_info *info) if (next (offset) != 0) error (EXIT_FAILURE, 0, _("matched more than one module")); handle_dwfl_module (info->output_file, false, mmi.found, - info->all, info->ignore); + info->all, info->ignore, info->relocate); } else do handle_output_dir_module (info->output_dir, mmi.found, - info->all, info->ignore, info->modnames); + info->all, info->ignore, + info->modnames, info->relocate); while ((offset = next (offset)) > 0); } diff --git a/tests/ChangeLog b/tests/ChangeLog index 32e4efe9..1284b134 100644 --- a/tests/ChangeLog +++ b/tests/ChangeLog @@ -1,3 +1,14 @@ +2007-10-09 Roland McGrath <roland@redhat.com> + + * dwflmodtest.c (print_module): Don't use %p in output. + * run-dwfl-bug-offline-rel.sh: Updated expected output. + +2007-10-08 Roland McGrath <roland@redhat.com> + + * testfile42.bz2: New data file. + * Makefile.am (EXTRA_DIST): Add it. + * run-elflint-test.sh: New test on that file. + 2007-10-04 Roland McGrath <roland@redhat.com> * run-readelf-test4.sh: New file. diff --git a/tests/Makefile.am b/tests/Makefile.am index 1fce5b72..ab325c7a 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -132,7 +132,7 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh \ testfile36.bz2 testfile36.debug.bz2 \ testfile37.bz2 testfile37.debug.bz2 \ testfile38.bz2 testfile39.bz2 testfile40.bz2 testfile40.debug.bz2 \ - testfile41.bz2 + testfile41.bz2 testfile42.bz2 installed_TESTS_ENVIRONMENT = libdir=$(DESTDIR)$(libdir) \ bindir=$(DESTDIR)$(bindir) \ diff --git a/tests/dwflmodtest.c b/tests/dwflmodtest.c index c34cac42..94f960fa 100644 --- a/tests/dwflmodtest.c +++ b/tests/dwflmodtest.c @@ -188,8 +188,8 @@ print_module (Dwfl_Module *mod __attribute__ ((unused)), Dwarf *dw, Dwarf_Addr bias, void *arg) { - printf ("module: %30s %08" PRIx64 " %12p %" PRIx64 " (%s)\n", - name, base, dw, bias, dwfl_errmsg (-1)); + printf ("module: %30s %08" PRIx64 " %s %" PRIx64 " (%s)\n", + name, base, dw == NULL ? "no" : "DWARF", bias, dwfl_errmsg (-1)); if (dw != NULL && *(const bool *) arg) { diff --git a/tests/run-dwfl-bug-offline-rel.sh b/tests/run-dwfl-bug-offline-rel.sh index 40e90bec..d1f6149a 100755 --- a/tests/run-dwfl-bug-offline-rel.sh +++ b/tests/run-dwfl-bug-offline-rel.sh @@ -29,7 +29,7 @@ testfiles testfile36 testfile36.debug testrun_compare ./dwflmodtest -e testfile36 <<\EOF module: 00000000..00002308 testfile36 (null) -module: 00000000 (nil) 0 (Callback returned failure) +module: 00000000 DWARF 0 (no error) module: 00000000..00002308 testfile36 testfile36.debug EOF diff --git a/tests/run-elflint-test.sh b/tests/run-elflint-test.sh index 660f1070..a0b93a25 100755 --- a/tests/run-elflint-test.sh +++ b/tests/run-elflint-test.sh @@ -38,4 +38,7 @@ testrun ../src/elflint -q testfile32 testfiles testfile33 testrun ../src/elflint -q testfile33 +testfiles testfile42 +testrun ../src/elflint -q --gnu-ld testfile42 + exit 0 diff --git a/tests/testfile42.bz2 b/tests/testfile42.bz2 Binary files differnew file mode 100644 index 00000000..2530aba2 --- /dev/null +++ b/tests/testfile42.bz2 |