summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUlrich Drepper <drepper@redhat.com>2007-10-16 05:21:27 +0000
committerUlrich Drepper <drepper@redhat.com>2007-10-16 05:21:27 +0000
commitb597dfad924980dede10d7c19d87900b6172e599 (patch)
tree3c090b69070ad0056d479d90aa1f8829810140ba
parent3fc3d7bd6bd8485404a936f7354e781dc2be6a5a (diff)
merge of '92c36bfdbc6468d1711c043b530e0dfe5abb6dec'
and 'c22c8c43f8f68b0bffd4d5ccdb2282c958268742'
-rw-r--r--NEWS7
-rw-r--r--backends/ChangeLog18
-rw-r--r--backends/Makefile.am3
-rw-r--r--backends/linux-core-note.c17
-rw-r--r--backends/sparc64_corenote.c2
-rw-r--r--backends/sparc_auxv.c43
-rw-r--r--backends/sparc_corenote.c109
-rw-r--r--backends/sparc_init.c10
-rw-r--r--backends/sparc_symbol.c13
-rw-r--r--libdw/ChangeLog7
-rw-r--r--libdw/dwarf_begin_elf.c5
-rw-r--r--libdw/libdw.h8
-rw-r--r--libdwfl/ChangeLog65
-rw-r--r--libdwfl/derelocate.c41
-rw-r--r--libdwfl/dwfl_module_getdwarf.c16
-rw-r--r--libdwfl/dwfl_module_getsym.c11
-rw-r--r--libdwfl/dwfl_report_elf.c100
-rw-r--r--libdwfl/libdwflP.h19
-rw-r--r--libdwfl/linux-kernel-modules.c59
-rw-r--r--libdwfl/offline.c197
-rw-r--r--libdwfl/relocate.c143
-rw-r--r--libebl/ChangeLog5
-rw-r--r--libebl/eblobjnote.c86
-rw-r--r--libelf/ChangeLog6
-rw-r--r--libelf/elf_begin.c9
-rw-r--r--src/ChangeLog111
-rw-r--r--src/Makefile.am17
-rw-r--r--src/ar.c55
-rw-r--r--src/arlib.c7
-rw-r--r--src/arlib.h5
-rw-r--r--src/arlib2.c4
-rw-r--r--src/elflint.c63
-rw-r--r--src/make-debug-archive.in132
-rw-r--r--src/readelf.c486
-rw-r--r--src/unstrip.c253
-rw-r--r--tests/ChangeLog11
-rw-r--r--tests/Makefile.am2
-rw-r--r--tests/dwflmodtest.c4
-rwxr-xr-xtests/run-dwfl-bug-offline-rel.sh2
-rwxr-xr-xtests/run-elflint-test.sh3
-rw-r--r--tests/testfile42.bz2bin0 -> 16201 bytes
41 files changed, 1665 insertions, 489 deletions
diff --git a/NEWS b/NEWS
index 3120ce50..eb52105a 100644
--- a/NEWS
+++ b/NEWS
@@ -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 $@
diff --git a/src/ar.c b/src/ar.c
index e8ce9551..a9102a55 100644
--- a/src/ar.c
+++ b/src/ar.c
@@ -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 (&sections[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 (&sections[i].shdr)))
- && !strcmp (sections[i].name, name))
+ if (sections_match (sections, i, shdr, name))
return &sections[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 = &sections[alloc_avail++];
+ else
+ for (size_t i = alloc_avail + 1; i < nalloc; ++i)
+ if (sections_match (sections, i, shdr, name))
+ {
+ sec = &sections[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
new file mode 100644
index 00000000..2530aba2
--- /dev/null
+++ b/tests/testfile42.bz2
Binary files differ