diff options
author | Ulrich Drepper <[email protected]> | 2007-10-16 05:21:27 +0000 |
---|---|---|
committer | Ulrich Drepper <[email protected]> | 2007-10-16 05:21:27 +0000 |
commit | b597dfad924980dede10d7c19d87900b6172e599 (patch) | |
tree | 3c090b69070ad0056d479d90aa1f8829810140ba /src | |
parent | 3fc3d7bd6bd8485404a936f7354e781dc2be6a5a (diff) |
merge of '92c36bfdbc6468d1711c043b530e0dfe5abb6dec'
and 'c22c8c43f8f68b0bffd4d5ccdb2282c958268742'
Diffstat (limited to 'src')
-rw-r--r-- | src/ChangeLog | 111 | ||||
-rw-r--r-- | src/Makefile.am | 17 | ||||
-rw-r--r-- | src/ar.c | 55 | ||||
-rw-r--r-- | src/arlib.c | 7 | ||||
-rw-r--r-- | src/arlib.h | 5 | ||||
-rw-r--r-- | src/arlib2.c | 4 | ||||
-rw-r--r-- | src/elflint.c | 63 | ||||
-rw-r--r-- | src/make-debug-archive.in | 132 | ||||
-rw-r--r-- | src/readelf.c | 486 | ||||
-rw-r--r-- | src/unstrip.c | 253 |
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] + chmod +x [email protected] + mv -f [email protected] $@ @@ -884,6 +884,15 @@ write_member (struct armem *memb, off_t *startp, off_t *lenp, Elf *elf, return 0; } +/* Store the name in the long name table if necessary. + Record its offset or -1 if we did not need to use the table. */ +static void +remember_long_name (struct armem *mem, const char *name, size_t namelen) +{ + mem->long_name_off = (namelen > MAX_AR_NAME_LEN + ? arlib_add_long_name (name, namelen) + : -1l); +} static int do_oper_delete (const char *arfname, char **argv, int argc, @@ -963,12 +972,7 @@ do_oper_delete (const char *arfname, char **argv, int argc, arlib_add_symbols (subelf, arfname, arhdr->ar_name, newp->off); /* Remember long file names. */ - size_t ar_namelen = strlen (arhdr->ar_name); - if (ar_namelen > MAX_AR_NAME_LEN) - newp->long_name_off = arlib_add_long_name (arhdr->ar_name, - ar_namelen); - else - newp->long_name_off = -1l; + remember_long_name (newp, arhdr->ar_name, strlen (arhdr->ar_name)); } next: @@ -1087,6 +1091,9 @@ do_oper_insert (int oper, const char *arfname, char **argv, int argc, arlib_init (); + /* Initialize early for no_old case. */ + off_t cur_off = SARMAG; + if (fd == -1) { if (!suppress_create_msg) @@ -1118,7 +1125,6 @@ do_oper_insert (int oper, const char *arfname, char **argv, int argc, /* While iterating over the current content of the archive we must determine a number of things: which archive members to keep, which are replaced, and where to insert the new members. */ - off_t cur_off = SARMAG; Elf_Cmd cmd = ELF_C_READ_MMAP; Elf *subelf; while ((subelf = elf_begin (fd, cmd, elf)) != NULL) @@ -1137,11 +1143,7 @@ do_oper_insert (int oper, const char *arfname, char **argv, int argc, newp->mem = NULL; /* Remember long file names. */ - size_t ar_namelen = strlen (arhdr->ar_name); - if (ar_namelen > MAX_AR_NAME_LEN) - newp->long_name_off = arlib_add_long_name (arhdr->ar_name, ar_namelen); - else - newp->long_name_off = -1l; + remember_long_name (newp, arhdr->ar_name, strlen (arhdr->ar_name)); /* Check whether this is a file we are looking for. */ if (oper != oper_qappend) @@ -1223,17 +1225,13 @@ do_oper_insert (int oper, const char *arfname, char **argv, int argc, for (int cnt = 0; cnt < argc; ++cnt) { const char *bname = basename (argv[cnt]); + size_t bnamelen = strlen (bname); if (found[cnt] == NULL) { found[cnt] = alloca (sizeof (struct armem)); found[cnt]->old_off = -1; - size_t ar_namelen = strlen (argv[cnt]); - if (ar_namelen > MAX_AR_NAME_LEN) - found[cnt]->long_name_off = arlib_add_long_name (bname, - ar_namelen); - else - found[cnt]->long_name_off = -1l; + remember_long_name (found[cnt], bname, bnamelen); } struct stat newst; @@ -1275,14 +1273,12 @@ do_oper_insert (int oper, const char *arfname, char **argv, int argc, printf ("%c - %s\n", found[cnt]->old_off == -1l ? 'a' : 'r', argv[cnt]); -#ifdef DEBUG found[cnt]->elf = newelf; -#endif found[cnt]->sec = newst.st_mtime; found[cnt]->uid = newst.st_uid; found[cnt]->gid = newst.st_gid; found[cnt]->mode = newst.st_mode; - found[cnt]->name = basename (argv[cnt]); + found[cnt]->name = bname; found[cnt]->mem = elf_rawfile (newelf, &found[cnt]->size); if (found[cnt] == NULL || elf_cntl (newelf, ELF_C_FDDONE) != 0) @@ -1291,13 +1287,9 @@ do_oper_insert (int oper, const char *arfname, char **argv, int argc, close (newfd); - /* Remember long file names. */ - size_t bnamelen = strlen (bname); - if (bnamelen > MAX_AR_NAME_LEN) - found[cnt]->long_name_off = arlib_add_long_name (bname, - bnamelen); - else - found[cnt]->long_name_off = -1l; + if (found[cnt]->old_off != -1l) + /* Remember long file names. */ + remember_long_name (found[cnt], bname, bnamelen); } } } @@ -1449,8 +1441,9 @@ do_oper_insert (int oper, const char *arfname, char **argv, int argc, if (all->long_name_off == -1) { size_t namelen = strlen (all->name); - memset (mempcpy (arhdr.ar_name, all->name, namelen), - ' ', sizeof (arhdr.ar_name) - namelen); + char *p = mempcpy (arhdr.ar_name, all->name, namelen); + *p++ = '/'; + memset (p, ' ', sizeof (arhdr.ar_name) - namelen - 1); } else { @@ -1480,7 +1473,7 @@ do_oper_insert (int oper, const char *arfname, char **argv, int argc, /* Pad the file if its size is odd. */ if ((all->size & 1) != 0) - if (write (newfd, "\n", 1) != 1) + if (unlikely (write_retry (newfd, "\n", 1) != 1)) goto nonew_unlink; } else diff --git a/src/arlib.c b/src/arlib.c index 1b8785e4..af98454c 100644 --- a/src/arlib.c +++ b/src/arlib.c @@ -118,6 +118,13 @@ arlib_finalize (void) symtab.longnameslen = obstack_object_size (&symtab.longnamesob); if (symtab.longnameslen != sizeof (struct ar_hdr)) { + if ((symtab.longnameslen & 1) != 0) + { + /* Add one more byte to make length even. */ + obstack_grow (&symtab.longnamesob, "\n", 1); + ++symtab.longnameslen; + } + symtab.longnames = obstack_finish (&symtab.longnamesob); memcpy (&((struct ar_hdr *) symtab.longnames)->ar_size, tmpbuf, diff --git a/src/arlib.h b/src/arlib.h index af8e8e42..fd26d248 100644 --- a/src/arlib.h +++ b/src/arlib.h @@ -35,8 +35,9 @@ #include <sys/types.h> -/* Maximum length of a file name that fits directly into the ar header. */ -#define MAX_AR_NAME_LEN (sizeof (((struct ar_hdr *) NULL)->ar_name)) +/* Maximum length of a file name that fits directly into the ar header. + We cannot use the final byte since a / goes there. */ +#define MAX_AR_NAME_LEN (sizeof (((struct ar_hdr *) NULL)->ar_name) - 1) /* Words matching in size to archive header. */ diff --git a/src/arlib2.c b/src/arlib2.c index 47edb356..7098fec1 100644 --- a/src/arlib2.c +++ b/src/arlib2.c @@ -41,10 +41,10 @@ long int arlib_add_long_name (const char *filename, size_t filenamelen) { - int retval = obstack_object_size (&symtab.longnamesob); + size_t size = obstack_object_size (&symtab.longnamesob); obstack_grow (&symtab.longnamesob, filename, filenamelen); obstack_grow (&symtab.longnamesob, "/\n", 2); - return retval; + return size - sizeof (struct ar_hdr); } diff --git a/src/elflint.c b/src/elflint.c index e855d483..3ddf670f 100644 --- a/src/elflint.c +++ b/src/elflint.c @@ -727,13 +727,14 @@ section [%2d] '%s': symbol %zu: function in COMMON section is nonsense\n"), destshdr = gelf_getshdr (elf_getscn (ebl->elf, xndx), &destshdr_mem); if (destshdr != NULL) { + GElf_Addr sh_addr = (ehdr->e_type == ET_REL ? 0 + : destshdr->sh_addr); if (GELF_ST_TYPE (sym->st_info) != STT_TLS) { if (! ebl_check_special_symbol (ebl, ehdr, sym, name, destshdr)) { - if ((sym->st_value - destshdr->sh_addr) - > destshdr->sh_size) + if (sym->st_value - sh_addr > destshdr->sh_size) { /* GNU ld has severe bugs. When it decides to remove empty sections it leaves symbols referencing them @@ -750,7 +751,7 @@ section [%2d] '%s': symbol %zu: function in COMMON section is nonsense\n"), section [%2d] '%s': symbol %zu: st_value out of bounds\n"), idx, section_name (ebl, idx), cnt); } - else if ((sym->st_value - destshdr->sh_addr + else if ((sym->st_value - sh_addr + sym->st_size) > destshdr->sh_size) ERROR (gettext ("\ section [%2d] '%s': symbol %zu does not fit completely in referenced section [%2d] '%s'\n"), @@ -890,18 +891,24 @@ section [%2d] '%s': symbol %zu: non-local section symbol\n"), destshdr = gelf_getshdr (gotscn, &destshdr_mem); } - const char *sname = (destshdr == NULL ? NULL + const char *sname = ((destshdr == NULL || xndx == SHN_UNDEF) + ? NULL : elf_strptr (ebl->elf, ehdr->e_shstrndx, destshdr->sh_name)); if (sname == NULL) - ERROR (gettext ("\ -section [%2d] '%s': _GLOBAL_OFFSET_TABLE_ symbol refers to bad section\n"), - idx, section_name (ebl, idx)); + { + if (xndx != SHN_UNDEF || ehdr->e_type != ET_REL) + ERROR (gettext ("\ +section [%2d] '%s': _GLOBAL_OFFSET_TABLE_ symbol refers to \ +bad section [%2d]\n"), + idx, section_name (ebl, idx), xndx); + } else if (strcmp (sname, ".got.plt") != 0 && strcmp (sname, ".got") != 0) ERROR (gettext ("\ -section [%2d] '%s': _GLOBAL_OFFSET_TABLE_ symbol refers to '%s' section\n"), - idx, section_name (ebl, idx), sname); +section [%2d] '%s': _GLOBAL_OFFSET_TABLE_ symbol refers to \ +section [%2d] '%s'\n"), + idx, section_name (ebl, idx), xndx, sname); if (destshdr != NULL) { @@ -909,7 +916,8 @@ section [%2d] '%s': _GLOBAL_OFFSET_TABLE_ symbol refers to '%s' section\n"), if (!ebl_check_special_symbol (ebl, ehdr, sym, name, destshdr)) { - if (sym->st_value != destshdr->sh_addr) + if (ehdr->e_type != ET_REL + && sym->st_value != destshdr->sh_addr) /* This test is more strict than the psABIs which usually allow the symbol to be in the middle of the .got section, allowing negative offsets. */ @@ -1307,7 +1315,8 @@ section [%2d] '%s': relocation %zu: only symbol '_GLOBAL_OFFSET_TABLE_' can be u { if (destshdr != NULL && GELF_R_TYPE (r_info) != 0 - && (r_offset - destshdr->sh_addr) >= destshdr->sh_size) + && (r_offset - (ehdr->e_type == ET_REL ? 0 + : destshdr->sh_addr)) >= destshdr->sh_size) ERROR (gettext ("\ section [%2d] '%s': relocation %zu: offset out of bounds\n"), idx, section_name (ebl, idx), cnt); @@ -3086,7 +3095,7 @@ static const struct const char *name; size_t namelen; GElf_Word type; - enum { unused, exact, atleast } attrflag; + enum { unused, exact, atleast, exact_or_gnuld } attrflag; GElf_Word attr; GElf_Word attr2; } special_sections[] = @@ -3096,7 +3105,8 @@ static const struct { ".comment", 8, SHT_PROGBITS, exact, 0, 0 }, { ".data", 6, SHT_PROGBITS, exact, SHF_ALLOC | SHF_WRITE, 0 }, { ".data1", 7, SHT_PROGBITS, exact, SHF_ALLOC | SHF_WRITE, 0 }, - { ".debug", 7, SHT_PROGBITS, exact, 0, 0 }, + { ".debug_str", 11, SHT_PROGBITS, exact_or_gnuld, SHF_MERGE | SHF_STRINGS, 0 }, + { ".debug", 6, SHT_PROGBITS, exact, 0, 0 }, { ".dynamic", 9, SHT_DYNAMIC, atleast, SHF_ALLOC, SHF_WRITE }, { ".dynstr", 8, SHT_STRTAB, exact, SHF_ALLOC, 0 }, { ".dynsym", 8, SHT_DYNSYM, exact, SHF_ALLOC, 0 }, @@ -3132,6 +3142,10 @@ static const struct #define nspecial_sections \ (sizeof (special_sections) / sizeof (special_sections[0])) +#define IS_KNOWN_SPECIAL(idx, string, prefix) \ + (special_sections[idx].namelen == sizeof string - (prefix ? 1 : 0) \ + && !memcmp (special_sections[idx].name, string, \ + sizeof string - (prefix ? 1 : 0))) static void check_sections (Ebl *ebl, GElf_Ehdr *ehdr) @@ -3213,13 +3227,18 @@ cannot get section header for section [%2zu] '%s': %s\n"), char stbuf3[100]; GElf_Word good_type = special_sections[s].type; - if (special_sections[s].namelen == sizeof ".plt" && - !memcmp (special_sections[s].name, ".plt", sizeof ".plt") + if (IS_KNOWN_SPECIAL (s, ".plt", false) && ebl_bss_plt_p (ebl, ehdr)) good_type = SHT_NOBITS; + /* In a debuginfo file, any normal section can be SHT_NOBITS. + This is only invalid for DWARF sections and .shstrtab. */ if (shdr->sh_type != good_type - && !(is_debuginfo && shdr->sh_type == SHT_NOBITS)) + && (shdr->sh_type != SHT_NOBITS + || !is_debuginfo + || IS_KNOWN_SPECIAL (s, ".debug_str", false) + || IS_KNOWN_SPECIAL (s, ".debug", true) + || IS_KNOWN_SPECIAL (s, ".shstrtab", false))) ERROR (gettext ("\ section [%2d] '%s' has wrong type: expected %s, is %s\n"), (int) cnt, scnname, @@ -3228,12 +3247,14 @@ section [%2d] '%s' has wrong type: expected %s, is %s\n"), ebl_section_type_name (ebl, shdr->sh_type, stbuf2, sizeof (stbuf2))); - if (special_sections[s].attrflag == exact) + if (special_sections[s].attrflag == exact + || special_sections[s].attrflag == exact_or_gnuld) { /* Except for the link order and group bit all the other bits should match exactly. */ if ((shdr->sh_flags & ~(SHF_LINK_ORDER | SHF_GROUP)) - != special_sections[s].attr) + != special_sections[s].attr + && (special_sections[s].attrflag == exact || !gnuld)) ERROR (gettext ("\ section [%2zu] '%s' has wrong flags: expected %s, is %s\n"), cnt, scnname, @@ -3665,6 +3686,9 @@ phdr[%d]: no note entries defined for the type of file\n"), /* The p_offset values in a separate debug file are bogus. */ return; + if (phdr->p_filesz == 0) + return; + GElf_Off notes_size = 0; Elf_Data *data = elf_getdata_rawchunk (ebl->elf, phdr->p_offset, phdr->p_filesz, @@ -3683,6 +3707,9 @@ phdr[%d]: no note entries defined for the type of file\n"), static void check_note_section (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Shdr *shdr, int idx) { + if (shdr->sh_size == 0) + return; + Elf_Data *data = elf_getdata (elf_getscn (ebl->elf, idx), NULL); if (data == NULL) { diff --git a/src/make-debug-archive.in b/src/make-debug-archive.in new file mode 100644 index 00000000..c3fcbce4 --- /dev/null +++ b/src/make-debug-archive.in @@ -0,0 +1,132 @@ +#!/bin/sh +# +# Script to make an offline archive for debugging with libdwfl-based tools. +# +# make-debug-archive ARCHIVE {options} +# make-debug-archive --kernel [--force] [RELEASE] +# +# Valid options are those listed under 'Input selection options' +# by running @UNSTRIP@ --help. +# +# The archive installed by --kernel be used automatically by -K. +# An offline archive can be used via -e in any tool that accepts those options. +# + +UNSTRIP=${UNSTRIP:-@UNSTRIP@} +AR=${AR:-@AR@} +SUDO=${SUDO:-/usr/bin/sudo} + +LS=/bin/ls +RM=/bin/rm +MV=/bin/mv +MKDIR=/bin/mkdir +XARGS=/usr/bin/xargs + +outdir=${TMPDIR:-/tmp}/debugar$$ + +usage() +{ + echo "Usage: $0 ARCHIVE {options}" + echo " or: $0 --kernel [--sudo] [--force] [RELEASE]" + echo + echo "Valid options are listed under 'Input selection options'" + echo "when running: $UNSTRIP --help" + echo + echo "The --kernel form updates the file used by -K if the" + echo "kernel installation has changed, or always with --force." + echo "With --sudo, touches the installed file via $SUDO." +} + +fatal_usage() +{ + usage >&2 + exit 2 +} + +script_version() +{ + echo "`basename $0` (@PACKAGE_NAME@) @PACKAGE_VERSION@" + echo "Copyright (C) 2007 Red Hat, Inc." + echo "This is free software; see the source for copying conditions." + echo "There is NO warranty; not even for MERCHANTABILITY or" + echo "FITNESS FOR A PARTICULAR PURPOSE." + echo "Written by Roland McGrath." +} + +sudo= +kernel=no +force_kernel=no +while [ $# -gt 0 ]; do + case "x$1" in + x--help) usage; exit 0 ;; + x--version) script_version; exit 0 ;; + x--kernel) kernel=yes ;; + x--force) force_kernel=yes ;; + x--sudo) sudo=$SUDO ;; + *) break ;; + esac + shift +done + +if [ $kernel = no ] && [ $force_kernel = yes -o -n "$sudo" ]; then + usage +fi + +if [ $kernel = yes ]; then + if [ $# -eq 0 ]; then + release=`uname -r` + elif [ $# -eq 1 ]; then + release=$1 + else + fatal_usage + fi + + dir=/usr/lib/debug/lib/modules/$release + archive=$dir/debug.a + dep=/lib/modules/$release/modules.dep + + if [ ! -d $dir ]; then + echo >&2 "$0: $dir not installed" + exit 1 + fi + + # Without --force, bail if the kernel installation is not newer. + # This file is normally touched by installing new kernels or modules. + if [ $force_kernel = no -a "$archive" -nt "$dep" ]; then + exit 0 + fi + + # We have to kill the old one first, because our own -K would use it. + [ ! -e "$archive" ] || $sudo $RM -f "$archive" || exit + + set "$archive" "-K$release" +fi + +if [ $# -lt 2 ]; then + fatal_usage +fi + +archive="$1" +shift + +case "$archive" in +/*) ;; +*) archive="`/bin/pwd`/$archive" ;; +esac + +if [ -z "$sudo" ]; then + new_archive="$archive.new" +else + new_archive="$outdir.a" +fi + +$RM -f "$new_archive" || exit + +trap '$RM -rf "$outdir" "$new_archive"' 0 1 2 15 + +$MKDIR "$outdir" && +$UNSTRIP -d "$outdir" -m -a -R "$@" && +(cd "$outdir" && $LS | $XARGS $AR cq "$new_archive") && +$sudo $MV -f "$new_archive" "$archive" + +exit diff --git a/src/readelf.c b/src/readelf.c index ffcb0a1c..c591b322 100644 --- a/src/readelf.c +++ b/src/readelf.c @@ -39,6 +39,7 @@ #include <inttypes.h> #include <langinfo.h> #include <libdw.h> +#include <libdwfl.h> #include <libintl.h> #include <locale.h> #include <stdarg.h> @@ -198,11 +199,8 @@ static size_t shnum; /* Declarations of local functions. */ -static void process_file (int fd, Elf *elf, const char *prefix, - const char *fname, bool only_one, - bool will_print_archive_index); -static void process_elf_file (Elf *elf, const char *prefix, const char *fname, - bool only_one); +static void process_file (int fd, const char *fname, bool only_one); +static void process_elf_file (Dwfl_Module *dwflmod); static void print_ehdr (Ebl *ebl, GElf_Ehdr *ehdr); static void print_shdr (Ebl *ebl, GElf_Ehdr *ehdr); static void print_phdr (Ebl *ebl, GElf_Ehdr *ehdr); @@ -218,7 +216,7 @@ static void handle_verneed (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr); static void handle_verdef (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr); static void handle_versym (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr); -static void print_debug (Ebl *ebl, GElf_Ehdr *ehdr); +static void print_debug (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr); static void handle_hash (Ebl *ebl); static void handle_notes (Ebl *ebl, GElf_Ehdr *ehdr); static void print_liblist (Ebl *ebl); @@ -256,21 +254,7 @@ main (int argc, char *argv[]) continue; } - /* Create an `Elf' descriptor. */ - Elf *elf = elf_begin (fd, ELF_C_READ_MMAP, NULL); - if (elf == NULL) - error (0, 0, gettext ("cannot generate Elf descriptor: %s\n"), - elf_errmsg (-1)); - else - { - process_file (fd, elf, NULL, argv[remaining], only_one, - print_archive_index); - - /* Now we can close the descriptor. */ - if (elf_end (elf) != 0) - error (0, 0, gettext ("error while closing Elf descriptor: %s"), - elf_errmsg (-1)); - } + process_file (fd, argv[remaining], only_one); close (fd); } @@ -435,103 +419,138 @@ warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\ } -/* Process one file. */ +/* Check if the file is an archive, and if so dump its index. */ static void -process_file (int fd, Elf *elf, const char *prefix, const char *fname, - bool only_one, bool will_print_archive_index) +check_archive_index (int fd, const char *fname, bool only_one) { - /* We can handle two types of files: ELF files and archives. */ - Elf_Kind kind = elf_kind (elf); - struct stat64 st; - - switch (kind) + /* Create an `Elf' descriptor. */ + Elf *elf = elf_begin (fd, ELF_C_READ_MMAP, NULL); + if (elf == NULL) + error (0, 0, gettext ("cannot generate Elf descriptor: %s"), + elf_errmsg (-1)); + else { - case ELF_K_ELF: - if (unlikely (will_print_archive_index)) + if (elf_kind (elf) == ELF_K_AR) + { + if (!only_one) + printf ("\n%s:\n\n", fname); + dump_archive_index (elf, fname); + } + else error (0, 0, gettext ("'%s' is not an archive, cannot print archive index"), fname); - /* Yes! It's an ELF file. */ - if (any_control_option) - process_elf_file (elf, prefix, fname, only_one); - break; - case ELF_K_AR: - { - size_t prefix_len = prefix == NULL ? 0 : strlen (prefix); - size_t fname_len = strlen (fname) + 1; - char new_prefix[prefix_len + 1 + fname_len]; - char *cp = new_prefix; + /* Now we can close the descriptor. */ + if (elf_end (elf) != 0) + error (0, 0, gettext ("error while closing Elf descriptor: %s"), + elf_errmsg (-1)); + } +} - /* Create the full name of the file. */ - if (prefix != NULL) - { - cp = mempcpy (cp, prefix, prefix_len); - *cp++ = ':'; - } - memcpy (cp, fname, fname_len); +/* Trivial callback used for checking if we opened an archive. */ +static int +count_dwflmod (Dwfl_Module *dwflmod __attribute__ ((unused)), + void **userdata __attribute__ ((unused)), + const char *name __attribute__ ((unused)), + Dwarf_Addr base __attribute__ ((unused)), + void *arg) +{ + *(bool *) arg = false; + return DWARF_CB_ABORT; +} - if (will_print_archive_index) - dump_archive_index (elf, new_prefix); +static int +process_dwflmod (Dwfl_Module *dwflmod, + void **userdata __attribute__ ((unused)), + const char *name __attribute__ ((unused)), + Dwarf_Addr base __attribute__ ((unused)), + void *arg) +{ + bool only_one = *(bool *) arg; - /* It's an archive. We process each file in it. */ - Elf *subelf; - Elf_Cmd cmd = ELF_C_READ_MMAP; - while ((subelf = elf_begin (fd, cmd, elf)) != NULL) - { - kind = elf_kind (subelf); + /* Print the file name. */ + if (!only_one) + { + const char *fname; + dwfl_module_info (dwflmod, NULL, NULL, NULL, NULL, NULL, &fname, NULL); - /* Call this function recursively. */ - if (kind == ELF_K_ELF || kind == ELF_K_AR) - { - Elf_Arhdr *arhdr = elf_getarhdr (subelf); - assert (arhdr != NULL); + printf ("\n%s:\n\n", fname); + } - process_file (fd, subelf, new_prefix, arhdr->ar_name, false, - kind == ELF_K_AR && will_print_archive_index); - } + process_elf_file (dwflmod); - /* Get next archive element. */ - cmd = elf_next (subelf); - if (elf_end (subelf) != 0) - error (0, 0, - gettext (" error while freeing sub-ELF descriptor: %s\n"), - elf_errmsg (-1)); - } - } - break; + return DWARF_CB_OK; +} - default: +/* Stub libdwfl callback, only the ELF handle already open is ever used. */ +static int +find_no_debuginfo (Dwfl_Module *mod __attribute__ ((unused)), + void **userdata __attribute__ ((unused)), + const char *modname __attribute__ ((unused)), + Dwarf_Addr base __attribute__ ((unused)), + const char *file_name __attribute__ ((unused)), + const char *debuglink_file __attribute__ ((unused)), + GElf_Word debuglink_crc __attribute__ ((unused)), + char **debuginfo_file_name __attribute__ ((unused))) +{ + return -1; +} + +/* Process one input file. */ +static void +process_file (int fd, const char *fname, bool only_one) +{ + if (print_archive_index) + check_archive_index (fd, fname, only_one); + + if (!any_control_option) + return; + + /* Use libdwfl in a trivial way to open the libdw handle for us. + This takes care of applying relocations to DWARF data in ET_REL files. */ + static const Dwfl_Callbacks callbacks = + { + .section_address = dwfl_offline_section_address, + .find_debuginfo = find_no_debuginfo + }; + Dwfl *dwfl = dwfl_begin (&callbacks); + if (dwfl_report_offline (dwfl, fname, fname, fd) == NULL) + { + struct stat64 st; if (fstat64 (fd, &st) != 0) error (0, errno, gettext ("cannot stat input file")); else if (st.st_size == 0) error (0, 0, gettext ("input file is empty")); else - /* We cannot do anything. */ - error (0, 0, gettext ("\ -Not an ELF file - it has the wrong magic bytes at the start")); - break; + error (0, 0, gettext ("failed reading '%s': %s"), + fname, dwfl_errmsg (-1)); + } + else + { + dwfl_report_end (dwfl, NULL, NULL); + + if (only_one) + /* Clear ONLY_ONE if we have multiple modules, from an archive. */ + dwfl_getmodules (dwfl, &count_dwflmod, &only_one, 1); + + /* Process the one or more modules gleaned from this file. */ + dwfl_getmodules (dwfl, &process_dwflmod, &only_one, 0); } + dwfl_end (dwfl); } -/* Process one file. */ +/* Process one ELF file. */ static void -process_elf_file (Elf *elf, const char *prefix, const char *fname, - bool only_one) +process_elf_file (Dwfl_Module *dwflmod) { + GElf_Addr dwflbias; + Elf *elf = dwfl_module_getelf (dwflmod, &dwflbias); + GElf_Ehdr ehdr_mem; GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem); - /* Print the file name. */ - if (!only_one) - { - if (prefix != NULL) - printf ("\n%s(%s):\n\n", prefix, fname); - else - printf ("\n%s:\n\n", fname); - } - if (ehdr == NULL) { error (0, 0, gettext ("cannot read ELF header: %s"), elf_errmsg (-1)); @@ -578,7 +597,7 @@ process_elf_file (Elf *elf, const char *prefix, const char *fname, if (string_sections != NULL) dump_strings (ebl); if (print_debug_sections != 0) - print_debug (ebl, ehdr); + print_debug (dwflmod, ebl, ehdr); if (print_notes) handle_notes (ebl, ehdr); if (print_string_sections) @@ -2721,6 +2740,79 @@ print_liblist (Ebl *ebl) } +static char * +format_dwarf_addr (Dwfl_Module *dwflmod, + int address_size, Dwarf_Addr address) +{ + /* See if there is a name we can give for this address. */ + GElf_Sym sym; + const char *name = dwfl_module_addrsym (dwflmod, address, &sym, NULL); + if (name != NULL) + sym.st_value = address - sym.st_value; + + /* Relativize the address. */ + int n = dwfl_module_relocations (dwflmod); + int i = n < 1 ? -1 : dwfl_module_relocate_address (dwflmod, &address); + + /* In an ET_REL file there is a section name to refer to. */ + const char *scn = (i < 0 ? NULL + : dwfl_module_relocation_info (dwflmod, i, NULL)); + + char *result; + if ((name != NULL + ? (sym.st_value != 0 + ? (scn != NULL + ? (address_size == 0 + ? asprintf (&result, + gettext ("%s+%#" PRIx64 " <%s+%#" PRIx64 ">"), + scn, address, name, sym.st_value) + : asprintf (&result, + gettext ("%s+%#0*" PRIx64 " <%s+%#" PRIx64 ">"), + scn, 2 + address_size * 2, address, + name, sym.st_value)) + : (address_size == 0 + ? asprintf (&result, + gettext ("%#" PRIx64 " <%s+%#" PRIx64 ">"), + address, name, sym.st_value) + : asprintf (&result, + gettext ("%#0*" PRIx64 " <%s+%#" PRIx64 ">"), + 2 + address_size * 2, address, + name, sym.st_value))) + : (scn != NULL + ? (address_size == 0 + ? asprintf (&result, + gettext ("%s+%#" PRIx64 " <%s>"), + scn, address, name) + : asprintf (&result, + gettext ("%s+%#0*" PRIx64 " <%s>"), + scn, 2 + address_size * 2, address, name)) + : (address_size == 0 + ? asprintf (&result, + gettext ("%#" PRIx64 " <%s>"), + address, name) + : asprintf (&result, + gettext ("%#0*" PRIx64 " <%s>"), + 2 + address_size * 2, address, name)))) + : (scn != NULL + ? (address_size == 0 + ? asprintf (&result, + gettext ("%s+%#" PRIx64), + scn, address) + : asprintf (&result, + gettext ("%s+%#0*" PRIx64), + scn, 2 + address_size * 2, address)) + : (address_size == 0 + ? asprintf (&result, + "%#" PRIx64, + address) + : asprintf (&result, + "%#0*" PRIx64, + 2 + address_size * 2, address)))) < 0) + error (EXIT_FAILURE, 0, _("memory exhausted")); + + return result; +} + static const char * dwarf_tag_string (unsigned int tag) { @@ -3307,7 +3399,7 @@ dwarf_discr_list_string (unsigned int code) static void -print_ops (Dwarf *dbg, int indent, int indentrest, +print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest, unsigned int addrsize, Dwarf_Word len, const unsigned char *data) { static const char *const known[] = @@ -3487,9 +3579,18 @@ print_ops (Dwarf *dbg, int indent, int indentrest, data += addrsize; len -= addrsize; - printf ("%*s[%4" PRIuMAX "] %s %" PRIuMAX "\n", - indent, "", (uintmax_t) offset, - known[op] ?: "???", (uintmax_t) addr); + if (op == DW_OP_addr) + { + char *a = format_dwarf_addr (dwflmod, 0, addr); + printf ("%*s[%4" PRIuMAX "] %s %s\n", + indent, "", (uintmax_t) offset, + known[op] ?: "???", a); + free (a); + } + else + printf ("%*s[%4" PRIuMAX "] %s %#" PRIxMAX "\n", + indent, "", (uintmax_t) offset, + known[op] ?: "???", (uintmax_t) addr); offset += 1 + addrsize; break; @@ -3646,7 +3747,8 @@ print_ops (Dwarf *dbg, int indent, int indentrest, static void -print_debug_abbrev_section (Ebl *ebl __attribute__ ((unused)), +print_debug_abbrev_section (Dwfl_Module *dwflmod __attribute__ ((unused)), + Ebl *ebl __attribute__ ((unused)), GElf_Ehdr *ehdr __attribute__ ((unused)), Elf_Scn *scn __attribute__ ((unused)), GElf_Shdr *shdr, Dwarf *dbg) @@ -3717,7 +3819,8 @@ print_debug_abbrev_section (Ebl *ebl __attribute__ ((unused)), not have to know a bit about the structure of the section, libdwarf takes care of it. */ static void -print_debug_aranges_section (Ebl *ebl __attribute__ ((unused)), +print_debug_aranges_section (Dwfl_Module *dwflmod __attribute__ ((unused)), + Ebl *ebl __attribute__ ((unused)), GElf_Ehdr *ehdr __attribute__ ((unused)), Elf_Scn *scn __attribute__ ((unused)), GElf_Shdr *shdr, Dwarf *dbg) @@ -3773,7 +3876,8 @@ print_debug_aranges_section (Ebl *ebl __attribute__ ((unused)), /* Print content of DWARF .debug_ranges section. */ static void -print_debug_ranges_section (Ebl *ebl __attribute__ ((unused)), +print_debug_ranges_section (Dwfl_Module *dwflmod, + Ebl *ebl __attribute__ ((unused)), GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr, Dwarf *dbg) { @@ -3800,7 +3904,7 @@ print_debug_ranges_section (Ebl *ebl __attribute__ ((unused)), if (data->d_size - offset < address_size * 2) { - printf (" [%6tx] <INVALID DATA>\n", offset); + printf (gettext (" [%6tx] <INVALID DATA>\n"), offset); break; } @@ -3820,21 +3924,24 @@ print_debug_ranges_section (Ebl *ebl __attribute__ ((unused)), } if (begin == (Dwarf_Addr) -1l) /* Base address entry. */ - printf (" [%6tx] base address %#0*" PRIxMAX "\n", offset, - 2 + (int) (address_size * 2), (uintmax_t) end); + { + char *b = format_dwarf_addr (dwflmod, address_size, end); + printf (gettext (" [%6tx] base address %s\n"), offset, b); + free (b); + } else if (begin == 0 && end == 0) /* End of list entry. */ first = true; else { + char *b = format_dwarf_addr (dwflmod, address_size, begin); + char *e = format_dwarf_addr (dwflmod, address_size, end); /* We have an address range entry. */ if (first) /* First address range entry in a list. */ - printf (" [%6tx] %#0*" PRIxMAX "..%#0*" PRIxMAX "\n", offset, - 2 + (int) (address_size * 2), (uintmax_t) begin, - 2 + (int) (address_size * 2), (uintmax_t) end); + printf (gettext (" [%6tx] %s..%s\n"), offset, b, e); else - printf (" %#0*" PRIxMAX "..%#0*" PRIxMAX "\n", - 2 + (int) (address_size * 2), (uintmax_t) begin, - 2 + (int) (address_size * 2), (uintmax_t) end); + printf (gettext (" %s..%s\n"), b, e); + free (b); + free (e); first = false; } @@ -3843,7 +3950,8 @@ print_debug_ranges_section (Ebl *ebl __attribute__ ((unused)), static void -print_debug_frame_section (Ebl *ebl __attribute__ ((unused)), +print_debug_frame_section (Dwfl_Module *dwflmod __attribute__ ((unused)), + Ebl *ebl __attribute__ ((unused)), GElf_Ehdr *ehdr __attribute__ ((unused)), Elf_Scn *scn __attribute__ ((unused)), GElf_Shdr *shdr __attribute__ ((unused)), @@ -3854,6 +3962,7 @@ print_debug_frame_section (Ebl *ebl __attribute__ ((unused)), struct attrcb_args { + Dwfl_Module *dwflmod; Dwarf *dbg; int level; unsigned int addrsize; @@ -3885,18 +3994,21 @@ attr_callback (Dwarf_Attribute *attrp, void *arg) switch (form) { - case DW_FORM_addr:; - Dwarf_Addr addr; - if (unlikely (dwarf_formaddr (attrp, &addr) != 0)) - { - attrval_out: - error (0, 0, gettext ("cannot get attribute value: %s"), - dwarf_errmsg (-1)); - return DWARF_CB_ABORT; - } - printf (" %*s%-20s %#0*" PRIxMAX "\n", - (int) (level * 2), "", dwarf_attr_string (attr), - 2 + (int) (cbargs->addrsize * 2), (uintmax_t) addr); + case DW_FORM_addr: + { + Dwarf_Addr addr; + if (unlikely (dwarf_formaddr (attrp, &addr) != 0)) + { + attrval_out: + error (0, 0, gettext ("cannot get attribute value: %s"), + dwarf_errmsg (-1)); + return DWARF_CB_ABORT; + } + char *a = format_dwarf_addr (cbargs->dwflmod, cbargs->addrsize, addr); + printf (" %*s%-20s %s\n", + (int) (level * 2), "", dwarf_attr_string (attr), a); + free (a); + } break; case DW_FORM_indirect: @@ -4033,7 +4145,8 @@ attr_callback (Dwarf_Attribute *attrp, void *arg) case DW_AT_frame_base: case DW_AT_return_addr: case DW_AT_static_link: - print_ops (cbargs->dbg, 12 + level * 2, 12 + level * 2, + print_ops (cbargs->dwflmod, cbargs->dbg, + 12 + level * 2, 12 + level * 2, cbargs->addrsize, block.length, block.data); break; } @@ -4051,7 +4164,8 @@ attr_callback (Dwarf_Attribute *attrp, void *arg) static void -print_debug_info_section (Ebl *ebl __attribute__ ((unused)), +print_debug_info_section (Dwfl_Module *dwflmod, + Ebl *ebl __attribute__ ((unused)), GElf_Ehdr *ehdr __attribute__ ((unused)), Elf_Scn *scn __attribute__ ((unused)), GElf_Shdr *shdr, Dwarf *dbg) @@ -4088,6 +4202,7 @@ print_debug_info_section (Ebl *ebl __attribute__ ((unused)), struct attrcb_args args; + args.dwflmod = dwflmod; args.dbg = dbg; args.addrsize = addrsize; args.cu_offset = offset; @@ -4172,7 +4287,8 @@ print_debug_info_section (Ebl *ebl __attribute__ ((unused)), static void -print_debug_line_section (Ebl *ebl, GElf_Ehdr *ehdr __attribute__ ((unused)), +print_debug_line_section (Dwfl_Module *dwflmod, Ebl *ebl, + GElf_Ehdr *ehdr __attribute__ ((unused)), Elf_Scn *scn, GElf_Shdr *shdr, Dwarf *dbg) { printf (gettext ("\ @@ -4391,10 +4507,11 @@ print_debug_line_section (Ebl *ebl, GElf_Ehdr *ehdr __attribute__ ((unused)), line += line_increment; address += address_increment; + char *a = format_dwarf_addr (dwflmod, 0, address); printf (gettext ("\ - special opcode %u: address+%u = %#" PRIx64 ", line%+d = %zu\n"), - opcode, address_increment, (uint64_t) address, - line_increment, line); + special opcode %u: address+%u = %s, line%+d = %zu\n"), + opcode, address_increment, a, line_increment, line); + free (a); } else if (opcode == 0) { @@ -4429,8 +4546,11 @@ print_debug_line_section (Ebl *ebl, GElf_Ehdr *ehdr __attribute__ ((unused)), address = read_4ubyte_unaligned_inc (dbg, linep); else address = read_8ubyte_unaligned_inc (dbg, linep); - printf (gettext ("set address to %#" PRIx64 "\n"), - (uint64_t) address); + { + char *a = format_dwarf_addr (dwflmod, 0, address); + printf (gettext ("set address to %s\n"), a); + free (a); + } break; case DW_LNE_define_file: @@ -4478,9 +4598,12 @@ define new file: dir=%u, mtime=%" PRIu64 ", length=%" PRIu64 ", name=%s\n"), address. */ get_uleb128 (u128, linep); address += minimum_instr_len * u128; - printf (gettext ("\ - advance address by %u to %#" PRIx64 "\n"), - u128, (uint64_t) address); + { + char *a = format_dwarf_addr (dwflmod, 0, address); + printf (gettext ("advance address by %u to %s\n"), + u128, a); + free (a); + } break; case DW_LNS_advance_line: @@ -4527,9 +4650,12 @@ define new file: dir=%u, mtime=%" PRIu64 ", length=%" PRIu64 ", name=%s\n"), u128 = (minimum_instr_len * ((255 - opcode_base) / line_range)); address += u128; - printf (gettext ("\ - advance address by constant %u to %#" PRIx64 "\n"), - u128, (uint64_t) address); + { + char *a = format_dwarf_addr (dwflmod, 0, address); + printf (gettext ("advance address by constant %u to %s\n"), + u128, a); + free (a); + } break; case DW_LNS_fixed_advance_pc: @@ -4540,9 +4666,13 @@ define new file: dir=%u, mtime=%" PRIu64 ", length=%" PRIu64 ", name=%s\n"), u128 = read_2ubyte_unaligned_inc (dbg, linep); address += u128; - printf (gettext ("\ - advance address by fixed value %u to %#" PRIx64 "\n"), - u128, (uint64_t) address); + { + char *a = format_dwarf_addr (dwflmod, 0, address); + printf (gettext ("\ +advance address by fixed value %u to %s\n"), + u128, a); + free (a); + } break; case DW_LNS_set_prologue_end: @@ -4585,7 +4715,8 @@ define new file: dir=%u, mtime=%" PRIu64 ", length=%" PRIu64 ", name=%s\n"), static void -print_debug_loc_section (Ebl *ebl __attribute__ ((unused)), +print_debug_loc_section (Dwfl_Module *dwflmod, + Ebl *ebl __attribute__ ((unused)), GElf_Ehdr *ehdr __attribute__ ((unused)), Elf_Scn *scn __attribute__ ((unused)), GElf_Shdr *shdr, @@ -4614,7 +4745,7 @@ print_debug_loc_section (Ebl *ebl __attribute__ ((unused)), if (data->d_size - offset < address_size * 2) { - printf (" [%6tx] <INVALID DATA>\n", offset); + printf (gettext (" [%6tx] <INVALID DATA>\n"), offset); break; } @@ -4634,8 +4765,11 @@ print_debug_loc_section (Ebl *ebl __attribute__ ((unused)), } if (begin == (Dwarf_Addr) -1l) /* Base address entry. */ - printf (" [%6tx] base address %#0*" PRIxMAX "\n", offset, - 2 + (int) (address_size * 2), (uintmax_t) end); + { + char *b = format_dwarf_addr (dwflmod, address_size, end); + printf (gettext (" [%6tx] base address %s\n"), offset, b); + free (b); + } else if (begin == 0 && end == 0) /* End of list entry. */ first = true; else @@ -4643,17 +4777,18 @@ print_debug_loc_section (Ebl *ebl __attribute__ ((unused)), /* We have a location expression entry. */ uint_fast16_t len = read_2ubyte_unaligned_inc (dbg, readp); + char *b = format_dwarf_addr (dwflmod, address_size, begin); + char *e = format_dwarf_addr (dwflmod, address_size, end); + if (first) /* First entry in a list. */ - printf (" [%6tx] %#0*" PRIxMAX "..%#0*" PRIxMAX, - offset, - 2 + (int) (address_size * 2), (uintmax_t) begin, - 2 + (int) (address_size * 2), (uintmax_t) end); + printf (gettext (" [%6tx] %s..%s"), offset, b, e); else - printf (" %#0*" PRIxMAX "..%#0*" PRIxMAX, - 2 + (int) (address_size * 2), (uintmax_t) begin, - 2 + (int) (address_size * 2), (uintmax_t) end); + printf (gettext (" %s..%s"), b, e); - print_ops (dbg, 1, 18 + (address_size * 4), + free (b); + free (e); + + print_ops (dwflmod, dbg, 1, 18 + (address_size * 4), address_size, len, readp); first = false; @@ -4686,7 +4821,8 @@ mac_compare (const void *p1, const void *p2) static void -print_debug_macinfo_section (Ebl *ebl __attribute__ ((unused)), +print_debug_macinfo_section (Dwfl_Module *dwflmod __attribute__ ((unused)), + Ebl *ebl __attribute__ ((unused)), GElf_Ehdr *ehdr __attribute__ ((unused)), Elf_Scn *scn, GElf_Shdr *shdr, Dwarf *dbg) { @@ -4857,7 +4993,8 @@ print_pubnames (Dwarf *dbg __attribute__ ((unused)), Dwarf_Global *global, /* Print the known exported symbols in the DWARF section '.debug_pubnames'. */ static void -print_debug_pubnames_section (Ebl *ebl __attribute__ ((unused)), +print_debug_pubnames_section (Dwfl_Module *dwflmod __attribute__ ((unused)), + Ebl *ebl __attribute__ ((unused)), GElf_Ehdr *ehdr __attribute__ ((unused)), Elf_Scn *scn __attribute__ ((unused)), GElf_Shdr *shdr, Dwarf *dbg) @@ -4871,7 +5008,8 @@ print_debug_pubnames_section (Ebl *ebl __attribute__ ((unused)), /* Print the content of the DWARF string section '.debug_str'. */ static void -print_debug_str_section (Ebl *ebl __attribute__ ((unused)), +print_debug_str_section (Dwfl_Module *dwflmod __attribute__ ((unused)), + Ebl *ebl __attribute__ ((unused)), GElf_Ehdr *ehdr __attribute__ ((unused)), Elf_Scn *scn __attribute__ ((unused)), GElf_Shdr *shdr, Dwarf *dbg) @@ -4910,34 +5048,29 @@ print_debug_str_section (Ebl *ebl __attribute__ ((unused)), } } - static void -print_debug (Ebl *ebl, GElf_Ehdr *ehdr) +print_debug (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr) { - /* Find the version information sections. For this we have to - search through the section table. */ - Dwarf *dbg; - Elf_Scn *scn; - size_t shstrndx; - /* Before we start the real work get a debug context descriptor. */ - dbg = dwarf_begin_elf (ebl->elf, DWARF_C_READ, NULL); + Dwarf_Addr dwbias; + Dwarf *dbg = dwfl_module_getdwarf (dwflmod, &dwbias); if (dbg == NULL) { error (0, 0, gettext ("cannot get debug context descriptor: %s"), - dwarf_errmsg (-1)); + dwfl_errmsg (-1)); return; } /* Get the section header string table index. */ + size_t shstrndx; if (elf_getshstrndx (ebl->elf, &shstrndx) < 0) error (EXIT_FAILURE, 0, gettext ("cannot get section header string table index")); - scn = NULL; + /* Look through all the sections for the debugging sections to print. */ + Elf_Scn *scn = NULL; while ((scn = elf_nextscn (ebl->elf, scn)) != NULL) { - /* Handle the section if it is part of the versioning handling. */ GElf_Shdr shdr_mem; GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); @@ -4947,7 +5080,8 @@ print_debug (Ebl *ebl, GElf_Ehdr *ehdr) { const char *name; enum section_e bitmask; - void (*fp) (Ebl *, GElf_Ehdr *, Elf_Scn *, GElf_Shdr *, Dwarf *); + void (*fp) (Dwfl_Module *, Ebl *, + GElf_Ehdr *, Elf_Scn *, GElf_Shdr *, Dwarf *); } debug_sections[] = { #define NEW_SECTION(name) \ @@ -4974,14 +5108,11 @@ print_debug (Ebl *ebl, GElf_Ehdr *ehdr) if (strcmp (name, debug_sections[n].name) == 0) { if (print_debug_sections & debug_sections[n].bitmask) - debug_sections[n].fp (ebl, ehdr, scn, shdr, dbg); + debug_sections[n].fp (dwflmod, ebl, ehdr, scn, shdr, dbg); break; } } } - - /* We are done with the DWARF handling. */ - dwarf_end (dbg); } @@ -5144,6 +5275,7 @@ handle_core_item (Elf *core, const Ebl_Core_Item *item, const void *desc, break; case 'T': + case (char) ('T'|0x80): assert (count == 2); Dwarf_Word sec; Dwarf_Word usec; @@ -5161,6 +5293,22 @@ handle_core_item (Elf *core, const Ebl_Core_Item *item, const void *desc, default: abort (); } + if (unlikely (item->format == (char) ('T'|0x80))) + { + /* This is a hack for an ill-considered 64-bit ABI where + tv_usec is actually a 32-bit field with 32 bits of padding + rounding out struct timeval. We've already converted it as + a 64-bit field. For little-endian, this just means the + high half is the padding; it's presumably zero, but should + be ignored anyway. For big-endian, it means the 32-bit + field went into the high half of USEC. */ + GElf_Ehdr ehdr_mem; + GElf_Ehdr *ehdr = gelf_getehdr (core, &ehdr_mem); + if (likely (ehdr->e_ident[EI_DATA] == ELFDATA2MSB)) + usec >>= 32; + else + usec &= UINT32_MAX; + } colno = print_core_item (colno, ',', ITEM_WRAP_COLUMN, 0, item->name, maxfmt, "%" PRIu64 ".%.6" PRIu64, sec, usec); break; diff --git a/src/unstrip.c b/src/unstrip.c index ec22aa91..d19ad27e 100644 --- a/src/unstrip.c +++ b/src/unstrip.c @@ -30,11 +30,6 @@ * prelink vs .debug_* linked addresses - * merge many inputs? -> ET_EXEC with union phdrs + new phdrs for ET_REL mods - ** with applied relocs to ET_REL mods, use data modified by dwfl - *** still must apply relocs to SHF_ALLOC - ** useless unless merge all symtabs and dwarf sections - */ #ifdef HAVE_CONFIG_H @@ -91,6 +86,8 @@ static const struct argp_option options[] = { "all", 'a', NULL, 0, N_("Create output for modules that have no separate debug information"), 0 }, + { "relocate", 'R', NULL, 0, + N_("Apply relocations to DWARF sections in ET_REL files"), 0 }, { "list-only", 'n', NULL, 0, N_("Only list module and file names, build IDs"), 0 }, { NULL, 0, NULL, 0, NULL, 0 } @@ -107,6 +104,7 @@ struct arg_info bool ignore; bool modnames; bool match_files; + bool relocate; }; /* Handle program arguments. */ @@ -154,6 +152,9 @@ parse_opt (int key, char *arg, struct argp_state *state) case 'n': info->list = true; break; + case 'R': + info->relocate = true; + break; case ARGP_KEY_ARGS: case ARGP_KEY_NO_ARGS: @@ -199,10 +200,10 @@ parse_opt (int key, char *arg, struct argp_state *state) return EINVAL; } - if (info->ignore || info->all || info->modnames) + if (info->ignore || info->all || info->modnames || info->relocate) { argp_error (state, _("\ --m, -a, and -i options not allowed with explicit files")); +-m, -a, -R, and -i options not allowed with explicit files")); return EINVAL; } @@ -543,7 +544,7 @@ adjust_all_relocs (Elf *elf, Elf_Scn *symtab, const GElf_Shdr *symshdr, possible, add in section symbols for the added sections. */ static Elf_Data * add_new_section_symbols (Elf_Scn *old_symscn, size_t old_shnum, - Elf *elf, Elf_Scn *symscn, size_t shnum) + Elf *elf, bool rel, Elf_Scn *symscn, size_t shnum) { const size_t added = shnum - old_shnum; @@ -590,7 +591,7 @@ add_new_section_symbols (Elf_Scn *old_symscn, size_t old_shnum, ELF_CHECK (i_shdr != NULL, _("cannot get section header: %s")); GElf_Sym sym = { - .st_value = i_shdr->sh_addr, + .st_value = rel ? 0 : i_shdr->sh_addr, .st_info = GELF_ST_INFO (STB_LOCAL, STT_SECTION), .st_shndx = i < SHN_LORESERVE ? i : SHN_XINDEX }; @@ -623,7 +624,7 @@ add_new_section_symbols (Elf_Scn *old_symscn, size_t old_shnum, /* This has the side effect of updating STT_SECTION symbols' values, in case of prelink adjustments. */ static Elf_Data * -check_symtab_section_symbols (Elf *elf, Elf_Scn *scn, +check_symtab_section_symbols (Elf *elf, bool rel, Elf_Scn *scn, size_t shnum, size_t shstrndx, Elf_Scn *oscn, size_t oshnum, size_t oshstrndx, size_t debuglink) @@ -632,10 +633,10 @@ check_symtab_section_symbols (Elf *elf, Elf_Scn *scn, elf_getdata (scn, NULL)); if (n == oshnum) - return add_new_section_symbols (oscn, n, elf, scn, shnum); + return add_new_section_symbols (oscn, n, elf, rel, scn, shnum); if (n == oshstrndx || (n == debuglink && n == oshstrndx - 1)) - return add_new_section_symbols (oscn, n, elf, scn, shstrndx); + return add_new_section_symbols (oscn, n, elf, rel, scn, shstrndx); return NULL; } @@ -650,15 +651,20 @@ struct section }; static int -compare_alloc_sections (const struct section *s1, const struct section *s2) +compare_alloc_sections (const struct section *s1, const struct section *s2, + bool rel) { - /* Sort by address. */ - if (s1->shdr.sh_addr < s2->shdr.sh_addr) - return -1; - if (s1->shdr.sh_addr > s2->shdr.sh_addr) - return 1; + if (!rel) + { + /* Sort by address. */ + if (s1->shdr.sh_addr < s2->shdr.sh_addr) + return -1; + if (s1->shdr.sh_addr > s2->shdr.sh_addr) + return 1; + } - return 0; + /* At the same address, preserve original section order. */ + return (ssize_t) elf_ndxscn (s1->scn) - (ssize_t) elf_ndxscn (s2->scn); } static int @@ -676,7 +682,7 @@ compare_unalloc_sections (const GElf_Shdr *shdr1, const GElf_Shdr *shdr2, } static int -compare_sections (const void *a, const void *b) +compare_sections (const void *a, const void *b, bool rel) { const struct section *s1 = a; const struct section *s2 = b; @@ -686,11 +692,23 @@ compare_sections (const void *a, const void *b) return (s1->shdr.sh_flags & SHF_ALLOC) ? -1 : 1; return ((s1->shdr.sh_flags & SHF_ALLOC) - ? compare_alloc_sections (s1, s2) + ? compare_alloc_sections (s1, s2, rel) : compare_unalloc_sections (&s1->shdr, &s2->shdr, s1->name, s2->name)); } +static int +compare_sections_rel (const void *a, const void *b) +{ + return compare_sections (a, b, true); +} + +int +compare_sections_nonrel (const void *a, const void *b) +{ + return compare_sections (a, b, false); +} + struct symbol { @@ -717,7 +735,7 @@ struct symbol /* Collect input symbols into our internal form. */ static void -collect_symbols (Elf *outelf, Elf_Scn *symscn, Elf_Scn *strscn, +collect_symbols (Elf *outelf, bool rel, Elf_Scn *symscn, Elf_Scn *strscn, const size_t nent, const GElf_Addr bias, const size_t scnmap[], struct symbol *table, size_t *map, struct section *split_bss) @@ -752,16 +770,14 @@ collect_symbols (Elf *outelf, Elf_Scn *symscn, Elf_Scn *strscn, if (scnmap != NULL && shndx != SHN_UNDEF && shndx < SHN_LORESERVE) s->shndx = scnmap[shndx - 1]; - if (GELF_ST_TYPE (s->info.info) == STT_SECTION) + if (GELF_ST_TYPE (s->info.info) == STT_SECTION && !rel) { + /* Update the value to match the output section. */ GElf_Shdr shdr_mem; - GElf_Shdr *shdr = gelf_getshdr (elf_getscn (outelf, shndx), + GElf_Shdr *shdr = gelf_getshdr (elf_getscn (outelf, s->shndx), &shdr_mem); ELF_CHECK (shdr != NULL, _("cannot get section header: %s")); - - if (GELF_ST_TYPE (s->info.info) == STT_SECTION) - /* Update the value to match the output section. */ - s->value = shdr->sh_addr; + s->value = shdr->sh_addr; } else if (split_bss != NULL && s->value < split_bss->shdr.sh_addr @@ -836,6 +852,18 @@ compare_symbols_output (const void *a, const void *b) #undef CMP +/* Return true iff the flags, size, and name match. */ +static bool +sections_match (const struct section *sections, size_t i, + const GElf_Shdr *shdr, const char *name) +{ + return (sections[i].shdr.sh_flags == shdr->sh_flags + && (sections[i].shdr.sh_size == shdr->sh_size + || (sections[i].shdr.sh_size < shdr->sh_size + && section_can_shrink (§ions[i].shdr))) + && !strcmp (sections[i].name, name)); +} + /* Locate a matching allocated section in SECTIONS. */ static struct section * find_alloc_section (const GElf_Shdr *shdr, GElf_Addr bias, const char *name, @@ -858,11 +886,7 @@ find_alloc_section (const GElf_Shdr *shdr, GElf_Addr bias, const char *name, --i; for (; i < nalloc && sections[i].shdr.sh_addr == addr; ++i) - if (sections[i].shdr.sh_flags == shdr->sh_flags - && (sections[i].shdr.sh_size == shdr->sh_size - || (sections[i].shdr.sh_size < shdr->sh_size - && section_can_shrink (§ions[i].shdr))) - && !strcmp (sections[i].name, name)) + if (sections_match (sections, i, shdr, name)) return §ions[i]; break; } @@ -1001,7 +1025,7 @@ find_alloc_sections_prelink (Elf *debug, Elf_Data *debug_shstrtab, } } qsort (undo_sections, undo_nalloc, - sizeof undo_sections[0], compare_sections); + sizeof undo_sections[0], compare_sections_nonrel); } bool fail = false; @@ -1221,7 +1245,9 @@ copy_elided_sections (Elf *unstripped, Elf *stripped, const struct section *stripped_symtab = NULL; /* Sort the sections, allocated by address and others after. */ - qsort (sections, stripped_shnum - 1, sizeof sections[0], compare_sections); + qsort (sections, stripped_shnum - 1, sizeof sections[0], + stripped_ehdr->e_type == ET_REL + ? compare_sections_rel : compare_sections_nonrel); size_t nalloc = stripped_shnum - 1; while (nalloc > 0 && !(sections[nalloc - 1].shdr.sh_flags & SHF_ALLOC)) { @@ -1260,6 +1286,7 @@ copy_elided_sections (Elf *unstripped, Elf *stripped, bool check_prelink = false; Elf_Scn *unstripped_symtab = NULL; size_t unstripped_strtab_ndx = SHN_UNDEF; + size_t alloc_avail = 0; scn = NULL; while ((scn = elf_nextscn (unstripped, scn)) != NULL) { @@ -1280,36 +1307,59 @@ copy_elided_sections (Elf *unstripped, Elf *stripped, const char *name = get_section_name (ndx, shdr, shstrtab); - /* Look for the section that matches. */ - struct section *sec = ((shdr->sh_flags & SHF_ALLOC) - ? find_alloc_section (shdr, bias, name, - sections, nalloc) - : find_unalloc_section (shdr, name)); - if (sec == NULL) + struct section *sec = NULL; + if (shdr->sh_flags & SHF_ALLOC) { - if ((shdr->sh_flags & SHF_ALLOC) && stripped_ehdr->e_type != ET_REL) + if (stripped_ehdr->e_type != ET_REL) { - /* If we couldn't figure it out, it may be a prelink issue. */ - check_prelink = true; - continue; + /* Look for the section that matches. */ + sec = find_alloc_section (shdr, bias, name, sections, nalloc); + if (sec == NULL) + { + /* We couldn't figure it out. It may be a prelink issue. */ + check_prelink = true; + continue; + } + } + else + { + /* The sh_addr of allocated sections does not help us, + but the order usually matches. */ + if (likely (sections_match (sections, alloc_avail, shdr, name))) + sec = §ions[alloc_avail++]; + else + for (size_t i = alloc_avail + 1; i < nalloc; ++i) + if (sections_match (sections, i, shdr, name)) + { + sec = §ions[i]; + break; + } + } + } + else + { + /* Look for the section that matches. */ + sec = find_unalloc_section (shdr, name); + if (sec == NULL) + { + /* An additional unallocated section is fine if not SHT_NOBITS. + We looked it up anyway in case it's an unallocated section + copied in both files (e.g. SHT_NOTE), and don't keep both. */ + if (shdr->sh_type != SHT_NOBITS) + continue; + + /* Somehow some old .debug files wound up with SHT_NOBITS + .comment sections, so let those pass. */ + if (!strcmp (name, ".comment")) + continue; } - - /* An additional unallocated section is fine if not SHT_NOBITS. - We looked it up anyway in case it's an unallocated section - copied in both files (e.g. SHT_NOTE), so we don't keep both. */ - if (shdr->sh_type != SHT_NOBITS && !(shdr->sh_flags & SHF_ALLOC)) - continue; - - /* Somehow some old .debug files wound up with SHT_NOBITS - .comment sections, so let those pass. */ - if (!(shdr->sh_flags & SHF_ALLOC) && !strcmp (name, ".comment")) - continue; - - error (EXIT_FAILURE, 0, - _("cannot find matching section for [%Zu] '%s'"), - elf_ndxscn (scn), name); } + if (sec == NULL) + error (EXIT_FAILURE, 0, + _("cannot find matching section for [%Zu] '%s'"), + elf_ndxscn (scn), name); + sec->outscn = scn; } @@ -1374,6 +1424,7 @@ copy_elided_sections (Elf *unstripped, Elf *stripped, { /* This was created by stripping. We don't want it. */ debuglink = secndx; + ndx_section[secndx - 1] = SHN_UNDEF; continue; } @@ -1418,7 +1469,16 @@ copy_elided_sections (Elf *unstripped, Elf *stripped, GElf_Shdr *shdr = gelf_getshdr (sec->outscn, &shdr_mem); ELF_CHECK (shdr != NULL, _("cannot get section header: %s")); - shdr_mem.sh_addr = sec->shdr.sh_addr; + /* In an ET_REL file under --relocate, the sh_addr of SHF_ALLOC + sections will have been set nonzero by relocation. This + touched the shdrs of whichever file had the symtab. sh_addr + is still zero in the corresponding shdr. The relocated + address is what we want to use. */ + if (stripped_ehdr->e_type != ET_REL + || !(shdr_mem.sh_flags & SHF_ALLOC) + || shdr_mem.sh_addr == 0) + shdr_mem.sh_addr = sec->shdr.sh_addr; + shdr_mem.sh_type = sec->shdr.sh_type; shdr_mem.sh_size = sec->shdr.sh_size; shdr_mem.sh_info = sec->shdr.sh_info; @@ -1525,13 +1585,14 @@ copy_elided_sections (Elf *unstripped, Elf *stripped, size_t symndx_map[total_syms]; if (stripped_symtab != NULL) - collect_symbols (unstripped, stripped_symtab->scn, + collect_symbols (unstripped, stripped_ehdr->e_type == ET_REL, + stripped_symtab->scn, elf_getscn (stripped, stripped_symtab->shdr.sh_link), stripped_nsym, 0, ndx_section, symbols, symndx_map, NULL); Elf_Scn *unstripped_strtab = elf_getscn (unstripped, shdr->sh_link); - collect_symbols (unstripped, + collect_symbols (unstripped, stripped_ehdr->e_type == ET_REL, unstripped_symtab, unstripped_strtab, unstripped_nsym, stripped_ehdr->e_type == ET_REL ? 0 : bias, NULL, &symbols[stripped_nsym - 1], @@ -1542,11 +1603,15 @@ copy_elided_sections (Elf *unstripped, Elf *stripped, /* Now we can weed out the duplicates. Assign remaining symbols new slots, collecting a map from old indices to new. */ - size_t nsym = *symbols[0].map = 1; - for (size_t i = 1; i < total_syms; ++i) - *symbols[i].map = (!compare_symbols (&symbols[i - 1], &symbols[i]) - ? 0 /* This is a duplicate. */ - : ++nsym); /* Allocate the next slot. */ + size_t nsym = 0; + for (struct symbol *s = symbols; s < &symbols[total_syms]; ++s) + /* Skip a section symbol for a removed section, or a duplicate. */ + *s->map = (((s->shndx == SHN_UNDEF + && GELF_ST_TYPE (s->info.info) == STT_SECTION) + || (s + 1 < &symbols[total_syms] + && !compare_symbols (s, s + 1))) ? 0 + /* Allocate the next slot. */ + : ++nsym); /* Now we sort again, to determine the order in the output. */ qsort (symbols, total_syms, sizeof symbols[0], compare_symbols_output); @@ -1627,14 +1692,18 @@ copy_elided_sections (Elf *unstripped, Elf *stripped, &symndx_map[stripped_nsym - 1]); } else if (stripped_symtab != NULL && stripped_shnum != unstripped_shnum) - check_symtab_section_symbols (unstripped, stripped_symtab->scn, + check_symtab_section_symbols (unstripped, + stripped_ehdr->e_type == ET_REL, + stripped_symtab->scn, unstripped_shnum, unstripped_shstrndx, stripped_symtab->outscn, stripped_shnum, stripped_shstrndx, debuglink); if (stripped_dynsym != NULL) - (void) check_symtab_section_symbols (unstripped, stripped_dynsym->outscn, + (void) check_symtab_section_symbols (unstripped, + stripped_ehdr->e_type == ET_REL, + stripped_dynsym->outscn, unstripped_shnum, unstripped_shstrndx, stripped_dynsym->scn, stripped_shnum, @@ -1861,7 +1930,7 @@ handle_explicit_files (const char *output_file, bool create_dirs, /* Handle a pair of files opened implicitly by libdwfl for one module. */ static void handle_dwfl_module (const char *output_file, bool create_dirs, - Dwfl_Module *mod, bool all, bool ignore) + Dwfl_Module *mod, bool all, bool ignore, bool relocate) { GElf_Addr bias; Elf *stripped = dwfl_module_getelf (mod, &bias); @@ -1922,25 +1991,38 @@ handle_dwfl_module (const char *output_file, bool create_dirs, if (stripped_ehdr.e_type == ET_REL) { - /* We can't use the Elf handles already open, - because the DWARF sections have been relocated. */ + if (!relocate) + { + /* We can't use the Elf handles already open, + because the DWARF sections have been relocated. */ - const char *stripped_file = NULL; - const char *unstripped_file = NULL; - (void) dwfl_module_info (mod, NULL, NULL, NULL, NULL, NULL, - &stripped_file, &unstripped_file); + const char *stripped_file = NULL; + const char *unstripped_file = NULL; + (void) dwfl_module_info (mod, NULL, NULL, NULL, NULL, NULL, + &stripped_file, &unstripped_file); - handle_explicit_files (output_file, create_dirs, - stripped_file, unstripped_file); + handle_explicit_files (output_file, create_dirs, + stripped_file, unstripped_file); + return; + } + + /* Relocation is what we want! This ensures that all sections that can + get sh_addr values assigned have them, even ones not used in DWARF. + They might still be used in the symbol table. */ + if (dwfl_module_relocations (mod) < 0) + error (EXIT_FAILURE, 0, + _("cannot cache section addresses for module '%s': %s"), + dwfl_module_info (mod, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + dwfl_errmsg (-1)); } - else - handle_file (output_file, create_dirs, stripped, &stripped_ehdr, debug); + + handle_file (output_file, create_dirs, stripped, &stripped_ehdr, debug); } /* Handle one module being written to the output directory. */ static void handle_output_dir_module (const char *output_dir, Dwfl_Module *mod, - bool all, bool ignore, bool modnames) + bool all, bool ignore, bool modnames, bool relocate) { if (! modnames) { @@ -1960,7 +2042,7 @@ handle_output_dir_module (const char *output_dir, Dwfl_Module *mod, if (asprintf (&output_file, "%s/%s", output_dir, modnames ? name : file) < 0) error (EXIT_FAILURE, 0, _("memory exhausted")); - handle_dwfl_module (output_file, true, mod, all, ignore); + handle_dwfl_module (output_file, true, mod, all, ignore, relocate); } @@ -2073,12 +2155,13 @@ handle_implicit_modules (const struct arg_info *info) if (next (offset) != 0) error (EXIT_FAILURE, 0, _("matched more than one module")); handle_dwfl_module (info->output_file, false, mmi.found, - info->all, info->ignore); + info->all, info->ignore, info->relocate); } else do handle_output_dir_module (info->output_dir, mmi.found, - info->all, info->ignore, info->modnames); + info->all, info->ignore, + info->modnames, info->relocate); while ((offset = next (offset)) > 0); } |