]> git.ipfire.org Git - thirdparty/elfutils.git/commitdiff
src/
authorRoland McGrath <roland@redhat.com>
Fri, 18 May 2007 08:59:43 +0000 (08:59 +0000)
committerRoland McGrath <roland@redhat.com>
Fri, 18 May 2007 08:59:43 +0000 (08:59 +0000)
2007-05-18  Roland McGrath  <roland@redhat.com>

* unstrip.c (copy_elided_sections): Match up non-NOBITS sections with
stripped file, so as not to duplicate a section copied in both.

* strip.c (handle_elf): Keep SHT_NOTE section copies in the debug file.

tests/
2007-05-18  Roland McGrath  <roland@redhat.com>

* run-strip-test4.sh (stripped, debugfile): Use new reference files.
* testfile37.bz2: New data file.
* testfile37.debug.bz2: New data file.
* run-unstrip-test2.sh: New file.
* Makefile.am (TESTS, EXTRA_DIST): Add them.

24 files changed:
ChangeLog
configure.ac
libdwfl/ChangeLog
libdwfl/linux-kernel-modules.c
libdwfl/offline.c
src/ChangeLog
src/addr2line.c
src/elflint.c
src/strip.c
src/unstrip.c
tests/ChangeLog
tests/Makefile.am
tests/run-dwfl-bug-offline-rel.sh [new file with mode: 0755]
tests/run-strip-test.sh
tests/run-strip-test4.sh
tests/run-strip-test6.sh
tests/run-unstrip-test.sh [new file with mode: 0755]
tests/run-unstrip-test2.sh [new file with mode: 0755]
tests/testfile35.bz2 [new file with mode: 0644]
tests/testfile35.debug.bz2 [new file with mode: 0644]
tests/testfile36.bz2 [new file with mode: 0644]
tests/testfile36.debug.bz2 [new file with mode: 0644]
tests/testfile37.bz2 [new file with mode: 0644]
tests/testfile37.debug.bz2 [new file with mode: 0644]

index 760c4d2d873d5e70e224752273fc3140d1e26d9f..b9f088a0b39a8e2c244136a7832531a09d27f633 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+2007-05-16  Roland McGrath  <roland@redhat.com>
+
+       * configure.ac (AM_INIT_AUTOMAKE): Use -Wno-portability.
+
 2006-11-02  Roland McGrath  <roland@redhat.com>
 
        * Makefile.am (EXTRA_DIST): Add EXCEPTION file.
index 596219e56289e115d040dd6a09668e59818f5447..b6856ccd871e634ac7706fceca68faa66b9600c0 100644 (file)
@@ -25,7 +25,8 @@ AC_CONFIG_FILES([config/Makefile])
 AC_COPYRIGHT([Copyright (C) 1996-2003, 2004, 2005, 2006, 2007 Red Hat, Inc.])
 AC_PREREQ(2.59)                        dnl Minimum Autoconf version required.
 
-AM_INIT_AUTOMAKE([gnits 1.7])
+dnl We use GNU make extensions; automake 1.10 defaults to -Wportability.
+AM_INIT_AUTOMAKE([gnits 1.7 -Wno-portability])
 AM_MAINTAINER_MODE
 
 dnl Unique ID for this build.
index 39c7ee29d0ae98d9b40ce70d6077a47f9a9fa350..6e6407ae25a1bb4ffec7e2860d46d73fdae906fc 100644 (file)
@@ -1,3 +1,23 @@
+2007-05-17  Roland McGrath  <roland@redhat.com>
+
+       * linux-kernel-modules.c (dwfl_linux_kernel_report_offline): Look at
+       whole /lib/modules/VERSION tree, not just /lib/modules/VERSION/kernel.
+       (dwfl_linux_kernel_find_elf): Likewise.
+
+       * linux-kernel-modules.c (dwfl_linux_kernel_report_modules): Use
+       getline and sscanf instead of fscanf.
+
+2007-05-08  Roland McGrath  <roland@redhat.com>
+
+       * offline.c (dwfl_offline_section_address): Don't assume section
+       numbers match between stripped and debuginfo files.  Instead, assume
+       only that the ordering among SHF_ALLOC sections matches.
+
+       * linux-kernel-modules.c (report_kernel): Change RELEASE argument to
+       pointer to string.
+       (dwfl_linux_kernel_report_offline): Update caller.
+       (dwfl_linux_kernel_report_kernel): Likewise.
+
 2007-04-23  Roland McGrath  <roland@redhat.com>
 
        * argp-std.c (options): Fix group title string.
index 2aaa25acb942954f644c70762522dbcfda42a898..98957521b4ca074af175609499fd7df751be49a0 100644 (file)
@@ -139,21 +139,24 @@ find_kernel_elf (Dwfl *dwfl, const char *release, char **fname)
 }
 
 static int
-report_kernel (Dwfl *dwfl, const char *release,
+report_kernel (Dwfl *dwfl, const char **release,
               int (*predicate) (const char *module, const char *file))
 {
   if (dwfl == NULL)
     return -1;
 
-  if (release == NULL)
+  const char *release_string = release == NULL ? NULL : *release;
+  if (release_string == NULL)
     {
-      release = kernel_release ();
-      if (release == NULL)
+      release_string = kernel_release ();
+      if (release_string == NULL)
        return errno;
+      if (release != NULL)
+       *release = release_string;
     }
 
   char *fname;
-  int fd = find_kernel_elf (dwfl, release, &fname);
+  int fd = find_kernel_elf (dwfl, release_string, &fname);
 
   int result = 0;
   if (fd < 0)
@@ -198,17 +201,17 @@ dwfl_linux_kernel_report_offline (Dwfl *dwfl, const char *release,
                                                    const char *file))
 {
   /* First report the kernel.  */
-  int result = report_kernel (dwfl, release, predicate);
+  int result = report_kernel (dwfl, &release, predicate);
   if (result == 0)
     {
-      /* Do "find /lib/modules/RELEASE/kernel -name *.ko".  */
+      /* Do "find /lib/modules/RELEASE -name *.ko".  */
 
       char *modulesdir[] = { NULL, NULL };
       if (release[0] == '/')
        modulesdir[0] = (char *) release;
       else
        {
-         if (asprintf (&modulesdir[0], MODULEDIRFMT "/kernel", release) < 0)
+         if (asprintf (&modulesdir[0], MODULEDIRFMT, release) < 0)
            return errno;
        }
 
@@ -394,10 +397,10 @@ dwfl_linux_kernel_find_elf (Dwfl_Module *mod __attribute__ ((unused)),
   if (!strcmp (module_name, KERNEL_MODNAME))
     return find_kernel_elf (mod->dwfl, release, file_name);
 
-  /* Do "find /lib/modules/`uname -r`/kernel -name MODULE_NAME.ko".  */
+  /* Do "find /lib/modules/`uname -r` -name MODULE_NAME.ko".  */
 
   char *modulesdir[] = { NULL, NULL };
-  if (asprintf (&modulesdir[0], MODULEDIRFMT "/kernel", release) < 0)
+  if (asprintf (&modulesdir[0], MODULEDIRFMT, release) < 0)
     return -1;
 
   FTS *fts = fts_open (modulesdir, FTS_LOGICAL | FTS_NOSTAT, NULL);
@@ -609,14 +612,21 @@ dwfl_linux_kernel_report_modules (Dwfl *dwfl)
   Dwarf_Addr modaddr;
   unsigned long int modsz;
   char modname[128];
-  while (fscanf (f, "%128s %lu %*s %*s %*s %" PRIx64 "\n",
-                modname, &modsz, &modaddr) == 3)
+  char *line = NULL;
+  size_t linesz = 0;
+  /* We can't just use fscanf here because it's not easy to distinguish \n
+     from other whitespace so as to take the optional word following the
+     address but always stop at the end of the line.  */
+  while (getline (&line, &linesz, f) > 0
+        && sscanf (line, "%128s %lu %*s %*s %*s %" PRIx64 " %*s\n",
+                   modname, &modsz, &modaddr) == 3)
     if (INTUSE(dwfl_report_module) (dwfl, modname,
                                    modaddr, modaddr + modsz) == NULL)
       {
        result = -1;
        break;
       }
+  free (line);
 
   if (result == 0)
     result = ferror_unlocked (f) ? errno : feof_unlocked (f) ? 0 : ENOEXEC;
index beeb0abf2fc8431acdfdc9f065d2e1201e5dd3ba..0a0645ef3a8ecf18c5ea5b2df006f7f5e36e8e9e 100644 (file)
@@ -65,21 +65,53 @@ dwfl_offline_section_address (Dwfl_Module *mod,
                              const GElf_Shdr *shdr __attribute__ ((unused)),
                              Dwarf_Addr *addr)
 {
-  GElf_Shdr shdr_mem;
-  GElf_Shdr *main_shdr = gelf_getshdr (elf_getscn (mod->main.elf, shndx),
-                                      &shdr_mem);
-  if (unlikely (main_shdr == NULL))
-    return -1;
-
+  assert (mod->e_type == ET_REL);
   assert (shdr->sh_addr == 0);
   assert (shdr->sh_flags & SHF_ALLOC);
-  assert (main_shdr->sh_flags == shdr->sh_flags);
 
-  if (main_shdr->sh_addr != 0)
-    assert (mod->symfile != &mod->main);
+  if (mod->symfile == &mod->main)
+    {
+      /* Because the actual address is zero, we failed to notice
+        we in fact had the right address cached already.  */
+      *addr = 0;
+      return 0;
+    }
+
+  /* The section numbers might not match between the two files.
+     The best we can rely on is the order of SHF_ALLOC sections.  */
+
+  Elf_Scn *ourscn = elf_getscn (mod->symfile->elf, shndx);
+  Elf_Scn *scn = NULL;
+  uint_fast32_t skip_alloc = 0;
+  while ((scn = elf_nextscn (mod->symfile->elf, scn)) != ourscn)
+    {
+      assert (scn != NULL);
+      GElf_Shdr shdr_mem;
+      GElf_Shdr *sh = gelf_getshdr (scn, &shdr_mem);
+      if (unlikely (sh == NULL))
+       return -1;
+      if (sh->sh_flags & SHF_ALLOC)
+       ++skip_alloc;
+    }
+
+  scn = NULL;
+  while ((scn = elf_nextscn (mod->main.elf, scn)) != NULL)
+    {
+      GElf_Shdr shdr_mem;
+      GElf_Shdr *main_shdr = gelf_getshdr (elf_getscn (mod->main.elf, shndx),
+                                          &shdr_mem);
+      if (unlikely (main_shdr == NULL))
+       return -1;
+      if ((main_shdr->sh_flags & SHF_ALLOC) && skip_alloc-- == 0)
+       {
+         assert (main_shdr->sh_flags == shdr->sh_flags);
+         *addr = main_shdr->sh_addr;
+         return 0;
+       }
+    }
 
-  *addr = main_shdr->sh_addr;
-  return 0;
+  /* This should never happen.  */
+  return -1;
 }
 INTDEF (dwfl_offline_section_address)
 
index 39b64ff0926327198dbf88ec1cd20aafebcd6a99..d227695136e30e8d6e7465a976e5a6d72a2e6949 100644 (file)
@@ -1,3 +1,58 @@
+2007-05-18  Roland McGrath  <roland@redhat.com>
+
+       * unstrip.c (copy_elided_sections): Match up non-NOBITS sections with
+       stripped file, so as not to duplicate a section copied in both.
+
+       * strip.c (handle_elf): Keep SHT_NOTE section copies in the debug file.
+
+2007-05-17  Roland McGrath  <roland@redhat.com>
+
+       * unstrip.c (copy_elided_sections): Don't call gelf_newphdr for 0.
+
+       * unstrip.c (handle_file): Tweak BIAS != 0 warning.
+
+       * unstrip.c (handle_file): Take new arg CREATE_DIRS.  If set,
+       call make_directories here.
+       (handle_explicit_files): Take new arg CREATE_DIRS, pass it down.
+       (handle_dwfl_module): Likewise.
+       (handle_implicit_modules): Update callers.
+       (handle_output_dir_module): Likewise.  Don't do make_directories here.
+
+       * unstrip.c (get_section_name): New function, broken out of ...
+       (copy_elided_sections): here.  Update callers.
+       (find_alloc_section): Broken out of ...
+       (copy_elided_sections): ... here.  Update caller.
+       (symtab_count_leading_section_symbols): Take new arg NEWSYMDATA,
+       update STT_SECTION symbols' st_value fields as a side effect.
+       (check_symtab_section_symbols): Update caller.
+       (add_new_section_symbols): Set st_value in symbols added.
+       (collect_symbols): Reset S->value for STT_SECTION symbols recorded.
+       Take new arg SPLIT_BSS.  Adjust S->shndx recorded for symbols moved
+       from .bss to .dynbss.
+       (find_alloc_sections_prelink): New function.  Associate debug file
+       allocated SHT_NOBITS shdrs with stripped moved by prelink via
+       .gnu.prelink_undo information.
+       (copy_elided_sections): Call it when we couldn't find every allocated
+       section.  Don't use a debug file non-NOBITS section if SHF_ALLOC.
+       Take STRIPPED_EHDR arg instead of E_TYPE and PHNUM.
+       (handle_file): Update callers.
+
+       * unstrip.c (copy_elided_sections): Ignore unfound unallocated section
+       named ".comment".
+
+       * elflint.c (check_sections): Fix association of segments with
+       sections when p_memsz > p_filesz.
+
+2007-04-29  Roland McGrath  <roland@redhat.com>
+
+       * addr2line.c (options, main): Tweak argp group settings to fix
+       usage output.
+
+2007-04-28  Roland McGrath  <roland@redhat.com>
+
+       * strip.c (handle_elf): Update debug file's SHT_NOBITS sections'
+       sizes to match sections adjusted in the stripped file.
+
 2007-04-24  Roland McGrath  <roland@redhat.com>
 
        * elfcmp.c (OPT_HASH_INEXACT): New macro.
index e133e7af919275206b311c1544d7984d9959207b..6f6de1f7eeedb8aa6ffa633ea3ae537bb577fa76 100644 (file)
@@ -61,7 +61,7 @@ const char *argp_program_bug_address = PACKAGE_BUGREPORT;
 /* Definitions of arguments for argp functions.  */
 static const struct argp_option options[] =
 {
-  { NULL, 0, NULL, 0, N_("Output Selection:"), 0 },
+  { NULL, 0, NULL, 0, N_("Output selection options:"), 2 },
   { "basenames", 's', NULL, 0, N_("Show only base names of source files"), 0 },
   { "absolute", 'A', NULL, 0,
     N_("Show absolute file names using compilation directory"), 0 },
@@ -131,6 +131,7 @@ main (int argc, char *argv[])
 
   /* Parse and process arguments.  This includes opening the modules.  */
   argp_children[0].argp = dwfl_standard_argp ();
+  argp_children[0].group = 1;
   Dwfl *dwfl = NULL;
   (void) argp_parse (&argp, argc, argv, 0, &remaining, &dwfl);
   assert (dwfl != NULL);
index 09c7fbd2f545b3517c0a4b9c3c1d56669902a9da..db3b49c1b5a1df1c7305981803998cc78a2f8391 100644 (file)
@@ -3407,7 +3407,9 @@ section [%2zu] '%s': merge flag set but entry size is zero\n"),
                    || (phdr->p_type == PT_TLS
                        && (shdr->sh_flags & SHF_TLS) != 0))
                && phdr->p_offset <= shdr->sh_offset
-               && phdr->p_offset + phdr->p_memsz > shdr->sh_offset)
+               && (phdr->p_offset + phdr->p_filesz > shdr->sh_offset
+                   || (phdr->p_offset + phdr->p_memsz > shdr->sh_offset
+                       && shdr->sh_type == SHT_NOBITS)))
              {
                /* Found the segment.  */
                if (phdr->p_offset + phdr->p_memsz
index 95eded6577a550d05ef653788567c7c164381265..5a2da64642a1f86d4186fb0dd81e65cb3b542206 100644 (file)
@@ -835,6 +835,7 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname,
                   elf_errmsg (-1));
 
          bool discard_section = (shdr_info[cnt].idx > 0
+                                 && shdr_info[cnt].shdr.sh_type != SHT_NOTE
                                  && cnt != ehdr->e_shstrndx);
 
          /* Set the section header in the new file.  */
@@ -1251,6 +1252,24 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname,
         symbol table.  */
       for (cnt = 1; cnt <= shdridx; ++cnt)
        {
+         /* Update section headers when the data size has changed.
+            We also update the SHT_NOBITS section in the debug
+            file so that the section headers match in sh_size.  */
+         inline void update_section_size (const Elf_Data *newdata)
+           {
+             GElf_Shdr shdr_mem;
+             GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+             shdr->sh_size = newdata->d_size;
+             (void) gelf_update_shdr (scn, shdr);
+             if (debugelf != NULL)
+               {
+                 /* libelf will use d_size to set sh_size.  */
+                 Elf_Data *debugdata = elf_getdata (elf_getscn (debugelf,
+                                                                cnt), NULL);
+                 debugdata->d_size = newdata->d_size;
+               }
+           }
+
          if (shdr_info[cnt].idx == 0 && debug_fname == NULL)
            /* Ignore sections which are discarded.  When we are saving a
               relocation section in a separate debug file, we must fix up
@@ -1354,12 +1373,9 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname,
                  Elf32_Word *chain = bucket + nbucket;
 
                  /* New size of the section.  */
-                 GElf_Shdr shdr_mem;
-                 GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
-                 shdr->sh_size = hashd->d_size
-                   = (2 + symd->d_size / elsize + nbucket)
-                     * sizeof (Elf32_Word);
-                 (void) gelf_update_shdr (scn, shdr);
+                 hashd->d_size = ((2 + symd->d_size / elsize + nbucket)
+                                  * sizeof (Elf32_Word));
+                 update_section_size (hashd);
 
                  /* Clear the arrays.  */
                  memset (bucket, '\0',
@@ -1411,12 +1427,9 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname,
                  Elf64_Xword *chain = bucket + nbucket;
 
                  /* New size of the section.  */
-                 GElf_Shdr shdr_mem;
-                 GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
-                 shdr->sh_size = hashd->d_size
-                   = (2 + symd->d_size / elsize + nbucket)
-                     * sizeof (Elf64_Xword);
-                 (void) gelf_update_shdr (scn, shdr);
+                 hashd->d_size = ((2 + symd->d_size / elsize + nbucket)
+                                  * sizeof (Elf64_Xword));
+                 update_section_size (hashd);
 
                  /* Clear the arrays.  */
                  memset (bucket, '\0',
@@ -1492,14 +1505,12 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname,
                  verstab[newsymidx[inner]] = verstab[inner];
 
              /* New size of the section.  */
-             GElf_Shdr shdr_mem;
-             GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
-             shdr->sh_size = verd->d_size
-               = gelf_fsize (newelf, verd->d_type,
-                             symd->d_size / gelf_fsize (elf, symd->d_type, 1,
-                                                        ehdr->e_version),
-                             ehdr->e_version);
-             (void) gelf_update_shdr (scn, shdr);
+             verd->d_size = gelf_fsize (newelf, verd->d_type,
+                                        symd->d_size
+                                        / gelf_fsize (elf, symd->d_type, 1,
+                                                      ehdr->e_version),
+                                        ehdr->e_version);
+             update_section_size (verd);
            }
          else if (shdr_info[cnt].shdr.sh_type == SHT_GROUP)
            {
index 25ce1f37ba07cdfc6d107bc64a26eeefc7c790b7..98e165ce8ddf74f1a4ed6cb4c706b2c9b5606d5b 100644 (file)
@@ -270,6 +270,27 @@ copy_elf (Elf *outelf, Elf *inelf)
     }
 }
 
+/* Create directories containing PATH.  */
+static void
+make_directories (const char *path)
+{
+  const char *lastslash = strrchr (path, '/');
+  if (lastslash == NULL)
+    return;
+
+  while (lastslash > path && lastslash[-1] == '/')
+    --lastslash;
+  if (lastslash == path)
+    return;
+
+  char *dir = strndupa (path, lastslash - path);
+  while (mkdir (dir, 0777) < 0 && errno != EEXIST)
+    if (errno == ENOENT)
+      make_directories (dir);
+    else
+      error (EXIT_FAILURE, errno, _("cannot create directory '%s'"), dir);
+}
+
 
 /* The binutils linker leaves gratuitous section symbols in .symtab
    that strip has to remove.  Older linkers likewise include a
@@ -293,9 +314,11 @@ section_can_shrink (const GElf_Shdr *shdr)
 }
 
 /* See if this symbol table has a leading section symbol for every single
-   section, in order.  The binutils linker produces this.  */
+   section, in order.  The binutils linker produces this.  While we're here,
+   update each section symbol's st_value.  */
 static size_t
-symtab_count_leading_section_symbols (Elf_Scn *scn, size_t shnum)
+symtab_count_leading_section_symbols (Elf *elf, Elf_Scn *scn, size_t shnum,
+                                     Elf_Data *newsymdata)
 {
   Elf_Data *data = elf_getdata (scn, NULL);
   Elf_Data *shndxdata = NULL;  /* XXX */
@@ -306,11 +329,22 @@ symtab_count_leading_section_symbols (Elf_Scn *scn, size_t shnum)
       GElf_Word shndx = SHN_UNDEF;
       GElf_Sym *sym = gelf_getsymshndx (data, shndxdata, i, &sym_mem, &shndx);
       ELF_CHECK (sym != NULL, _("cannot get symbol table entry: %s"));
+
+      GElf_Shdr shdr_mem;
+      GElf_Shdr *shdr = gelf_getshdr (elf_getscn (elf, i), &shdr_mem);
+      ELF_CHECK (shdr != NULL, _("cannot get section header: %s"));
+
       if (sym->st_shndx != SHN_XINDEX)
        shndx = sym->st_shndx;
 
       if (shndx != i || GELF_ST_TYPE (sym->st_info) != STT_SECTION)
        return i;
+
+      sym->st_value = shdr->sh_addr;
+      if (sym->st_shndx != SHN_XINDEX)
+       shndx = SHN_UNDEF;
+      ELF_CHECK (gelf_update_symshndx (newsymdata, shndxdata, i, sym, shndx),
+                _("cannot update symbol table: %s"));
     }
 
   return shnum;
@@ -489,7 +523,7 @@ adjust_all_relocs (Elf *elf, Elf_Scn *symtab, const GElf_Shdr *symshdr,
 
 /* The original file probably had section symbols for all of its
    sections, even the unallocated ones.  To match it as closely as
-   possible, to add in section symbols for the added sections.  */
+   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)
@@ -534,8 +568,12 @@ add_new_section_symbols (Elf_Scn *old_symscn, size_t old_shnum,
   /* Add in the new section symbols.  */
   for (size_t i = old_shnum; i < shnum; ++i)
     {
+      GElf_Shdr i_shdr_mem;
+      GElf_Shdr *i_shdr = gelf_getshdr (elf_getscn (elf, i), &i_shdr_mem);
+      ELF_CHECK (i_shdr != NULL, _("cannot get section header: %s"));
       GElf_Sym sym =
        {
+         .st_value = i_shdr->sh_addr,
          .st_info = GELF_ST_INFO (STB_LOCAL, STT_SECTION),
          .st_shndx = i < SHN_LORESERVE ? i : SHN_XINDEX
        };
@@ -565,13 +603,16 @@ add_new_section_symbols (Elf_Scn *old_symscn, size_t old_shnum,
   return symdata;
 }
 
+/* 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,
                              size_t shnum, size_t shstrndx,
                              Elf_Scn *oscn, size_t oshnum, size_t oshstrndx,
                              size_t debuglink)
 {
-  size_t n = symtab_count_leading_section_symbols (oscn, oshnum);
+  size_t n = symtab_count_leading_section_symbols (elf, oscn, oshnum,
+                                                  elf_getdata (scn, NULL));
 
   if (n == oshnum)
     return add_new_section_symbols (oscn, n, elf, scn, shnum);
@@ -659,9 +700,10 @@ struct symbol
 
 /* Collect input symbols into our internal form.  */
 static void
-collect_symbols (Elf_Scn *symscn, Elf_Scn *strscn,
+collect_symbols (Elf *outelf, Elf_Scn *symscn, Elf_Scn *strscn,
                 const size_t nent, const GElf_Addr bias,
-                const size_t scnmap[], struct symbol *table, size_t *map)
+                const size_t scnmap[], struct symbol *table, size_t *map,
+                struct section *split_bss)
 {
   Elf_Data *symdata = elf_getdata (symscn, NULL);
   Elf_Data *strdata = elf_getdata (strscn, NULL);
@@ -677,9 +719,6 @@ collect_symbols (Elf_Scn *symscn, Elf_Scn *strscn,
       if (sym->st_shndx != SHN_XINDEX)
        shndx = sym->st_shndx;
 
-      if (scnmap != NULL && shndx != SHN_UNDEF && shndx < SHN_LORESERVE)
-       shndx = scnmap[shndx - 1];
-
       if (sym->st_name >= strdata->d_size)
        error (EXIT_FAILURE, 0,
               _("invalid string offset in symbol [%Zu]"), i);
@@ -692,6 +731,27 @@ collect_symbols (Elf_Scn *symscn, Elf_Scn *strscn,
       s->shndx = shndx;
       s->info.info = sym->st_info;
       s->info.other = sym->st_other;
+
+      if (scnmap != NULL && shndx != SHN_UNDEF && shndx < SHN_LORESERVE)
+       s->shndx = scnmap[shndx - 1];
+
+      if (GELF_ST_TYPE (s->info.info) == STT_SECTION)
+       {
+         GElf_Shdr shdr_mem;
+         GElf_Shdr *shdr = gelf_getshdr (elf_getscn (outelf, 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;
+       }
+      else if (split_bss != NULL
+              && s->value < split_bss->shdr.sh_addr
+              && s->value >= split_bss[-1].shdr.sh_addr
+              && shndx == elf_ndxscn (split_bss->outscn))
+       /* This symbol was in .bss and was split into .dynbss.  */
+       s->shndx = elf_ndxscn (split_bss[-1].outscn);
     }
 }
 
@@ -759,11 +819,286 @@ compare_symbols_output (const void *a, const void *b)
 
 #undef CMP
 
+/* Locate a matching allocated section in SECTIONS.  */
+static struct section *
+find_alloc_section (const GElf_Shdr *shdr, GElf_Addr bias, const char *name,
+                   struct section sections[], size_t nalloc)
+{
+  const GElf_Addr addr = shdr->sh_addr + bias;
+  size_t l = 0, u = nalloc;
+  while (l < u)
+    {
+      size_t i = (l + u) / 2;
+      if (addr < sections[i].shdr.sh_addr)
+       u = i;
+      else if (addr > sections[i].shdr.sh_addr)
+       l = i + 1;
+      else
+       {
+         /* We've found allocated sections with this address.
+            Find one with matching size, flags, and name.  */
+         while (i > 0 && sections[i - 1].shdr.sh_addr == addr)
+           --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))
+             return &sections[i];
+         break;
+       }
+    }
+  return NULL;
+}
+
+static inline const char *
+get_section_name (size_t ndx, const GElf_Shdr *shdr, const Elf_Data *shstrtab)
+{
+  if (shdr->sh_name >= shstrtab->d_size)
+    error (EXIT_FAILURE, 0, _("cannot read section [%Zu] name: %s"),
+          ndx, elf_errmsg (-1));
+  return shstrtab->d_buf + shdr->sh_name;
+}
+
+/* Fix things up when prelink has moved some allocated sections around
+   and the debuginfo file's section headers no longer match up.
+   This fills in SECTIONS[0..NALLOC-1].outscn or exits.
+   If there was a .bss section that was split into two sections
+   with the new one preceding it in sh_addr, we return that pointer.  */
+static struct section *
+find_alloc_sections_prelink (Elf *debug, Elf_Data *debug_shstrtab,
+                            Elf *main, const GElf_Ehdr *main_ehdr,
+                            Elf_Data *main_shstrtab, GElf_Addr bias,
+                            struct section *sections,
+                            size_t nalloc, size_t nsections)
+{
+  /* Clear assignments that might have been bogus.  */
+  for (size_t i = 0; i < nalloc; ++i)
+    sections[i].outscn = NULL;
+
+  Elf_Scn *undo = NULL;
+  for (size_t i = nalloc; i < nsections; ++i)
+    {
+      const struct section *sec = &sections[i];
+      if (sec->shdr.sh_type == SHT_PROGBITS
+         && !(sec->shdr.sh_flags & SHF_ALLOC)
+         && !strcmp (sec->name, ".gnu.prelink_undo"))
+       {
+         undo = sec->scn;
+         break;
+       }
+    }
+
+  /* Find the original allocated sections before prelinking.  */
+  struct section *undo_sections = NULL;
+  size_t undo_nalloc = 0;
+  if (undo != NULL)
+    {
+      Elf_Data *undodata = elf_rawdata (undo, NULL);
+      ELF_CHECK (undodata != NULL,
+                _("cannot read '.gnu.prelink_undo' section: %s"));
+
+      union
+      {
+       Elf32_Ehdr e32;
+       Elf64_Ehdr e64;
+      } ehdr;
+      Elf_Data dst =
+       {
+         .d_buf = &ehdr,
+         .d_size = sizeof ehdr,
+         .d_type = ELF_T_EHDR,
+         .d_version = EV_CURRENT
+       };
+      Elf_Data src = *undodata;
+      src.d_size = gelf_fsize (main, ELF_T_EHDR, 1, EV_CURRENT);
+      src.d_type = ELF_T_EHDR;
+      ELF_CHECK (gelf_xlatetom (main, &dst, &src,
+                               main_ehdr->e_ident[EI_DATA]) != NULL,
+                _("cannot read '.gnu.prelink_undo' section: %s"));
+
+      uint_fast16_t phnum;
+      uint_fast16_t shnum;
+      if (ehdr.e32.e_ident[EI_CLASS] == ELFCLASS32)
+       {
+         phnum = ehdr.e32.e_phnum;
+         shnum = ehdr.e32.e_shnum;
+       }
+      else
+       {
+         phnum = ehdr.e64.e_phnum;
+         shnum = ehdr.e64.e_shnum;
+       }
+
+      size_t phsize = gelf_fsize (main, ELF_T_PHDR, phnum, EV_CURRENT);
+      src.d_buf += src.d_size + phsize;
+      src.d_size = gelf_fsize (main, ELF_T_SHDR, shnum - 1, EV_CURRENT);
+      src.d_type = ELF_T_SHDR;
+      if ((size_t) (src.d_buf - undodata->d_buf) > undodata->d_size
+         || undodata->d_size - (src.d_buf - undodata->d_buf) != src.d_size)
+       error (EXIT_FAILURE, 0, _("invalid contents in '%s' section"),
+              ".gnu.prelink_undo");
+
+      union
+      {
+       Elf32_Shdr s32[shnum - 1];
+       Elf64_Shdr s64[shnum - 1];
+      } shdr;
+      dst.d_buf = &shdr;
+      dst.d_size = sizeof shdr;
+      ELF_CHECK (gelf_xlatetom (main, &dst, &src,
+                               main_ehdr->e_ident[EI_DATA]) != NULL,
+                _("cannot read '.gnu.prelink_undo' section: %s"));
+
+      undo_sections = xmalloc ((shnum - 1) * sizeof undo_sections[0]);
+      for (size_t i = 0; i < shnum - 1; ++i)
+       {
+         struct section *sec = &undo_sections[undo_nalloc];
+         if (ehdr.e32.e_ident[EI_CLASS] == ELFCLASS32)
+           {
+#define COPY(field) sec->shdr.field = shdr.s32[i].field
+             COPY (sh_name);
+             COPY (sh_type);
+             COPY (sh_flags);
+             COPY (sh_addr);
+             COPY (sh_offset);
+             COPY (sh_size);
+             COPY (sh_link);
+             COPY (sh_info);
+             COPY (sh_addralign);
+             COPY (sh_entsize);
+#undef COPY
+           }
+         else
+           sec->shdr = shdr.s64[i];
+         if (sec->shdr.sh_flags & SHF_ALLOC)
+           {
+             sec->shdr.sh_addr += bias;
+             sec->name = get_section_name (i + 1, &sec->shdr, main_shstrtab);
+             sec->scn = elf_getscn (main, i + 1); /* Really just for ndx.  */
+             sec->outscn = NULL;
+             sec->strent = NULL;
+             ++undo_nalloc;
+           }
+       }
+      qsort (undo_sections, undo_nalloc,
+            sizeof undo_sections[0], compare_sections);
+    }
+
+  bool fail = false;
+  inline void check_match (bool match, Elf_Scn *scn, const char *name)
+    {
+      if (!match)
+       {
+         fail = true;
+         error (0, 0, _("cannot find matching section for [%Zu] '%s'"),
+                elf_ndxscn (scn), name);
+       }
+    }
+
+  Elf_Scn *scn = NULL;
+  while ((scn = elf_nextscn (debug, scn)) != NULL)
+    {
+      GElf_Shdr shdr_mem;
+      GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+      ELF_CHECK (shdr != NULL, _("cannot get section header: %s"));
+
+      if (!(shdr->sh_flags & SHF_ALLOC))
+       continue;
+
+      const char *name = get_section_name (elf_ndxscn (scn), shdr,
+                                          debug_shstrtab);
+
+      if (undo_sections != NULL)
+       {
+         struct section *sec = find_alloc_section (shdr, 0, name,
+                                                   undo_sections,
+                                                   undo_nalloc);
+         if (sec != NULL)
+           {
+             sec->outscn = scn;
+             continue;
+           }
+       }
+
+      /* If there is no prelink info, we are just here to find
+        the sections to give error messages about.  */
+      for (size_t i = 0; shdr != NULL && i < nalloc; ++i)
+       if (sections[i].outscn == scn)
+         shdr = NULL;
+      check_match (shdr == NULL, scn, name);
+    }
+
+  if (fail)
+    exit (EXIT_FAILURE);
+
+  /* Now we have lined up output sections for each of the original sections
+     before prelinking.  Translate those to the prelinked sections.
+     This matches what prelink's undo_sections does.  */
+  struct section *split_bss = NULL;
+  for (size_t i = 0; i < undo_nalloc; ++i)
+    {
+      const struct section *undo_sec = &undo_sections[i];
+
+      const char *name = undo_sec->name;
+      scn = undo_sec->scn; /* This is just for elf_ndxscn.  */
+
+      for (size_t j = 0; j < nalloc; ++j)
+       {
+         struct section *sec = &sections[j];
+#define RELA_SCALED(field) \
+         (2 * sec->shdr.field == 3 * undo_sec->shdr.field)
+         if (sec->outscn == NULL
+             && sec->shdr.sh_name == undo_sec->shdr.sh_name
+             && sec->shdr.sh_flags == undo_sec->shdr.sh_flags
+             && sec->shdr.sh_addralign == undo_sec->shdr.sh_addralign
+             && (((sec->shdr.sh_type == undo_sec->shdr.sh_type
+                   && sec->shdr.sh_entsize == undo_sec->shdr.sh_entsize
+                   && (sec->shdr.sh_size == undo_sec->shdr.sh_size
+                       || (sec->shdr.sh_size > undo_sec->shdr.sh_size
+                           && main_ehdr->e_type == ET_EXEC
+                           && !strcmp (sec->name, ".dynstr"))))
+                  || (sec->shdr.sh_size == undo_sec->shdr.sh_size
+                      && ((sec->shdr.sh_entsize == undo_sec->shdr.sh_entsize
+                           && undo_sec->shdr.sh_type == SHT_NOBITS)
+                          || undo_sec->shdr.sh_type == SHT_PROGBITS)
+                      && !strcmp (sec->name, ".plt")))
+                 || (sec->shdr.sh_type == SHT_RELA
+                     && undo_sec->shdr.sh_type == SHT_REL
+                     && RELA_SCALED (sh_entsize) && RELA_SCALED (sh_size))
+                 || (sec->shdr.sh_entsize == undo_sec->shdr.sh_entsize
+                     && (sec->shdr.sh_type == undo_sec->shdr.sh_type
+                         || (sec->shdr.sh_type == SHT_PROGBITS
+                             && undo_sec->shdr.sh_type == SHT_NOBITS))
+                     && sec->shdr.sh_size < undo_sec->shdr.sh_size
+                     && (!strcmp (sec->name, ".bss")
+                         || !strcmp (sec->name, ".sbss"))
+                     && (split_bss = sec) > sections)))
+           {
+             sec->outscn = undo_sec->outscn;
+             undo_sec = NULL;
+             break;
+           }
+       }
+
+      check_match (undo_sec == NULL, scn, name);
+    }
+
+  free (undo_sections);
+
+  if (fail)
+    exit (EXIT_FAILURE);
+
+  return split_bss;
+}
+
 /* Fill in any SHT_NOBITS sections in UNSTRIPPED by
    copying their contents and sh_type from STRIPPED.  */
 static void
-copy_elided_sections (Elf *unstripped, Elf *stripped, uint_fast16_t e_type,
-                     uint_fast16_t phnum, GElf_Addr bias)
+copy_elided_sections (Elf *unstripped, Elf *stripped,
+                     const GElf_Ehdr *stripped_ehdr, GElf_Addr bias)
 {
   size_t unstripped_shstrndx;
   ELF_CHECK (elf_getshstrndx (unstripped, &unstripped_shstrndx) == 0,
@@ -811,39 +1146,6 @@ copy_elided_sections (Elf *unstripped, Elf *stripped, uint_fast16_t e_type,
        stripped_symtab = &sections[nalloc];
     }
 
-  /* Locate a matching allocated section in SECTIONS.  */
-  inline struct section *find_alloc_section (const GElf_Shdr *shdr,
-                                            const char *name)
-    {
-      const GElf_Addr addr = shdr->sh_addr + bias;
-      size_t l = 0, u = nalloc;
-      while (l < u)
-       {
-         size_t i = (l + u) / 2;
-         if (addr < sections[i].shdr.sh_addr)
-           u = i;
-         else if (addr > sections[i].shdr.sh_addr)
-           l = i + 1;
-         else
-           {
-             /* We've found allocated sections with this address.
-                Find one with matching size, flags, and name.  */
-             while (i > 0 && sections[i - 1].shdr.sh_addr == addr)
-               --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))
-                 return &sections[i];
-             break;
-           }
-       }
-      return NULL;
-    }
-
   /* Locate a matching unallocated section in SECTIONS.  */
   inline struct section *find_unalloc_section (const GElf_Shdr *shdr,
                                               const char *name)
@@ -869,16 +1171,9 @@ copy_elided_sections (Elf *unstripped, Elf *stripped, uint_fast16_t e_type,
                                                unstripped_shstrndx), NULL);
   ELF_CHECK (shstrtab != NULL,
             _("cannot read section header string table: %s"));
-  inline const char *unstripped_section_name (Elf_Scn *sec,
-                                             const GElf_Shdr *shdr)
-    {
-      if (shdr->sh_name >= shstrtab->d_size)
-       error (EXIT_FAILURE, 0, _("cannot read section [%Zu] name: %s"),
-              elf_ndxscn (sec), elf_errmsg (-1));
-      return shstrtab->d_buf + shdr->sh_name;
-    }
 
   /* Match each debuginfo section with its corresponding stripped section.  */
+  bool check_prelink = false;
   Elf_Scn *unstripped_symtab = NULL;
   size_t unstripped_strtab_ndx = SHN_UNDEF;
   scn = NULL;
@@ -888,31 +1183,68 @@ copy_elided_sections (Elf *unstripped, Elf *stripped, uint_fast16_t e_type,
       GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
       ELF_CHECK (shdr != NULL, _("cannot get section header: %s"));
 
-      /* Anything not already SHT_NOBITS is fine as it stands.  */
-      if (shdr->sh_type != SHT_NOBITS)
+      if (shdr->sh_type == SHT_SYMTAB)
        {
-         if (shdr->sh_type == SHT_SYMTAB)
-           {
-             unstripped_symtab = scn;
-             unstripped_strtab_ndx = shdr->sh_link;
-           }
+         unstripped_symtab = scn;
+         unstripped_strtab_ndx = shdr->sh_link;
          continue;
        }
 
-      const char *name = unstripped_section_name (scn, shdr);
+      const size_t ndx = elf_ndxscn (scn);
+      if (ndx == unstripped_shstrndx)
+       continue;
+
+      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, name)
+                            ? find_alloc_section (shdr, bias, name,
+                                                  sections, nalloc)
                             : find_unalloc_section (shdr, name));
       if (sec == NULL)
-       error (EXIT_FAILURE, 0,
-              _("cannot find matching section for [%Zu] '%s'"),
-              elf_ndxscn (scn), name);
+       {
+         if ((shdr->sh_flags & SHF_ALLOC) && stripped_ehdr->e_type != ET_REL)
+           {
+             /* If we couldn't figure it out, it may be a prelink issue.  */
+             check_prelink = true;
+             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);
+       }
 
       sec->outscn = scn;
     }
 
+  /* If that failed due to changes made by prelink, we take another tack.
+     We keep track of a .bss section that was partly split into .dynbss
+     so that collect_symbols can update symbols' st_shndx fields.  */
+  struct section *split_bss = NULL;
+  if (check_prelink)
+    {
+      Elf_Data *data = elf_getdata (elf_getscn (stripped, stripped_shstrndx),
+                                   NULL);
+      ELF_CHECK (data != NULL,
+                _("cannot read section header string table: %s"));
+      split_bss = find_alloc_sections_prelink (unstripped, shstrtab,
+                                              stripped, stripped_ehdr,
+                                              data, bias, sections,
+                                              nalloc, stripped_shnum - 1);
+    }
+
   /* Make sure each main file section has a place to go.  */
   const struct section *stripped_dynsym = NULL;
   size_t debuglink = SHN_UNDEF;
@@ -1006,7 +1338,7 @@ copy_elided_sections (Elf *unstripped, Elf *stripped, uint_fast16_t e_type,
            scn = elf_getscn (unstripped, i + 1);
            GElf_Shdr shdr_mem;
            GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
-           const char *name = unstripped_section_name (scn, shdr);
+           const char *name = get_section_name (i + 1, shdr, shstrtab);
            unstripped_strent[i] = ebl_strtabadd (strtab, name, 0);
            ELF_CHECK (unstripped_strent[i] != NULL,
                       _("cannot add section name to string table: %s"));
@@ -1075,7 +1407,7 @@ copy_elided_sections (Elf *unstripped, Elf *stripped, uint_fast16_t e_type,
        elf_flagdata (outdata, ELF_C_SET, ELF_F_DIRTY);
 
        /* Preserve the file layout of the allocated sections.  */
-       if (e_type != ET_REL && (shdr_mem.sh_flags & SHF_ALLOC))
+       if (stripped_ehdr->e_type != ET_REL && (shdr_mem.sh_flags & SHF_ALLOC))
          {
            shdr_mem.sh_offset = sec->shdr.sh_offset;
            placed[elf_ndxscn (sec->outscn) - 1] = true;
@@ -1140,7 +1472,9 @@ copy_elided_sections (Elf *unstripped, Elf *stripped, uint_fast16_t e_type,
   struct Ebl_Strtab *symstrtab = NULL;
   Elf_Data *symstrdata = NULL;
   if (unstripped_symtab != NULL && (stripped_symtab != NULL
-                                   || (e_type != ET_REL && bias != 0)))
+                                   || check_prelink /* Section adjustments. */
+                                   || (stripped_ehdr->e_type != ET_REL
+                                       && bias != 0)))
     {
       /* Merge the stripped file's symbol table into the unstripped one.  */
       const size_t stripped_nsym = (stripped_symtab == NULL ? 1
@@ -1159,16 +1493,17 @@ copy_elided_sections (Elf *unstripped, Elf *stripped, uint_fast16_t e_type,
       size_t symndx_map[total_syms];
 
       if (stripped_symtab != NULL)
-       collect_symbols (stripped_symtab->scn,
+       collect_symbols (unstripped, stripped_symtab->scn,
                         elf_getscn (stripped, stripped_symtab->shdr.sh_link),
                         stripped_nsym, 0, ndx_section,
-                        symbols, symndx_map);
+                        symbols, symndx_map, NULL);
 
       Elf_Scn *unstripped_strtab = elf_getscn (unstripped, shdr->sh_link);
-      collect_symbols (unstripped_symtab, unstripped_strtab,
-                      unstripped_nsym, e_type == ET_REL ? 0 : bias, NULL,
+      collect_symbols (unstripped,
+                      unstripped_symtab, unstripped_strtab, unstripped_nsym,
+                      stripped_ehdr->e_type == ET_REL ? 0 : bias, NULL,
                       &symbols[stripped_nsym - 1],
-                      &symndx_map[stripped_nsym - 1]);
+                      &symndx_map[stripped_nsym - 1], split_bss);
 
       /* Next, sort our array of all symbols.  */
       qsort (symbols, total_syms, sizeof symbols[0], compare_symbols);
@@ -1243,13 +1578,17 @@ copy_elided_sections (Elf *unstripped, Elf *stripped, uint_fast16_t e_type,
       ELF_CHECK (gelf_update_shdr (unstripped_symtab, shdr),
                 _("cannot update section header: %s"));
 
-      /* Adjust any relocations referring to the old symbol table.  */
-      const size_t old_sh_link = elf_ndxscn (stripped_symtab->scn);
-      for (const struct section *sec = sections;
-          sec < &sections[stripped_shnum - 1];
-          ++sec)
-       if (sec->outscn != NULL && sec->shdr.sh_link == old_sh_link)
-         adjust_relocs (sec->outscn, sec->scn, &sec->shdr, symndx_map, shdr);
+      if (stripped_symtab != NULL)
+       {
+         /* Adjust any relocations referring to the old symbol table.  */
+         const size_t old_sh_link = elf_ndxscn (stripped_symtab->scn);
+         for (const struct section *sec = sections;
+              sec < &sections[stripped_shnum - 1];
+              ++sec)
+           if (sec->outscn != NULL && sec->shdr.sh_link == old_sh_link)
+             adjust_relocs (sec->outscn, sec->scn, &sec->shdr,
+                            symndx_map, shdr);
+       }
 
       /* Also adjust references to the other old symbol table.  */
       adjust_all_relocs (unstripped, unstripped_symtab, shdr,
@@ -1328,8 +1667,12 @@ copy_elided_sections (Elf *unstripped, Elf *stripped, uint_fast16_t e_type,
          }
     } while (skip_reloc);
 
+  if (stripped_ehdr->e_phnum > 0)
+    ELF_CHECK (gelf_newphdr (unstripped, stripped_ehdr->e_phnum),
+              _("cannot create program headers: %s"));
+
   /* Copy each program header from the stripped file.  */
-  for (uint_fast16_t i = 0; i < phnum; ++i)
+  for (uint_fast16_t i = 0; i < stripped_ehdr->e_phnum; ++i)
     {
       GElf_Phdr phdr_mem;
       GElf_Phdr *phdr = gelf_getphdr (stripped, i, &phdr_mem);
@@ -1360,7 +1703,7 @@ copy_elided_sections (Elf *unstripped, Elf *stripped, uint_fast16_t e_type,
 
 /* Process one pair of files, already opened.  */
 static void
-handle_file (const char *output_file,
+handle_file (const char *output_file, bool create_dirs,
             Elf *stripped, const GElf_Ehdr *stripped_ehdr,
             Elf *unstripped)
 {
@@ -1387,15 +1730,24 @@ handle_file (const char *output_file,
 
   /* One day we could adjust all the DWARF data (like prelink itself does).  */
   if (bias != 0)
-    error (0, 0,
-          _("DWARF output not adjusted for prelinking bias; use prelink -u"));
+    {
+      if (output_file == NULL)
+       error (0, 0, _("\
+DWARF data not adjusted for prelinking bias; consider prelink -u"));
+      else
+       error (0, 0, _("\
+DWARF data in '%s' not adjusted for prelinking bias; consider prelink -u"),
+              output_file);
+    }
 
   if (output_file == NULL)
     /* Modify the unstripped file in place.  */
-    copy_elided_sections (unstripped, stripped, stripped_ehdr->e_type,
-                         stripped_ehdr->e_phnum, bias);
+    copy_elided_sections (unstripped, stripped, stripped_ehdr, bias);
   else
     {
+      if (create_dirs)
+       make_directories (output_file);
+
       /* Copy the unstripped file and then modify it.  */
       int outfd = open64 (output_file, O_RDWR | O_CREAT,
                          stripped_ehdr->e_type == ET_REL ? 0666 : 0777);
@@ -1416,8 +1768,7 @@ handle_file (const char *output_file,
       else
        {
          copy_elf (outelf, unstripped);
-         copy_elided_sections (outelf, stripped, stripped_ehdr->e_type,
-                               stripped_ehdr->e_phnum, bias);
+         copy_elided_sections (outelf, stripped, stripped_ehdr, bias);
        }
 
       elf_end (outelf);
@@ -1436,7 +1787,7 @@ open_file (const char *file, bool writable)
 
 /* Handle a pair of files we need to open by name.  */
 static void
-handle_explicit_files (const char *output_file,
+handle_explicit_files (const char *output_file, bool create_dirs,
                       const char *stripped_file, const char *unstripped_file)
 {
   int stripped_fd = open_file (stripped_file, false);
@@ -1465,7 +1816,7 @@ handle_explicit_files (const char *output_file,
               stripped_file, unstripped_file);
     }
 
-  handle_file (output_file, stripped, &stripped_ehdr, unstripped);
+  handle_file (output_file, create_dirs, stripped, &stripped_ehdr, unstripped);
 
   elf_end (stripped);
   close (stripped_fd);
@@ -1477,8 +1828,8 @@ handle_explicit_files (const char *output_file,
 
 /* Handle a pair of files opened implicitly by libdwfl for one module.  */
 static void
-handle_dwfl_module (const char *output_file, Dwfl_Module *mod,
-                   bool all, bool ignore)
+handle_dwfl_module (const char *output_file, bool create_dirs,
+                   Dwfl_Module *mod, bool all, bool ignore)
 {
   GElf_Addr bias;
   Elf *stripped = dwfl_module_getelf (mod, &bias);
@@ -1547,31 +1898,11 @@ handle_dwfl_module (const char *output_file, Dwfl_Module *mod,
       (void) dwfl_module_info (mod, NULL, NULL, NULL, NULL, NULL,
                               &stripped_file, &unstripped_file);
 
-      handle_explicit_files (output_file, stripped_file, unstripped_file);
+      handle_explicit_files (output_file, create_dirs,
+                            stripped_file, unstripped_file);
     }
   else
-    handle_file (output_file, stripped, &stripped_ehdr, debug);
-}
-
-/* Create directories containing PATH.  */
-static void
-make_directories (const char *path)
-{
-  const char *lastslash = strrchr (path, '/');
-  if (lastslash == NULL)
-    return;
-
-  while (lastslash > path && lastslash[-1] == '/')
-    --lastslash;
-  if (lastslash == path)
-    return;
-
-  char *dir = strndupa (path, lastslash - path);
-  while (mkdir (dir, 0777) < 0 && errno != EEXIST)
-    if (errno == ENOENT)
-      make_directories (dir);
-    else
-      error (EXIT_FAILURE, errno, _("cannot create directory '%s'"), dir);
+    handle_file (output_file, create_dirs, stripped, &stripped_ehdr, debug);
 }
 
 /* Handle one module being written to the output directory.  */
@@ -1597,8 +1928,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"));
 
-  make_directories (output_file);
-  handle_dwfl_module (output_file, mod, all, ignore);
+  handle_dwfl_module (output_file, true, mod, all, ignore);
 }
 
 
@@ -1665,7 +1995,7 @@ 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, mmi.found,
+      handle_dwfl_module (info->output_file, false, mmi.found,
                          info->all, info->ignore);
     }
   else
@@ -1747,12 +2077,12 @@ name of the main file complete with directory underneath OUTPUT-DIRECTORY.")
          char *file;
          if (asprintf (&file, "%s/%s", info.output_dir, info.args[0]) < 0)
            error (EXIT_FAILURE, 0, _("memory exhausted"));
-         make_directories (file);
-         handle_explicit_files (file, info.args[0], info.args[1]);
+         handle_explicit_files (file, true, info.args[0], info.args[1]);
          free (file);
        }
       else
-       handle_explicit_files (info.output_file, info.args[0], info.args[1]);
+       handle_explicit_files (info.output_file, false,
+                              info.args[0], info.args[1]);
     }
   else
     {
index 89fbab7cb8514a8c92f0af96de2bbbba4afcd32c..3424c1e2c99d8c4a61d160db8d5fed77c2fdf899 100644 (file)
@@ -1,3 +1,28 @@
+2007-05-18  Roland McGrath  <roland@redhat.com>
+
+       * run-strip-test4.sh (stripped, debugfile): Use new reference files.
+       * testfile37.bz2: New data file.
+       * testfile37.debug.bz2: New data file.
+       * run-unstrip-test2.sh: New file.
+       * Makefile.am (TESTS, EXTRA_DIST): Add them.
+
+2007-05-10  Roland McGrath  <roland@redhat.com>
+
+       * run-dwfl-bug-offline-rel.sh: New file.
+       * testfile36.bz2: New data file.
+       * testfile36.debug.bz2: New data file.
+       * Makefile.am (TESTS, EXTRA_DIST): Add them.
+
+2007-04-28  Roland McGrath  <roland@redhat.com>
+
+       * run-strip-test6.sh (stripped, debugfile): Use new reference files.
+       * testfile35.bz2: New data file.
+       * testfile35.debug.bz2: New data file.
+       * run-unstrip-test.sh: New file.
+       * Makefile.am (TESTS, EXTRA_DIST): Add them.
+
+       * run-strip-test.sh: Do all elflint and cmp runs even when some fail.
+
 2007-04-26  Roland McGrath  <roland@redhat.com>
 
        * run-elflint-self.sh: Run all tests even if one fails.
index 0715fff83a3eca7f48992fb1d211ae5462afa81f..f485acd119d5fd38989fa19c5ac1dac50c43e98b 100644 (file)
@@ -71,13 +71,15 @@ TESTS = run-arextract.sh run-arsymtest.sh newfile test-nlist \
        run-show-abbrev.sh run-line2addr.sh hash \
        newscn run-strip-test.sh run-strip-test2.sh \
        run-strip-test3.sh run-strip-test4.sh run-strip-test5.sh \
-       run-strip-test6.sh run-ecp-test.sh run-ecp-test2.sh \
+       run-strip-test6.sh run-unstrip-test.sh run-unstrip-test2.sh \
+       run-ecp-test.sh run-ecp-test2.sh \
        run-elflint-test.sh run-elflint-self.sh run-ranlib-test.sh \
        run-ranlib-test2.sh run-ranlib-test3.sh run-ranlib-test4.sh \
        run-addrscopes.sh run-strings-test.sh run-funcscopes.sh \
        run-find-prologues.sh run-allregs.sh run-readelf-test1.sh \
        run-native-test.sh run-bug1-test.sh \
-       dwfl-bug-addr-overflow run-addrname-test.sh dwfl-bug-fd-leak
+       dwfl-bug-addr-overflow run-addrname-test.sh dwfl-bug-fd-leak \
+       run-dwfl-bug-offline-rel.sh
 # run-show-ciefde.sh
 
 if !STANDALONE
@@ -102,11 +104,12 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh \
             testfile13.bz2 run-strip-test3.sh run-allfcts.sh \
             run-line2addr.sh run-elflint-test.sh testfile14.bz2 \
             run-strip-test4.sh run-strip-test5.sh run-strip-test6.sh \
+            run-unstrip-test.sh run-unstrip-test2.sh \
             run-elflint-self.sh run-ranlib-test.sh run-ranlib-test2.sh \
             run-ranlib-test3.sh run-ranlib-test4.sh \
             run-addrscopes.sh run-strings-test.sh run-funcscopes.sh \
             run-find-prologues.sh run-allregs.sh run-native-test.sh \
-            run-addrname-test.sh \
+            run-addrname-test.sh run-dwfl-bug-offline-rel.sh \
             testfile15.bz2 testfile15.debug.bz2 \
             testfile16.bz2 testfile16.debug.bz2 \
             testfile17.bz2 testfile17.debug.bz2 \
@@ -119,7 +122,8 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh \
             run-bug1-test.sh testfile28.bz2 testfile28.rdwr.bz2 \
             testfile29.bz2 testfile29.rdwr.bz2 \
             testfile30.bz2 testfile31.bz2 testfile32.bz2 testfile33.bz2 \
-            testfile34.bz2
+            testfile34.bz2 testfile35.bz2 testfile35.debug.bz2
+            testfile36.bz2 testfile36.debug.bz2
 
 installed_TESTS_ENVIRONMENT = libdir=$(DESTDIR)$(libdir) \
                              bindir=$(DESTDIR)$(bindir) \
diff --git a/tests/run-dwfl-bug-offline-rel.sh b/tests/run-dwfl-bug-offline-rel.sh
new file mode 100755 (executable)
index 0000000..40e90be
--- /dev/null
@@ -0,0 +1,36 @@
+#! /bin/sh
+# Copyright (C) 2007 Red Hat, Inc.
+# This file is part of Red Hat elfutils.
+#
+# Red Hat elfutils is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by the
+# Free Software Foundation; version 2 of the License.
+#
+# Red Hat elfutils is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with Red Hat elfutils; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
+#
+# Red Hat elfutils is an included package of the Open Invention Network.
+# An included package of the Open Invention Network is a package for which
+# Open Invention Network licensees cross-license their patents.  No patent
+# license is granted, either expressly or impliedly, by designation as an
+# included package.  Should you wish to participate in the Open Invention
+# Network licensing program, please visit www.openinventionnetwork.com
+# <http://www.openinventionnetwork.com>.
+
+. $srcdir/test-subr.sh
+
+testfiles testfile36 testfile36.debug
+
+testrun_compare ./dwflmodtest -e testfile36 <<\EOF
+module:                                00000000..00002308 testfile36 (null)
+module:                                00000000        (nil) 0 (Callback returned failure)
+module:                                00000000..00002308 testfile36 testfile36.debug
+EOF
+
+exit 0
index 9a82d53d289bbbb4c5758b2f507abf90697c97fd..480101ebd9a97775b3379bafbf2eefaeae261476 100755 (executable)
@@ -36,16 +36,18 @@ tempfiles testfile.temp testfile.debug.temp testfile.unstrip
 
 testrun ../src/strip -o testfile.temp $debugout $original
 
-cmp $stripped testfile.temp
+status=0
+
+cmp $stripped testfile.temp || status=$?
 
 # Check elflint and the expected result.
-testrun ../src/elflint -q testfile.temp
+testrun ../src/elflint -q testfile.temp || status=$?
 
 test -z "$debugfile" || {
-cmp $debugfile testfile.debug.temp
+cmp $debugfile testfile.debug.temp || status=$?
 
 # Check elflint and the expected result.
-testrun ../src/elflint -q -d testfile.debug.temp
+testrun ../src/elflint -q -d testfile.debug.temp || status=$?
 
 # Now test unstrip recombining those files.
 testrun ../src/unstrip -o testfile.unstrip testfile.temp testfile.debug.temp
@@ -54,4 +56,4 @@ testrun ../src/unstrip -o testfile.unstrip testfile.temp testfile.debug.temp
 testrun ../src/elfcmp --hash-inexact $original testfile.unstrip
 }
 
-exit 0
+exit $status
index 8e9be228b15955fd9d489b5362890f3b9ca20f20..64924a92884aed8b2a26ae09e3a1c54f8ecab783 100755 (executable)
@@ -1,5 +1,5 @@
 original=testfile11
-stripped=testfile15
-debugfile=testfile15.debug
+stripped=testfile37
+debugfile=testfile37.debug
 
 . $srcdir/run-strip-test.sh
index 8ee5f02cb4d51aa907b5677c76b7c5eb61e892f6..c59bf5e437051715a127b20560a925e294d5e06b 100755 (executable)
@@ -1,5 +1,5 @@
 original=testfile12
-stripped=testfile17
-debugfile=testfile17.debug
+stripped=testfile35
+debugfile=testfile35.debug
 
 . $srcdir/run-strip-test.sh
diff --git a/tests/run-unstrip-test.sh b/tests/run-unstrip-test.sh
new file mode 100755 (executable)
index 0000000..6702905
--- /dev/null
@@ -0,0 +1,41 @@
+#! /bin/sh
+# Copyright (C) 2007 Red Hat, Inc.
+# This file is part of Red Hat elfutils.
+#
+# Red Hat elfutils is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by the
+# Free Software Foundation; version 2 of the License.
+#
+# Red Hat elfutils is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with Red Hat elfutils; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
+#
+# Red Hat elfutils is an included package of the Open Invention Network.
+# An included package of the Open Invention Network is a package for which
+# Open Invention Network licensees cross-license their patents.  No patent
+# license is granted, either expressly or impliedly, by designation as an
+# included package.  Should you wish to participate in the Open Invention
+# Network licensing program, please visit www.openinventionnetwork.com
+# <http://www.openinventionnetwork.com>.
+
+. $srcdir/test-subr.sh
+
+original=${original:-testfile12}
+stripped=${stripped:-testfile17}
+debugfile=${debugfile:-${stripped}.debug}
+
+testfiles $original $stripped $debugfile
+
+# These are old reference output from run-test-strip6.sh, when
+# strip left the .debug file with unchanged sh_size in
+# stripped sections that shrank in the stripped file.  strip
+# no longer does that, but unstrip must still handle it.
+
+testrun ../src/unstrip -o testfile.unstrip $stripped $debugfile
+
+testrun ../src/elfcmp --hash-inexact $original testfile.unstrip
diff --git a/tests/run-unstrip-test2.sh b/tests/run-unstrip-test2.sh
new file mode 100755 (executable)
index 0000000..44074c1
--- /dev/null
@@ -0,0 +1,5 @@
+original=testfile11
+stripped=testfile15
+debugfile=testfile15.debug
+
+. $srcdir/run-unstrip-test.sh
diff --git a/tests/testfile35.bz2 b/tests/testfile35.bz2
new file mode 100644 (file)
index 0000000..b591301
Binary files /dev/null and b/tests/testfile35.bz2 differ
diff --git a/tests/testfile35.debug.bz2 b/tests/testfile35.debug.bz2
new file mode 100644 (file)
index 0000000..f191862
Binary files /dev/null and b/tests/testfile35.debug.bz2 differ
diff --git a/tests/testfile36.bz2 b/tests/testfile36.bz2
new file mode 100644 (file)
index 0000000..e912a19
Binary files /dev/null and b/tests/testfile36.bz2 differ
diff --git a/tests/testfile36.debug.bz2 b/tests/testfile36.debug.bz2
new file mode 100644 (file)
index 0000000..76aca42
Binary files /dev/null and b/tests/testfile36.debug.bz2 differ
diff --git a/tests/testfile37.bz2 b/tests/testfile37.bz2
new file mode 100644 (file)
index 0000000..254ce32
Binary files /dev/null and b/tests/testfile37.bz2 differ
diff --git a/tests/testfile37.debug.bz2 b/tests/testfile37.debug.bz2
new file mode 100644 (file)
index 0000000..74e46a8
Binary files /dev/null and b/tests/testfile37.debug.bz2 differ