summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorUlrich Drepper <[email protected]>2007-10-16 05:21:27 +0000
committerUlrich Drepper <[email protected]>2007-10-16 05:21:27 +0000
commitb597dfad924980dede10d7c19d87900b6172e599 (patch)
tree3c090b69070ad0056d479d90aa1f8829810140ba /src
parent3fc3d7bd6bd8485404a936f7354e781dc2be6a5a (diff)
merge of '92c36bfdbc6468d1711c043b530e0dfe5abb6dec'
and 'c22c8c43f8f68b0bffd4d5ccdb2282c958268742'
Diffstat (limited to 'src')
-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
10 files changed, 825 insertions, 308 deletions
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 <[email protected]>
+
+ * 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 <[email protected]>
+
+ * 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 <[email protected]>
+
+ * readelf.c (handle_core_item): Handle 'T'|0x80 to indicate
+ 64-bit struct timeval with 32-bit tv_usec.
+
+2007-10-07 Roland McGrath <[email protected]>
+
+ * 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 <[email protected]>
+
+ * 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 <[email protected]>
+
+ * 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 <[email protected]>
+
+ * 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 <[email protected]>
* 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 > [email protected]
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);
}