]> git.ipfire.org Git - thirdparty/elfutils.git/commitdiff
2007-10-23 Roland McGrath <roland@redhat.com>
authorRoland McGrath <roland@redhat.com>
Tue, 23 Oct 2007 13:07:39 +0000 (13:07 +0000)
committerRoland McGrath <roland@redhat.com>
Tue, 23 Oct 2007 13:07:39 +0000 (13:07 +0000)
* linux-kernel-modules.c (report_kernel_archive): Reorder the kernel
module to appear first.

39 files changed:
backends/ChangeLog
backends/i386_corenote.c
backends/linux-core-note.c
backends/ppc_corenote.c
backends/ppc_regs.c
libdw/ChangeLog
libdw/dwarf_child.c
libdw/dwarf_formref.c
libdw/dwarf_formref_die.c
libdw/dwarf_siblingof.c
libdw/libdw.h
libdw/libdwP.h
libdw/libdw_findcu.c
libdw/libdw_form.c
libdwfl/ChangeLog
libdwfl/derelocate.c
libdwfl/dwfl_module.c
libdwfl/dwfl_module_build_id.c
libdwfl/dwfl_module_getdwarf.c
libdwfl/dwfl_module_getsym.c
libdwfl/dwfl_report_elf.c
libdwfl/libdwflP.h
libdwfl/linux-kernel-modules.c
libdwfl/offline.c
libdwfl/relocate.c
libebl/ChangeLog
libebl/eblcorenotetypename.c
libelf/ChangeLog
libelf/elf.h
src/ChangeLog
src/readelf.c
src/unstrip.c
tests/ChangeLog
tests/Makefile.am
tests/dwfl-addr-sect.c
tests/run-allregs.sh
tests/run-dwfl-addr-sect.sh [new file with mode: 0755]
tests/test-subr.sh
tests/testfile43.bz2 [new file with mode: 0644]

index 082f4f3907c6cc74d7369b7b4cebf4b94fe65e3f..2bb61f2b41f79b2fd56bf8830a048369daa5af8a 100644 (file)
@@ -1,3 +1,14 @@
+2007-10-18  Roland McGrath  <roland@redhat.com>
+
+       * ppc_regs.c (ppc_register_info): Assign 67 to "vscr".
+       Return "vector" and 32 bits for vscr and vrsave.
+       * ppc_corenote.c (altivec_regs): New variable.
+       (EXTRA_NOTES): New macro, handle NT_PPC_VMX.
+
+       * linux-core-note.c (EXTRA_REGSET): New macro.
+       Remove NT_PRXFPREG case.  Instead, use EXTRA_NOTES if defined.
+       * i386_corenote.c (EXTRA_NOTES): Define it.
+
 2007-10-09  Roland McGrath  <roland@redhat.com>
 
        * sparc_auxv.c: New file.
index cc72a45f67365a45fa7454a24e4b65a30c2e59ec..f6c4c1dee3eb69040c41b8d2f7947f4714b0616f 100644 (file)
@@ -99,6 +99,8 @@ static const Ebl_Register_Location prxfpreg_regs[] =
     { .offset = 32, .regno = 11, .count = 8, .bits = 80, .pad = 6 }, /* stN */
     { .offset = 32 + 128, .regno = 21, .count = 8, .bits = 128 }, /* xmm */
   };
-#define PRXFPREG_SIZE  512
+
+#define        EXTRA_NOTES \
+  EXTRA_REGSET (NT_PRFPXREG, 512, prxfpreg_regs)
 
 #include "linux-core-note.c"
index c4a90b7062d89f49541cc3b3f9a324fe8c2bb0dc..3dc413738f6fd095997af279abbfee656d703f87 100644 (file)
@@ -198,28 +198,23 @@ EBLHOOK(core_note) (n_type, descsz,
       *items = prpsinfo_items;
       return 1;
 
-#ifdef FPREGSET_SIZE
-    case NT_FPREGSET:
-      if (descsz != FPREGSET_SIZE)
-       return 0;
-      *regs_offset = 0;
-      *nregloc = sizeof fpregset_regs / sizeof fpregset_regs[0];
-      *reglocs = fpregset_regs;
-      *nitems = 0;
-      *items = NULL;
+#define EXTRA_REGSET(type, size, table)                                              \
+    case type:                                                               \
+      if (descsz != size)                                                    \
+       return 0;                                                             \
+      *regs_offset = 0;                                                              \
+      *nregloc = sizeof table / sizeof table[0];                             \
+      *reglocs = table;                                                              \
+      *nitems = 0;                                                           \
+      *items = NULL;                                                         \
       return 1;
+
+#ifdef FPREGSET_SIZE
+    EXTRA_REGSET (NT_FPREGSET, FPREGSET_SIZE, fpregset_regs)
 #endif
 
-#ifdef PRXFPREG_SIZE
-    case NT_PRXFPREG:
-      if (descsz != PRXFPREG_SIZE)
-       return 0;
-      *regs_offset = 0;
-      *nregloc = sizeof prxfpreg_regs / sizeof prxfpreg_regs[0];
-      *reglocs = prxfpreg_regs;
-      *nitems = 0;
-      *items = NULL;
-      return 1;
+#ifdef EXTRA_NOTES
+    EXTRA_NOTES
 #endif
     }
 
index e9ff124c1145277aa978c83120c3d2f6ccf4a9f9..daadbb4810f0eb9e027e257d9bb26f541eae7613 100644 (file)
@@ -71,6 +71,19 @@ static const Ebl_Register_Location fpregset_regs[] =
   };
 #define FPREGSET_SIZE          (33 * 8)
 
+static const Ebl_Register_Location altivec_regs[] =
+  {
+    /* vr0-vr31 */
+    { .offset = 0, .regno = 1124, .count = 32, .bits = 128 },
+    /* vscr XXX 67 is an unofficial assignment */
+    { .offset = 32 * 16, .regno = 67, .count = 1, .bits = 32, .pad = 12 },
+    /* vrsave */
+    { .offset = 33 * 16, .regno = 356, .count = 1, .bits = 32, .pad = 12 }
+  };
+
+#define EXTRA_NOTES \
+  EXTRA_REGSET (NT_PPC_VMX, 34 * 16, altivec_regs)
+
 #if BITS == 32
 # define ULONG                 uint32_t
 # define ALIGN_ULONG           4
index 4cf5abc6d4e996df7ba6f6d0c77dca1d48d4f272..cc7d84fa74003c4d6cd6cf54c9e5a6a2decc222e 100644 (file)
@@ -58,13 +58,13 @@ ppc_register_info (Ebl *ebl __attribute__ ((unused)),
       if (ebl->machine != EM_PPC64 && regno < 64)
        *bits = 64;
     }
-  else if (regno < 1124)
-    *setname = "privileged";
-  else
+  else if (regno == 67 || regno == 356 || regno >= 1124)
     {
       *setname = "vector";
-      *bits = 128;
+      *bits = regno >= 1124 ? 128 : 32;
     }
+  else
+    *setname = "privileged";
 
   switch (regno)
     {
@@ -100,6 +100,8 @@ ppc_register_info (Ebl *ebl __attribute__ ((unused)),
       return stpcpy (name, "fpscr") + 1 - name;
     case 66:
       return stpcpy (name, "msr") + 1 - name;
+    case 67:                   /* XXX unofficial assignment */
+      return stpcpy (name, "vscr") + 1 - name;
 
     case 70 + 0 ... 70 + 9:
       name[0] = 's';
index d21f5d6483c7b55102ef358b5fb60147b0464ae3..b7cd62851a72d003dc2d585af2ee4430169f02f1 100644 (file)
@@ -1,3 +1,22 @@
+2007-10-17  Roland McGrath  <roland@redhat.com>
+
+       * libdw.h (__deprecated_attribute__): New macro.
+       (dwarf_formref): Mark it deprecated.
+       * dwarf_formref.c (__libdw_formref): New function, broken out of ...
+       (dwarf_formref): ... here.  Call it.  Remove INTDEF.
+       * libdwP.h: Remove INTDECL.
+       Declare __libdw_formref.
+       * dwarf_siblingof.c (dwarf_siblingof): Call __libdw_formref instead.
+       * dwarf_formref_die.c: Likewise.  Handle DW_FORM_ref_addr here.
+
+       * libdw_form.c (__libdw_form_val_len): Fix DW_FORM_ref_addr result,
+       needs to check CU->version.
+
+       * libdwP.h (struct Dwarf_CU): New member `version'.
+       * libdw_findcu.c (__libdw_findcu): Initialize it.
+
+       * dwarf_child.c: Return 1 for null entry as first child.
+
 2007-10-05  Roland McGrath  <roland@redhat.com>
 
        * dwarf_begin_elf.c (check_section): Punt on SHT_NOBITS sections.
index 42b38137a4a51646c88bd27e0646246862ba3cfd..b22b010eff456f9bce8a280312c2f093357dd967 100644 (file)
@@ -1,5 +1,5 @@
 /* Return vhild of current DIE.
-   Copyright (C) 2003, 2004, 2005, 2006 Red Hat, Inc.
+   Copyright (C) 2003, 2004, 2005, 2006, 2007 Red Hat, Inc.
    This file is part of Red Hat elfutils.
    Written by Ulrich Drepper <drepper@redhat.com>, 2003.
 
@@ -177,6 +177,15 @@ dwarf_child (die, result)
   if (addr == NULL)
     return -1;
 
+  /* It's kosher (just suboptimal) to have a null entry first thing (7.5.3).
+     So if this starts with ULEB128 of 0 (even with silly encoding of 0),
+     it is a kosher null entry and we do not really have any children.  */
+  const unsigned char *code = addr;
+  while (unlikely (*code == 0x80))
+    ++code;
+  if (unlikely (*code == '\0'))
+    return 1;
+
   /* RESULT can be the same as DIE.  So preserve what we need.  */
   struct Dwarf_CU *cu = die->cu;
 
index ac905c826b0671c6b0449bd9806407d885665eab..7c4fb71a532d068fb1d1a755d4227d7b6f56c227 100644 (file)
@@ -1,5 +1,5 @@
 /* Return reference offset represented by attribute.
-   Copyright (C) 2003, 2005 Red Hat, Inc.
+   Copyright (C) 2003, 2005, 2007 Red Hat, Inc.
    This file is part of Red Hat elfutils.
    Written by Ulrich Drepper <drepper@redhat.com>, 2003.
 
 #include <dwarf.h>
 #include "libdwP.h"
 
-
 int
-dwarf_formref (attr, return_offset)
+__libdw_formref (attr, return_offset)
      Dwarf_Attribute *attr;
      Dwarf_Off *return_offset;
 {
-  if (attr == NULL)
-    return -1;
-
   const unsigned char *datap;
 
   switch (attr->form)
@@ -100,4 +96,16 @@ dwarf_formref (attr, return_offset)
 
   return 0;
 }
-INTDEF(dwarf_formref)
+
+/* This is the old public entry point.
+   It is now deprecated in favor of dwarf_formref_die.  */
+int
+dwarf_formref (attr, return_offset)
+     Dwarf_Attribute *attr;
+     Dwarf_Off *return_offset;
+{
+  if (attr == NULL)
+    return -1;
+
+  return __libdw_formref (attr, return_offset);
+}
index 18ffe2fb6368182ab2cb444ee01116eb59b544a0..90a4b2d358d5301fc4ca7232ed34898c841b148e 100644 (file)
@@ -1,5 +1,5 @@
 /* Look up the DIE in a reference-form attribute.
-   Copyright (C) 2005 Red Hat, Inc.
+   Copyright (C) 2005, 2007 Red Hat, Inc.
    This file is part of Red Hat elfutils.
 
    Red Hat elfutils is free software; you can redistribute it and/or modify
 #endif
 
 #include "libdwP.h"
+#include <dwarf.h>
+
 
 Dwarf_Die *
-dwarf_formref_die (Dwarf_Attribute *attr, Dwarf_Die *die_mem)
+dwarf_formref_die (attr, die_mem)
+     Dwarf_Attribute *attr;
+     Dwarf_Die *die_mem;
 {
+  if (attr == NULL)
+    return NULL;
+
   Dwarf_Off offset;
-  return (unlikely (INTUSE(dwarf_formref) (attr, &offset) != 0) ? NULL
-         : INTUSE(dwarf_offdie) (attr->cu->dbg, attr->cu->start + offset,
-                                 die_mem));
+  if (attr->form == DW_FORM_ref_addr)
+    {
+      /* This has an absolute offset.  */
+
+      uint8_t ref_size = (attr->cu->version == 2
+                         ? attr->cu->address_size
+                         : attr->cu->offset_size);
+
+      if (ref_size == 8)
+       offset = read_8ubyte_unaligned (attr->cu->dbg, attr->valp);
+      else
+       offset = read_4ubyte_unaligned (attr->cu->dbg, attr->valp);
+    }
+  else
+    {
+      /* Other forms produce an offset from the CU.  */
+      if (unlikely (__libdw_formref (attr, &offset) != 0))
+       return NULL;
+      offset += attr->cu->start;
+    }
+
+  return INTUSE(dwarf_offdie) (attr->cu->dbg, offset, die_mem);
 }
 INTDEF (dwarf_formref_die)
index 00e5a1c761fa97e51686a6326500602ba5fd442d..a6cca394b5646dac3abe9387d01d2771ad4b2045 100644 (file)
@@ -1,5 +1,5 @@
 /* Return sibling of given DIE.
-   Copyright (C) 2003, 2004, 2005 Red Hat, Inc.
+   Copyright (C) 2003, 2004, 2005, 2007 Red Hat, Inc.
    This file is part of Red Hat elfutils.
    Written by Ulrich Drepper <drepper@redhat.com>, 2003.
 
@@ -92,7 +92,7 @@ dwarf_siblingof (die, result)
        {
          Dwarf_Off offset;
          sibattr.valp = addr;
-         if (INTUSE(dwarf_formref) (&sibattr, &offset) != 0)
+         if (__libdw_formref (&sibattr, &offset) != 0)
            /* Something went wrong.  */
            return -1;
 
index 70a35b0469526b3e63ff1c5d61bd2e4e148e5693..6242d04f8cef941aaa18811cc51204bf2d7c967a 100644 (file)
 
 #if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 3)
 # define __nonnull_attribute__(...) __attribute__ ((__nonnull__ (__VA_ARGS__)))
+# define __deprecated_attribute__ __attribute__ ((__deprecated__))
 #else
 # define __nonnull_attribute__(args...)
+# define __deprecated_attribute__
 #endif
 
+
 #ifdef __GNUC_STDC_INLINE__
 # define __libdw_extern_inline extern __inline __attribute__ ((__gnu_inline__))
 #else
@@ -310,9 +313,10 @@ extern int dwarf_formsdata (Dwarf_Attribute *attr, Dwarf_Sword *return_uval)
 extern int dwarf_formaddr (Dwarf_Attribute *attr, Dwarf_Addr *return_addr)
      __nonnull_attribute__ (2);
 
-/* Return reference offset represented by attribute.  */
+/* This function is deprecated.  Always use dwarf_formref_die instead.
+   Return reference offset represented by attribute.  */
 extern int dwarf_formref (Dwarf_Attribute *attr, Dwarf_Off *return_offset)
-     __nonnull_attribute__ (2);
+     __nonnull_attribute__ (2) __deprecated_attribute__;
 
 /* Look up the DIE in a reference-form attribute.  */
 extern Dwarf_Die *dwarf_formref_die (Dwarf_Attribute *attr, Dwarf_Die *die_mem)
index f069075b98e34de7b460a06865c7e859b5df50f1..78fd5ce7d31d4e9daa20dacdfe658c22c08ba81b 100644 (file)
@@ -267,6 +267,7 @@ struct Dwarf_CU
   Dwarf_Off end;
   uint8_t address_size;
   uint8_t offset_size;
+  uint16_t version;
 
   /* Hash table for the abbreviations.  */
   Dwarf_Abbrev_Hash abbrev_hash;
@@ -365,6 +366,11 @@ extern size_t __libdw_form_val_len (Dwarf *dbg, struct Dwarf_CU *cu,
                                    const unsigned char *valp)
      __nonnull_attribute__ (1, 2, 4) internal_function;
 
+/* Helper function for DW_FORM_ref* handling.  */
+extern int __libdw_formref (Dwarf_Attribute *attr, Dwarf_Off *return_offset)
+     __nonnull_attribute__ (1, 2) internal_function;
+
+
 /* Helper function to locate attribute.  */
 extern unsigned char *__libdw_find_attr (Dwarf_Die *die,
                                         unsigned int search_name,
@@ -411,7 +417,6 @@ INTDECL (dwarf_entrypc)
 INTDECL (dwarf_errmsg)
 INTDECL (dwarf_formaddr)
 INTDECL (dwarf_formblock)
-INTDECL (dwarf_formref)
 INTDECL (dwarf_formref_die)
 INTDECL (dwarf_formsdata)
 INTDECL (dwarf_formstring)
index e6259d4934364906889cb0b8432e99310fcc3e44..afff6d3a5208fc387feac2281523befb6cf07c51 100644 (file)
@@ -1,5 +1,5 @@
 /* Find CU for given offset.
-   Copyright (C) 2003, 2004, 2005 Red Hat, Inc.
+   Copyright (C) 2003, 2004, 2005, 2007 Red Hat, Inc.
    This file is part of Red Hat elfutils.
    Written by Ulrich Drepper <drepper@redhat.com>, 2003.
 
@@ -97,6 +97,7 @@ __libdw_findcu (dbg, start)
 
   if (start < dbg->next_cu_offset)
     {
+    invalid:
       __libdw_seterrno (DWARF_E_INVALID_DWARF);
       return NULL;
     }
@@ -115,6 +116,15 @@ __libdw_findcu (dbg, start)
        /* No more entries.  */
        return NULL;
 
+      /* XXX We need the version number but dwarf_nextcu swallows it.  */
+      const char *bytes = (dbg->sectiondata[IDX_debug_info]->d_buf + oldoff
+                          + (2 * offset_size - 4));
+      uint16_t version = read_2ubyte_unaligned (dbg, bytes);
+
+      /* We only know how to handle the DWARF version 2 and 3 formats.  */
+      if (unlikely (version != 2) && unlikely (version != 3))
+       goto invalid;
+
       /* Create an entry for this CU.  */
       struct Dwarf_CU *newp = libdw_typed_alloc (dbg, struct Dwarf_CU);
 
@@ -123,6 +133,7 @@ __libdw_findcu (dbg, start)
       newp->end = dbg->next_cu_offset;
       newp->address_size = address_size;
       newp->offset_size = offset_size;
+      newp->version = version;
       Dwarf_Abbrev_Hash_init (&newp->abbrev_hash, 41);
       newp->orig_abbrev_offset = newp->last_abbrev_offset = abbrev_offset;
       newp->lines = NULL;
index 779b6c0e0757bd33228beaa797cd917ee24daf77..ad78f4b4fc97295affe43a05698d86c68bd00a7e 100644 (file)
@@ -1,5 +1,5 @@
 /* Helper functions for form handling.
-   Copyright (C) 2003, 2004, 2006 Red Hat, Inc.
+   Copyright (C) 2003, 2004, 2006, 2007 Red Hat, Inc.
    This file is part of Red Hat elfutils.
    Written by Ulrich Drepper <drepper@redhat.com>, 2003.
 
@@ -73,8 +73,11 @@ __libdw_form_val_len (Dwarf *dbg, struct Dwarf_CU *cu, unsigned int form,
       result = cu->address_size;
       break;
 
-    case DW_FORM_strp:
     case DW_FORM_ref_addr:
+      result = cu->version == 2 ? cu->address_size : cu->offset_size;
+      break;
+
+    case DW_FORM_strp:
       result = cu->offset_size;
       break;
 
index 3c56f177a58c3836dba6ae9aa1d165d4a9d04c3e..ea6e4e0197eb053207c781a0a627082d96caeac1 100644 (file)
@@ -1,3 +1,95 @@
+2007-10-23  Roland McGrath  <roland@redhat.com>
+
+       * linux-kernel-modules.c (report_kernel_archive): Reorder the kernel
+       module to appear first.
+
+2007-10-20  Roland McGrath  <roland@redhat.com>
+
+       * offline.c (process_archive_member): Take FD argument, pass it down
+       to process_file.  Return Elf_Cmd, not bool.
+       Call elf_next here, always before elf_end.
+       (process_archive): Update caller.  Don't close FD here unless there
+       are no member refs.
+
+       * dwfl_module.c (free_file): Close fd only when elf_end returns zero.
+
+       * libdwflP.h (struct dwfl_file): New bool member `relocated'.
+       * dwfl_module_getdwarf.c (dwfl_module_getelf): For ET_REL, apply
+       partial relocation to one or both files.
+       (dwfl_module_getdwarf): For ET_REL, make sure extra sections'
+       relocations have been applied to the debug file if dwfl_module_getelf
+       has been used before.
+
+       * relocate.c (resolve_symbol): New function.
+       (relocate_section): Call it.
+
+       * relocate.c (relocate_getsym): Handle null MOD->symfile.
+       (relocate_section): Take new bool arg, PARTIAL.  If true,
+       no error for BADRELTYPE/RELUNDEF, instead just skip them
+       and leave only those skipped relocs behind the reloc section.
+       (__libdwfl_relocate_section): Take new arg, pass it down.
+       (__libdwfl_relocate): Take new bool arg, DEBUG.  If false,
+       do partial relocation on all sections.
+       * dwfl_module_getdwarf.c (load_dw): Update caller.
+       * libdwflP.h: Update decls.
+       * derelocate.c (dwfl_module_address_section): Pass new argument
+       to __libdwfl_relocate_section, true.
+
+       * derelocate.c (cache_sections): Don't cache reloc sections when
+       section_address callback is null.
+
+2007-10-19  Roland McGrath  <roland@redhat.com>
+
+       * relocate.c (relocate_section): Fix fencepost error in r_offset check.
+
+       * derelocate.c (struct dwfl_relocation): Add member `relocs'.
+       (struct secref): Likewise.
+       (cache_sections): Cache the relocation section referring to each
+       section we cache, if any.
+       (dwfl_module_address_section): Use __libdwfl_relocate_section as
+       necessary.
+
+       * relocate.c (struct reloc_symtab_cache): New type.
+       (relocate_getsym): Use it instead of four arguments.
+       (__libdwfl_relocate): Update caller.
+       (relocate_section): New function, broken out of ...
+       (__libdwfl_relocate): ... here.
+       (__libdwfl_relocate_section): New function.
+       * libdwflP.h: Declare it.
+
+2007-10-17  Roland McGrath  <roland@redhat.com>
+
+       * dwfl_module_getsym.c (dwfl_module_getsym): Apply MOD->symfile->bias
+       to relocated st_value.
+
+       * dwfl_report_elf.c (__libdwfl_report_elf): Align initial BASE for
+       ET_REL to 0x100.
+
+2007-10-16  Roland McGrath  <roland@redhat.com>
+
+       * dwfl_report_elf.c (__libdwfl_report_elf): Readjust BASE when a later
+       section has larger alignment requirements not met by the original BASE,
+       rather than padding more between sections.
+
+       * dwfl_report_elf.c (__libdwfl_report_elf): Fix bias calculation.
+
+       * dwfl_module_build_id.c (__libdwfl_find_build_id): Apply module bias
+       to sh_addr value.
+
+       * dwfl_report_elf.c (__libdwfl_report_elf): Don't be confused by BASE
+       at zero in ET_REL case.  Adjust BASE to necessary alignment.
+
+       * dwfl_module_build_id.c (check_notes): Take -1, not 0, as stub value
+       for DATA_VADDR.
+       (__libdwfl_find_build_id): Update caller.
+
+       * relocate.c (__libdwfl_relocate_value): Don't use sh_offset.
+       * dwfl_report_elf.c (__libdwfl_report_elf): Likewise.
+       * offline.c (dwfl_offline_section_address): Bail early if there is
+       separate debug file.
+
+       * relocate.c (__libdwfl_relocate): Don't return DWFL_E_NO_DWARF.
+
 2007-10-09  Roland McGrath  <roland@redhat.com>
 
        * dwfl_report_elf.c (__libdwfl_report_elf): Clear SHDR->sh_offset when
index 6da999d31c8a18ecee471149440fc03a74035b29..0877276d77985e78df30bd0e6616ace30f55c505 100644 (file)
@@ -55,6 +55,7 @@ struct dwfl_relocation
   struct
   {
     Elf_Scn *scn;
+    Elf_Scn *relocs;
     const char *name;
     GElf_Addr start, end;
   } refs[0];
@@ -65,6 +66,7 @@ struct secref
 {
   struct secref *next;
   Elf_Scn *scn;
+  Elf_Scn *relocs;
   const char *name;
   GElf_Addr start, end;
 };
@@ -99,6 +101,7 @@ cache_sections (Dwfl_Module *mod)
       return -1;
     }
 
+  bool check_reloc_sections = false;
   Elf_Scn *scn = NULL;
   while ((scn = elf_nextscn (mod->main.elf, scn)) != NULL)
     {
@@ -128,6 +131,7 @@ cache_sections (Dwfl_Module *mod)
 
          struct secref *newref = alloca (sizeof *newref);
          newref->scn = scn;
+         newref->relocs = NULL;
          newref->name = name;
          newref->start = shdr->sh_addr + mod->main.bias;
          newref->end = newref->start + shdr->sh_size;
@@ -135,6 +139,28 @@ cache_sections (Dwfl_Module *mod)
          refs = newref;
          ++nrefs;
        }
+
+      if (mod->e_type == ET_REL
+         && shdr->sh_size != 0
+         && (shdr->sh_type == SHT_REL || shdr->sh_type == SHT_RELA)
+         && mod->dwfl->callbacks->section_address != NULL)
+       {
+         if (shdr->sh_info < elf_ndxscn (scn))
+           {
+             /* We've already looked at the section these relocs apply to.  */
+             Elf_Scn *tscn = elf_getscn (mod->main.elf, shdr->sh_info);
+             if (likely (tscn != NULL))
+               for (struct secref *sec = refs; sec != NULL; sec = sec->next)
+                 if (sec->scn == tscn)
+                   {
+                     sec->relocs = scn;
+                     break;
+                   }
+           }
+         else
+           /* We'll have to do a second pass.  */
+           check_reloc_sections = true;
+       }
     }
 
   mod->reloc_info = malloc (offsetof (struct dwfl_relocation, refs[nrefs]));
@@ -156,10 +182,40 @@ cache_sections (Dwfl_Module *mod)
     {
       mod->reloc_info->refs[i].name = sortrefs[i]->name;
       mod->reloc_info->refs[i].scn = sortrefs[i]->scn;
+      mod->reloc_info->refs[i].relocs = sortrefs[i]->relocs;
       mod->reloc_info->refs[i].start = sortrefs[i]->start;
       mod->reloc_info->refs[i].end = sortrefs[i]->end;
     }
 
+  if (unlikely (check_reloc_sections))
+    {
+      /* There was a reloc section that preceded its target section.
+        So we have to scan again now that we have cached all the
+        possible target sections we care about.  */
+
+      scn = NULL;
+      while ((scn = elf_nextscn (mod->main.elf, scn)) != NULL)
+       {
+         GElf_Shdr shdr_mem;
+         GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+         if (shdr == NULL)
+           goto elf_error;
+
+         if (shdr->sh_size != 0
+             && (shdr->sh_type == SHT_REL || shdr->sh_type == SHT_RELA))
+           {
+             Elf_Scn *tscn = elf_getscn (mod->main.elf, shdr->sh_info);
+             if (likely (tscn != NULL))
+               for (size_t i = 0; i < nrefs; ++i)
+                 if (mod->reloc_info->refs[i].scn == tscn)
+                   {
+                     mod->reloc_info->refs[i].relocs = scn;
+                     break;
+                   }
+           }
+       }
+    }
+
   return nrefs;
 }
 
@@ -321,6 +377,23 @@ dwfl_module_address_section (Dwfl_Module *mod, Dwarf_Addr *address,
   if (idx < 0)
     return NULL;
 
+  if (mod->reloc_info->refs[idx].relocs != NULL)
+    {
+      assert (mod->e_type == ET_REL);
+
+      Elf_Scn *tscn = mod->reloc_info->refs[idx].scn;
+      Elf_Scn *relocscn = mod->reloc_info->refs[idx].relocs;
+      Dwfl_Error result = __libdwfl_relocate_section (mod, mod->main.elf,
+                                                     relocscn, tscn, true);
+      if (likely (result == DWFL_E_NOERROR))
+       mod->reloc_info->refs[idx].relocs = NULL;
+      else
+       {
+         __libdwfl_seterrno (result);
+         return NULL;
+       }
+    }
+
   *bias = mod->main.bias;
   return mod->reloc_info->refs[idx].scn;
 }
index 4cee37c24587b973dd38d7c44002b3545b93dbae..434f3ff90586644958af01a2cba2564ae0f3c48f 100644 (file)
@@ -68,12 +68,10 @@ static void
 free_file (struct dwfl_file *file)
 {
   free (file->name);
-  if (file->elf != NULL)
-    {
-      elf_end (file->elf);
-      if (file->fd != -1)
-       close (file->fd);
-    }
+
+  /* Close the fd only on the last reference.  */
+  if (file->elf != NULL && elf_end (file->elf) == 0 && file->fd != -1)
+    close (file->fd);
 }
 
 void
index f9a6357ff42186ff33c862400f2a89e9410050b2..903b79c5e712cf66731a94f11c69b1b1a657747e 100644 (file)
@@ -73,6 +73,8 @@ found_build_id (Dwfl_Module *mod, bool set,
   return len;
 }
 
+#define NO_VADDR       ((GElf_Addr) -1l)
+
 static int
 check_notes (Dwfl_Module *mod, bool set, Elf_Data *data, GElf_Addr data_vaddr)
 {
@@ -86,7 +88,7 @@ check_notes (Dwfl_Module *mod, bool set, Elf_Data *data, GElf_Addr data_vaddr)
                                                     "GNU", sizeof "GNU"))
       return found_build_id (mod, set,
                             data->d_buf + desc_pos, nhdr.n_descsz,
-                            data_vaddr == 0 ? 0 : data_vaddr + pos);
+                            data_vaddr == NO_VADDR ? 0 : data_vaddr + pos);
   return 0;
 }
 
@@ -129,7 +131,7 @@ __libdwfl_find_build_id (Dwfl_Module *mod, bool set, Elf *elf)
        if (likely (shdr != NULL) && shdr->sh_type == SHT_NOTE)
          result = check_notes (mod, set, elf_getdata (scn, NULL),
                                (shdr->sh_flags & SHF_ALLOC)
-                               ? shdr->sh_addr : 0);
+                               ? shdr->sh_addr + mod->main.bias : NO_VADDR);
       }
     while (result == 0 && (scn = elf_nextscn (elf, scn)) != NULL);
 
index 0f26e5dcc7ed8e822a9d3581f5c1161b4582a8e2..775df731ec41e7154724dcdf028eef8414414a9e 100644 (file)
@@ -600,7 +600,7 @@ load_dw (Dwfl_Module *mod, struct dwfl_file *debugfile)
       find_symtab (mod);
       Dwfl_Error result = mod->symerr;
       if (result == DWFL_E_NOERROR)
-       result = __libdwfl_relocate (mod, debugfile->elf);
+       result = __libdwfl_relocate (mod, debugfile->elf, true);
       if (result != DWFL_E_NOERROR)
        return result;
 
@@ -689,6 +689,26 @@ dwfl_module_getelf (Dwfl_Module *mod, GElf_Addr *loadbase)
   find_file (mod);
   if (mod->elferr == DWFL_E_NOERROR)
     {
+      if (mod->e_type == ET_REL && ! mod->main.relocated)
+       {
+         /* Before letting them get at the Elf handle,
+            apply all the relocations we know how to.  */
+
+         mod->main.relocated = true;
+         if (likely (__libdwfl_module_getebl (mod) == DWFL_E_NOERROR))
+           {
+             (void) __libdwfl_relocate (mod, mod->main.elf, false);
+
+             if (mod->debug.elf == mod->main.elf)
+               mod->debug.relocated = true;
+             else if (mod->debug.elf != NULL && ! mod->debug.relocated)
+               {
+                 mod->debug.relocated = true;
+                 (void) __libdwfl_relocate (mod, mod->debug.elf, false);
+               }
+           }
+       }
+
       *loadbase = mod->main.bias;
       return mod->main.elf;
     }
@@ -708,6 +728,16 @@ dwfl_module_getdwarf (Dwfl_Module *mod, Dwarf_Addr *bias)
   find_dw (mod);
   if (mod->dwerr == DWFL_E_NOERROR)
     {
+      /* If dwfl_module_getelf was used previously, then partial apply
+        relocation to miscellaneous sections in the debug file too.  */
+      if (mod->e_type == ET_REL
+         && mod->main.relocated && ! mod->debug.relocated)
+       {
+         mod->debug.relocated = true;
+         if (mod->debug.elf != mod->main.elf)
+           (void) __libdwfl_relocate (mod, mod->debug.elf, false);
+       }
+
       *bias = mod->debug.bias;
       return mod->dw;
     }
index 04432a4371c71a4754240c99f89c99c41145ec78..5f289ccb1372716a95f8593e178a71664d2a4af4 100644 (file)
@@ -85,10 +85,7 @@ dwfl_module_getsym (Dwfl_Module *mod, int ndx,
       break;
 
     default:
-      if (mod->e_type != ET_REL)
-       /* Apply the bias to the symbol value.  */
-       sym->st_value += mod->symfile->bias;
-      else
+      if (mod->e_type == ET_REL)
        {
          /* In an ET_REL file, the symbol table values are relative
             to the section, not to the module's load base.  */
@@ -102,6 +99,8 @@ dwfl_module_getsym (Dwfl_Module *mod, int ndx,
              return NULL;
            }
        }
+      /* Apply the bias to the symbol value.  */
+      sym->st_value += mod->symfile->bias;
       break;
     }
 
index 905ddd12e306a72b82af8dc8f720921b59ed8039..0e5d09bcb2eee50f3cc7dbd050554f96456d8b53 100644 (file)
 #include <fcntl.h>
 #include <unistd.h>
 
+
+/* We start every ET_REL module at a moderately aligned boundary.
+   This keeps the low addresses easy to read compared to a layout
+   starting at 0 (as when using -e).  It also makes it unlikely
+   that a middle section will have a larger alignment and require
+   rejiggering (see below).  */
+#define REL_MIN_ALIGN  ((GElf_Xword) 0x100)
+
 Dwfl_Module *
 internal_function
 __libdwfl_report_elf (Dwfl *dwfl, const char *name, const char *file_name,
@@ -72,41 +80,91 @@ __libdwfl_report_elf (Dwfl *dwfl, const char *name, const char *file_name,
         By updating the section header in place, we leave the layout
         information to be found by relocation.  */
 
-      start = end = base;
+      start = end = base = (base + REL_MIN_ALIGN - 1) & -REL_MIN_ALIGN;
 
+      bool first = true;
       Elf_Scn *scn = NULL;
       while ((scn = elf_nextscn (elf, scn)) != NULL)
        {
          GElf_Shdr shdr_mem;
          GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
-         if (shdr == NULL)
+         if (unlikely (shdr == NULL))
            goto elf_error;
 
          if (shdr->sh_flags & SHF_ALLOC)
            {
              const GElf_Xword align = shdr->sh_addralign ?: 1;
-             if (shdr->sh_addr == 0 || (bias == 0 && end > start))
+             const GElf_Addr next = (end + align - 1) & -align;
+             if (shdr->sh_addr == 0
+                 /* Once we've started doing layout we have to do it all,
+                    unless we just layed out the first section at 0 when
+                    it already was at 0.  */
+                 || (bias == 0 && end > start && end != next))
                {
-                 shdr->sh_addr = (end + align - 1) & -align;
+                 shdr->sh_addr = next;
                  if (end == base)
                    /* This is the first section assigned a location.
                       Use its aligned address as the module's base.  */
-                   start = shdr->sh_addr;
+                   start = base = shdr->sh_addr;
+                 else if (unlikely (base & (align - 1)))
+                   {
+                     /* If BASE has less than the maximum alignment of
+                        any section, we eat more than the optimal amount
+                        of padding and so make the module's apparent
+                        size come out larger than it would when placed
+                        at zero.  So reset the layout with a better base.  */
+
+                     start = end = base = (base + align - 1) & -align;
+                     Elf_Scn *prev_scn = NULL;
+                     do
+                       {
+                         prev_scn = elf_nextscn (elf, prev_scn);
+                         GElf_Shdr prev_shdr_mem;
+                         GElf_Shdr *prev_shdr = gelf_getshdr (prev_scn,
+                                                              &prev_shdr_mem);
+                         if (unlikely (prev_shdr == NULL))
+                           goto elf_error;
+                         if (prev_shdr->sh_flags & SHF_ALLOC)
+                           {
+                             const GElf_Xword prev_align
+                               = prev_shdr->sh_addralign ?: 1;
+
+                             prev_shdr->sh_addr
+                               = (end + prev_align - 1) & -prev_align;
+                             end = prev_shdr->sh_addr + prev_shdr->sh_size;
+
+                             if (unlikely (! gelf_update_shdr (prev_scn,
+                                                               prev_shdr)))
+                               goto elf_error;
+                           }
+                       }
+                     while (prev_scn != scn);
+                     continue;
+                   }
+
                  end = shdr->sh_addr + shdr->sh_size;
-                 if (shdr->sh_addr == 0)
-                   /* This is a marker that this was resolved to zero,
-                      to prevent a callback.  */
-                   shdr->sh_offset = 0;
-                 if (! gelf_update_shdr (scn, shdr))
+                 if (likely (shdr->sh_addr != 0)
+                     && unlikely (! gelf_update_shdr (scn, shdr)))
                    goto elf_error;
                }
              else
                {
-                 if (bias == 0 || end < shdr->sh_addr + shdr->sh_size)
+                 /* The address is already assigned.  Just track it.  */
+                 if (first || end < shdr->sh_addr + shdr->sh_size)
                    end = shdr->sh_addr + shdr->sh_size;
-                 if (bias == 0 || bias > shdr->sh_addr)
+                 if (first || bias > shdr->sh_addr)
+                   /* This is the lowest address in the module.  */
                    bias = shdr->sh_addr;
+
+                 if ((shdr->sh_addr - bias + base) & (align - 1))
+                   /* This section winds up misaligned using BASE.
+                      Adjust BASE upwards to make it congruent to
+                      the lowest section address in the file modulo ALIGN.  */
+                   base = (((base + align - 1) & -align)
+                           + (bias & (align - 1)));
                }
+
+             first = false;
            }
        }
 
@@ -117,7 +175,7 @@ __libdwfl_report_elf (Dwfl *dwfl, const char *name, const char *file_name,
             Now just compute the bias from the requested base.  */
          start = base;
          end = end - bias + start;
-         bias -= start;
+         bias = start - bias;
        }
       break;
 
@@ -133,7 +191,7 @@ __libdwfl_report_elf (Dwfl *dwfl, const char *name, const char *file_name,
       for (uint_fast16_t i = 0; i < ehdr->e_phnum; ++i)
        {
          GElf_Phdr phdr_mem, *ph = gelf_getphdr (elf, i, &phdr_mem);
-         if (ph == NULL)
+         if (unlikely (ph == NULL))
            goto elf_error;
          if (ph->p_type == PT_LOAD)
            {
@@ -148,7 +206,7 @@ __libdwfl_report_elf (Dwfl *dwfl, const char *name, const char *file_name,
       for (uint_fast16_t i = ehdr->e_phnum; i-- > 0;)
        {
          GElf_Phdr phdr_mem, *ph = gelf_getphdr (elf, i, &phdr_mem);
-         if (ph == NULL)
+         if (unlikely (ph == NULL))
            goto elf_error;
          if (ph->p_type == PT_LOAD)
            {
index e75a3ab6cbb2469ae1549acc8cf31ff57d291b4d..bbb56aac54a4233bc41ca8d74c44e4a2e93af369 100644 (file)
@@ -121,6 +121,7 @@ struct dwfl_file
   char *name;
   int fd;
   bool valid;                  /* The build ID note has been matched.  */
+  bool relocated;              /* Partial relocation of all sections done.  */
 
   Elf *elf;
   GElf_Addr bias;              /* Actual load address - p_vaddr.  */
@@ -227,11 +228,20 @@ extern void __libdwfl_module_free (Dwfl_Module *mod) internal_function;
 
 
 /* Process relocations in debugging sections in an ET_REL file.
-   DEBUGFILE must be opened with ELF_C_READ_MMAP_PRIVATE or ELF_C_READ,
+   FILE must be opened with ELF_C_READ_MMAP_PRIVATE or ELF_C_READ,
    to make it possible to relocate the data in place (or ELF_C_RDWR or
    ELF_C_RDWR_MMAP if you intend to modify the Elf file on disk).  After
-   this, dwarf_begin_elf on DEBUGFILE will read the relocated data.  */
-extern Dwfl_Error __libdwfl_relocate (Dwfl_Module *mod, Elf *debugfile)
+   this, dwarf_begin_elf on FILE will read the relocated data.
+
+   When DEBUG is false, apply partial relocation to all sections.  */
+extern Dwfl_Error __libdwfl_relocate (Dwfl_Module *mod, Elf *file, bool debug)
+  internal_function;
+
+/* Process (simple) relocations in arbitrary section TSCN of an ET_REL file.
+   RELOCSCN is SHT_REL or SHT_RELA and TSCN is its sh_info target section.  */
+extern Dwfl_Error __libdwfl_relocate_section (Dwfl_Module *mod, Elf *relocated,
+                                             Elf_Scn *relocscn, Elf_Scn *tscn,
+                                             bool partial)
   internal_function;
 
 /* Adjust *VALUE from section-relative to absolute.
index c15e0896aaa176d70a0b40db4ead01413890d2bf..b113806e185135790cdd4c5dfbe3c35ae416cd83 100644 (file)
@@ -226,9 +226,26 @@ report_kernel_archive (Dwfl *dwfl, const char **release,
   if (fd < 0)
     result = errno ?: ENOENT;
   else
-    /* We have the archive file open!  */
-    result = __libdwfl_report_offline (dwfl, NULL, archive, fd, true,
-                                      predicate) == NULL ? -1 : 0;
+    {
+      /* We have the archive file open!  */
+      Dwfl_Module *last = __libdwfl_report_offline (dwfl, NULL, archive, fd,
+                                                   true, predicate);
+      if (unlikely (last == NULL))
+       result = -1;
+      else
+       {
+         /* Find the kernel and move it to the head of the list.  */
+         Dwfl_Module **tailp = &dwfl->modulelist, **prevp = tailp;
+         for (Dwfl_Module *m = *prevp; m != NULL; m = *(prevp = &m->next))
+           if (!m->gc && m->e_type != ET_REL && !strcmp (m->name, "kernel"))
+             {
+               *prevp = m->next;
+               m->next = *tailp;
+               *tailp = m;
+               break;
+             }
+       }
+    }
 
   free (archive);
   return result;
index 916d263e7384236c887d754929c8fe4456071678..ff7b793aa4148ce78a24d5e1a46cf8649e49bdb0 100644 (file)
@@ -53,8 +53,9 @@
 
 /* Since dwfl_report_elf lays out the sections already, this will only be
    called when the section headers of the debuginfo file are being
-   consulted instead.  With binutils strip-to-debug, the symbol table is in
-   the debuginfo file and relocation looks there.  */
+   consulted instead, or for the section placed at 0.  With binutils
+   strip-to-debug, the symbol table is in the debuginfo file and relocation
+   looks there.  */
 int
 dwfl_offline_section_address (Dwfl_Module *mod,
                              void **userdata __attribute__ ((unused)),
@@ -69,6 +70,11 @@ dwfl_offline_section_address (Dwfl_Module *mod,
   assert (shdr->sh_addr == 0);
   assert (shdr->sh_flags & SHF_ALLOC);
 
+  if (mod->debug.elf == NULL)
+    /* We are only here because sh_addr is zero even though layout is complete.
+       The first section in the first file under -e is placed at 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.  */
 
@@ -114,7 +120,8 @@ static Dwfl_Module *process_archive (Dwfl *dwfl, const char *name,
                                     int (*predicate) (const char *module,
                                                       const char *file));
 
-/* Report one module for an ELF file, or many for an archive.  */
+/* Report one module for an ELF file, or many for an archive.
+   Always consumes ELF and FD.  */
 static Dwfl_Module *
 process_file (Dwfl *dwfl, const char *name, const char *file_name, int fd,
              Elf *elf, int (*predicate) (const char *module,
@@ -135,7 +142,7 @@ process_file (Dwfl *dwfl, const char *name, const char *file_name, int fd,
     }
 }
 
-/* Report the open ELF file as a module.  */
+/* Report the open ELF file as a module.  Always consumes ELF and FD.  */
 static Dwfl_Module *
 process_elf (Dwfl *dwfl, const char *name, const char *file_name, int fd,
             Elf *elf)
@@ -166,24 +173,30 @@ process_elf (Dwfl *dwfl, const char *name, const char *file_name, int fd,
   return mod;
 }
 
-static bool
+/* Always consumes MEMBER.  Returns elf_next result on success.
+   For errors returns ELF_C_NULL with *MOD set to null.  */
+static Elf_Cmd
 process_archive_member (Dwfl *dwfl, const char *name, const char *file_name,
                        int (*predicate) (const char *module, const char *file),
-                       Elf *member, Dwfl_Module **mod)
+                       int fd, Elf *member, Dwfl_Module **mod)
 {
   const Elf_Arhdr *h = elf_getarhdr (member);
   if (unlikely (h == NULL))
     {
       __libdwfl_seterrno (DWFL_E_LIBELF);
+    fail:
       elf_end (member);
       *mod = NULL;
-      return false;
+      return ELF_C_NULL;
     }
 
   if (!strcmp (h->ar_name, "/") || !strcmp (h->ar_name, "//"))
     {
+    skip:;
+      /* Skip this and go to the next.  */
+      Elf_Cmd result = elf_next (member);
       elf_end (member);
-      return true;
+      return result;
     }
 
   char *member_name;
@@ -193,7 +206,7 @@ process_archive_member (Dwfl *dwfl, const char *name, const char *file_name,
       __libdwfl_seterrno (DWFL_E_NOMEM);
       elf_end (member);
       *mod = NULL;
-      return false;
+      return ELF_C_NULL;
     }
 
   char *module_name = NULL;
@@ -218,18 +231,24 @@ process_archive_member (Dwfl *dwfl, const char *name, const char *file_name,
          if (unlikely (want < 0))
            {
              __libdwfl_seterrno (DWFL_E_CB);
-             elf_end (member);
-             *mod = NULL;
-             return false;
+             goto fail;
            }
-         return true;
+         goto skip;
        }
     }
 
-  *mod = process_file (dwfl, name, member_name, -1, member, predicate);
+  /* We let __libdwfl_report_elf cache the fd in mod->main.fd,
+     though it's the same fd for all the members.
+     On module teardown we will close it only on the last Elf reference.  */
+  *mod = process_file (dwfl, name, member_name, fd, member, predicate);
   free (member_name);
   free (module_name);
-  return *mod != NULL;
+
+  if (*mod == NULL)            /* process_file called elf_end.  */
+    return ELF_C_NULL;
+
+  /* Advance the archive-reading offset for the next iteration.  */
+  return elf_next (member);
 }
 
 /* Report each member of the archive as its own module.  */
@@ -240,24 +259,18 @@ process_archive (Dwfl *dwfl, const char *name, const char *file_name, int fd,
 
 {
   Dwfl_Module *mod = NULL;
-  bool more = true;
-  do
-    {
-      Elf *member = elf_begin (-1, ELF_C_READ_MMAP_PRIVATE, archive);
-      if (process_archive_member (dwfl, name, file_name, predicate,
-                                 member, &mod))
-       /* Advance the archive-reading offset for the next iteration.  */
-       more = elf_next (member) != ELF_C_NULL;
-      else if (mod == NULL)
-       {
-         elf_end (member);
-         break;
-       }
-    }
-  while (more);
-  elf_end (archive);
-  if (likely (mod != NULL))
+  while (process_archive_member (dwfl, name, file_name, predicate,
+                                fd, elf_begin (fd, ELF_C_READ_MMAP_PRIVATE,
+                                               archive), &mod) != ELF_C_NULL)
+    ;
+
+  /* We can drop the archive Elf handle even if we're still using members
+     in live modules.  When the last module's elf_end on a member returns
+     zero, that module will close FD.  If no modules survived the predicate,
+     we are all done with the file right here.  */
+  if (elf_end (archive) == 0)
     close (fd);
+
   return mod;
 }
 
index e355af0c1637f9adc12790dc3b92ca5c63f94276..6265f1bf7d72388bdbeccf69988feda7b496bd83 100644 (file)
@@ -64,9 +64,7 @@ __libdwfl_relocate_value (Dwfl_Module *mod, Elf *elf, size_t *shstrndx,
   if (refshdr == NULL)
     return DWFL_E_LIBELF;
 
-  if (refshdr->sh_addr == 0
-      && (refshdr->sh_flags & SHF_ALLOC)
-      && refshdr->sh_offset != 0)
+  if (refshdr->sh_addr == 0 && (refshdr->sh_flags & SHF_ALLOC))
     {
       /* This is a loaded section.  Find its actual
         address and update the section header.  */
@@ -89,13 +87,11 @@ __libdwfl_relocate_value (Dwfl_Module *mod, Elf *elf, size_t *shstrndx,
           don't really care.  */
        refshdr->sh_addr = 0;   /* Make no adjustment below.  */
 
-      /* Mark it so we don't check it again for the next relocation.  */
-      refshdr->sh_offset = 0;
-
       /* Update the in-core file's section header to show the final
         load address (or unloadedness).  This serves as a cache,
         so we won't get here again for the same section.  */
-      if (unlikely (! gelf_update_shdr (refscn, refshdr)))
+      if (likely (refshdr->sh_addr != 0)
+         && unlikely (! gelf_update_shdr (refscn, refshdr)))
        return DWFL_E_LIBELF;
     }
 
@@ -104,17 +100,31 @@ __libdwfl_relocate_value (Dwfl_Module *mod, Elf *elf, size_t *shstrndx,
   return DWFL_E_NOERROR;
 }
 
+
+/* Cache used by relocate_getsym.  */
+struct reloc_symtab_cache
+{
+  Elf *symelf;
+  Elf_Data *symdata;
+  Elf_Data *symxndxdata;
+  Elf_Data *symstrdata;
+  size_t symshstrndx;
+  size_t strtabndx;
+};
+#define RELOC_SYMTAB_CACHE(cache)      \
+  struct reloc_symtab_cache cache =    \
+    { NULL, NULL, NULL, NULL, SHN_UNDEF, SHN_UNDEF }
+
 /* This is just doing dwfl_module_getsym, except that we must always use
    the symbol table in RELOCATED itself when it has one, not MOD->symfile.  */
 static Dwfl_Error
-relocate_getsym (Elf **symelf, Elf_Data **symdata, Elf_Data **symxndxdata,
-                size_t *symshstrndx,
-                Dwfl_Module *mod, Elf *relocated,
+relocate_getsym (Dwfl_Module *mod,
+                Elf *relocated, struct reloc_symtab_cache *cache,
                 int symndx, GElf_Sym *sym, GElf_Word *shndx)
 {
-  if (*symdata == NULL)
+  if (cache->symdata == NULL)
     {
-      if (mod->symfile->elf != relocated)
+      if (mod->symfile == NULL || mod->symfile->elf != relocated)
        {
          /* We have to look up the symbol table in the file we are
             relocating, if it has its own.  These reloc sections refer to
@@ -131,34 +141,42 @@ relocate_getsym (Elf **symelf, Elf_Data **symdata, Elf_Data **symxndxdata,
                  default:
                    continue;
                  case SHT_SYMTAB:
-                   *symelf = relocated;
-                   *symdata = elf_getdata (scn, NULL);
-                   if (unlikely (*symdata == NULL))
+                   cache->symelf = relocated;
+                   cache->symdata = elf_getdata (scn, NULL);
+                   cache->strtabndx = shdr->sh_link;
+                   if (unlikely (cache->symdata == NULL))
                      return DWFL_E_LIBELF;
                    break;
                  case SHT_SYMTAB_SHNDX:
-                   *symxndxdata = elf_getdata (scn, NULL);
-                   if (unlikely (*symxndxdata == NULL))
+                   cache->symxndxdata = elf_getdata (scn, NULL);
+                   if (unlikely (cache->symxndxdata == NULL))
                      return DWFL_E_LIBELF;
                    break;
                  }
-             if (*symdata != NULL && *symxndxdata != NULL)
+             if (cache->symdata != NULL && cache->symxndxdata != NULL)
                break;
            }
        }
-      if (*symdata == NULL)
+      if (cache->symdata == NULL)
        {
+         /* We might not have looked for a symbol table file yet,
+            when coming from __libdwfl_relocate_section.  */
+         if (unlikely (mod->symfile == NULL)
+             && unlikely (INTUSE(dwfl_module_getsymtab) (mod) < 0))
+           return dwfl_errno ();
+
          /* The symbol table we have already cached is the one from
             the file being relocated, so it's what we need.  Or else
             this is an ET_REL .debug file with no .symtab of its own;
             the symbols refer to the section indices in the main file.  */
-         *symelf = mod->symfile->elf;
-         *symdata = mod->symdata;
-         *symxndxdata = mod->symxndxdata;
+         cache->symelf = mod->symfile->elf;
+         cache->symdata = mod->symdata;
+         cache->symxndxdata = mod->symxndxdata;
+         cache->symstrdata = mod->symstrdata;
        }
     }
 
-  if (unlikely (gelf_getsymshndx (*symdata, *symxndxdata,
+  if (unlikely (gelf_getsymshndx (cache->symdata, cache->symxndxdata,
                                  symndx, sym, shndx) == NULL))
     return DWFL_E_LIBELF;
 
@@ -173,13 +191,362 @@ relocate_getsym (Elf **symelf, Elf_Data **symdata, Elf_Data **symxndxdata,
       return DWFL_E_NOERROR;
     }
 
-  return __libdwfl_relocate_value (mod, *symelf, symshstrndx,
+  return __libdwfl_relocate_value (mod, cache->symelf, &cache->symshstrndx,
                                   *shndx, &sym->st_value);
 }
 
+/* Handle an undefined symbol.  We really only support ET_REL for Linux
+   kernel modules, and offline archives.  The behavior of the Linux module
+   loader is very simple and easy to mimic.  It only matches magically
+   exported symbols, and we match any defined symbols.  But we get the same
+   answer except when the module's symbols are undefined and would prevent
+   it from being loaded.  */
+static Dwfl_Error
+resolve_symbol (Dwfl_Module *referer, struct reloc_symtab_cache *symtab,
+               GElf_Sym *sym, GElf_Word shndx)
+{
+  /* First we need its name.  */
+  if (sym->st_name != 0)
+    {
+      if (symtab->symstrdata == NULL)
+       {
+         /* Cache the strtab for this symtab.  */
+         assert (referer->symfile == NULL
+                 || referer->symfile->elf != symtab->symelf);
+         symtab->symstrdata = elf_getdata (elf_getscn (symtab->symelf,
+                                                       symtab->strtabndx),
+                                           NULL);
+         if (unlikely (symtab->symstrdata == NULL))
+           return DWFL_E_LIBELF;
+       }
+      if (unlikely (sym->st_name >= symtab->symstrdata->d_size))
+       return DWFL_E_BADSTROFF;
+
+      const char *name = symtab->symstrdata->d_buf;
+      name += sym->st_name;
+
+      for (Dwfl_Module *m = referer->dwfl->modulelist; m != NULL; m = m->next)
+       if (m != referer)
+         {
+           /* Get this module's symtab.
+              If we got a fresh error reading the table, report it.
+              If we just have no symbols in this module, no harm done.  */
+           if (m->symdata == NULL
+               && m->symerr == DWFL_E_NOERROR
+               && INTUSE(dwfl_module_getsymtab) (m) < 0
+               && m->symerr != DWFL_E_NO_SYMTAB)
+             return m->symerr;
+
+           for (size_t ndx = 1; ndx < m->syments; ++ndx)
+             {
+               sym = gelf_getsymshndx (m->symdata, m->symxndxdata,
+                                       ndx, sym, &shndx);
+               if (unlikely (sym == NULL))
+                 return DWFL_E_LIBELF;
+               if (sym->st_shndx != SHN_XINDEX)
+                 shndx = sym->st_shndx;
+
+               /* We are looking for a defined global symbol with a name.  */
+               if (shndx == SHN_UNDEF || shndx == SHN_COMMON
+                   || GELF_ST_BIND (sym->st_info) == STB_LOCAL
+                   || sym->st_name == 0)
+                 continue;
+
+               /* Get this candidate symbol's name.  */
+               if (unlikely (sym->st_name >= m->symstrdata->d_size))
+                 return DWFL_E_BADSTROFF;
+               const char *n = m->symstrdata->d_buf;
+               n += sym->st_name;
+
+               /* Does the name match?  */
+               if (strcmp (name, n))
+                 continue;
+
+               /* We found it!  */
+               if (shndx == SHN_ABS)
+                 return DWFL_E_NOERROR;
+
+               /* In an ET_REL file, the symbol table values are relative
+                  to the section, not to the module's load base.  */
+               size_t symshstrndx = SHN_UNDEF;
+               return __libdwfl_relocate_value (m, m->symfile->elf,
+                                                &symshstrndx,
+                                                shndx, &sym->st_value);
+             }
+         }
+    }
+
+  return DWFL_E_RELUNDEF;
+}
+
+static Dwfl_Error
+relocate_section (Dwfl_Module *mod, Elf *relocated, const GElf_Ehdr *ehdr,
+                 size_t shstrndx, struct reloc_symtab_cache *reloc_symtab,
+                 Elf_Scn *scn, GElf_Shdr *shdr,
+                 Elf_Scn *tscn, bool debugscn, bool partial)
+{
+  /* First, fetch the name of the section these relocations apply to.  */
+  GElf_Shdr tshdr_mem;
+  GElf_Shdr *tshdr = gelf_getshdr (tscn, &tshdr_mem);
+  const char *tname = elf_strptr (relocated, shstrndx, tshdr->sh_name);
+  if (tname == NULL)
+    return DWFL_E_LIBELF;
+
+  if (debugscn && ! ebl_debugscn_p (mod->ebl, tname))
+    /* This relocation section is not for a debugging section.
+       Nothing to do here.  */
+    return DWFL_E_NOERROR;
+
+  /* Fetch the section data that needs the relocations applied.  */
+  Elf_Data *tdata = elf_rawdata (tscn, NULL);
+  if (tdata == NULL)
+    return DWFL_E_LIBELF;
+
+  /* Apply one relocation.  Returns true for any invalid data.  */
+  Dwfl_Error relocate (GElf_Addr offset, const GElf_Sxword *addend,
+                      int rtype, int symndx)
+  {
+    /* First, resolve the symbol to an absolute value.  */
+    GElf_Addr value;
+
+    if (symndx == STN_UNDEF)
+      /* When strip removes a section symbol referring to a
+        section moved into the debuginfo file, it replaces
+        that symbol index in relocs with STN_UNDEF.  We
+        don't actually need the symbol, because those relocs
+        are always references relative to the nonallocated
+        debugging sections, which start at zero.  */
+      value = 0;
+    else
+      {
+       GElf_Sym sym;
+       GElf_Word shndx;
+       Dwfl_Error error = relocate_getsym (mod, relocated, reloc_symtab,
+                                           symndx, &sym, &shndx);
+       if (unlikely (error != DWFL_E_NOERROR))
+         return error;
+
+       if (shndx == SHN_UNDEF || shndx == SHN_COMMON)
+         {
+           /* Maybe we can figure it out anyway.  */
+           error = resolve_symbol (mod, reloc_symtab, &sym, shndx);
+           if (error != DWFL_E_NOERROR)
+             return error;
+         }
+
+       value = sym.st_value;
+      }
+
+    /* These are the types we can relocate.  */
+#define TYPES          DO_TYPE (BYTE, Byte); DO_TYPE (HALF, Half);     \
+    DO_TYPE (WORD, Word); DO_TYPE (SWORD, Sword);                      \
+    DO_TYPE (XWORD, Xword); DO_TYPE (SXWORD, Sxword)
+    size_t size;
+    Elf_Type type = ebl_reloc_simple_type (mod->ebl, rtype);
+    switch (type)
+      {
+#define DO_TYPE(NAME, Name)                    \
+       case ELF_T_##NAME:                      \
+         size = sizeof (GElf_##Name);          \
+       break
+       TYPES;
+#undef DO_TYPE
+      default:
+       /* This might be because ebl_openbackend failed to find
+          any libebl_CPU.so library.  Diagnose that clearly.  */
+       if (ebl_get_elfmachine (mod->ebl) == EM_NONE)
+         return DWFL_E_UNKNOWN_MACHINE;
+       return DWFL_E_BADRELTYPE;
+      }
+
+    if (offset + size > tdata->d_size)
+      return DWFL_E_BADRELOFF;
+
+#define DO_TYPE(NAME, Name) GElf_##Name Name;
+    union { TYPES; } tmpbuf;
+#undef DO_TYPE
+    Elf_Data tmpdata =
+      {
+       .d_type = type,
+       .d_buf = &tmpbuf,
+       .d_size = size,
+       .d_version = EV_CURRENT,
+      };
+    Elf_Data rdata =
+      {
+       .d_type = type,
+       .d_buf = tdata->d_buf + offset,
+       .d_size = size,
+       .d_version = EV_CURRENT,
+      };
+
+    /* XXX check for overflow? */
+    if (addend)
+      {
+       /* For the addend form, we have the value already.  */
+       value += *addend;
+       switch (type)
+         {
+#define DO_TYPE(NAME, Name)                    \
+           case ELF_T_##NAME:                  \
+             tmpbuf.Name = value;              \
+           break
+           TYPES;
+#undef DO_TYPE
+         default:
+           abort ();
+         }
+      }
+    else
+      {
+       /* Extract the original value and apply the reloc.  */
+       Elf_Data *d = gelf_xlatetom (relocated, &tmpdata, &rdata,
+                                    ehdr->e_ident[EI_DATA]);
+       if (d == NULL)
+         return DWFL_E_LIBELF;
+       assert (d == &tmpdata);
+       switch (type)
+         {
+#define DO_TYPE(NAME, Name)                            \
+           case ELF_T_##NAME:                          \
+             tmpbuf.Name += (GElf_##Name) value;       \
+           break
+           TYPES;
+#undef DO_TYPE
+         default:
+           abort ();
+         }
+      }
+
+    /* Now convert the relocated datum back to the target
+       format.  This will write into rdata.d_buf, which
+       points into the raw section data being relocated.  */
+    Elf_Data *s = gelf_xlatetof (relocated, &rdata, &tmpdata,
+                                ehdr->e_ident[EI_DATA]);
+    if (s == NULL)
+      return DWFL_E_LIBELF;
+    assert (s == &rdata);
+
+    /* We have applied this relocation!  */
+    return DWFL_E_NOERROR;
+  }
+
+  /* Fetch the relocation section and apply each reloc in it.  */
+  Elf_Data *reldata = elf_getdata (scn, NULL);
+  if (reldata == NULL)
+    return DWFL_E_LIBELF;
+
+  Dwfl_Error result = DWFL_E_NOERROR;
+  size_t nrels = shdr->sh_size / shdr->sh_entsize;
+  size_t complete = 0;
+  if (shdr->sh_type == SHT_REL)
+    for (size_t relidx = 0; !result && relidx < nrels; ++relidx)
+      {
+       GElf_Rel rel_mem, *r = gelf_getrel (reldata, relidx, &rel_mem);
+       if (r == NULL)
+         return DWFL_E_LIBELF;
+       result = relocate (r->r_offset, NULL,
+                          GELF_R_TYPE (r->r_info),
+                          GELF_R_SYM (r->r_info));
+       if (partial)
+         switch (result)
+           {
+           case DWFL_E_NOERROR:
+             /* We applied the relocation.  Elide it.  */
+             memset (&rel_mem, 0, sizeof rel_mem);
+             gelf_update_rel (reldata, relidx, &rel_mem);
+             ++complete;
+             break;
+           case DWFL_E_BADRELTYPE:
+           case DWFL_E_RELUNDEF:
+             /* We couldn't handle this relocation.  Skip it.  */
+             result = DWFL_E_NOERROR;
+             break;
+           default:
+             break;
+           }
+      }
+  else
+    for (size_t relidx = 0; !result && relidx < nrels; ++relidx)
+      {
+       GElf_Rela rela_mem, *r = gelf_getrela (reldata, relidx,
+                                              &rela_mem);
+       if (r == NULL)
+         return DWFL_E_LIBELF;
+       result = relocate (r->r_offset, &r->r_addend,
+                          GELF_R_TYPE (r->r_info),
+                          GELF_R_SYM (r->r_info));
+       if (partial)
+         switch (result)
+           {
+           case DWFL_E_NOERROR:
+             /* We applied the relocation.  Elide it.  */
+             memset (&rela_mem, 0, sizeof rela_mem);
+             gelf_update_rela (reldata, relidx, &rela_mem);
+             ++complete;
+             break;
+           case DWFL_E_BADRELTYPE:
+           case DWFL_E_RELUNDEF:
+             /* We couldn't handle this relocation.  Skip it.  */
+             result = DWFL_E_NOERROR;
+             break;
+           default:
+             break;
+           }
+      }
+
+  if (likely (result == DWFL_E_NOERROR))
+    {
+      if (!partial || complete == nrels)
+       /* Mark this relocation section as being empty now that we have
+          done its work.  This affects unstrip -R, so e.g. it emits an
+          empty .rela.debug_info along with a .debug_info that has
+          already been fully relocated.  */
+       nrels = 0;
+      else if (complete != 0)
+       {
+         /* We handled some of the relocations but not all.
+            We've zeroed out the ones we processed.
+            Now remove them from the section.  */
+
+         size_t next = 0;
+         if (shdr->sh_type == SHT_REL)
+           for (size_t relidx = 0; relidx < nrels; ++relidx)
+             {
+               GElf_Rel rel_mem;
+               GElf_Rel *r = gelf_getrel (reldata, relidx, &rel_mem);
+               if (r->r_info != 0 || r->r_offset != 0)
+                 {
+                   if (next != relidx)
+                     gelf_update_rel (reldata, next, r);
+                   ++next;
+                 }
+             }
+         else
+           for (size_t relidx = 0; relidx < nrels; ++relidx)
+             {
+               GElf_Rela rela_mem;
+               GElf_Rela *r = gelf_getrela (reldata, relidx, &rela_mem);
+               if (r->r_info != 0 || r->r_offset != 0 || r->r_addend != 0)
+                 {
+                   if (next != relidx)
+                     gelf_update_rela (reldata, next, r);
+                   ++next;
+                 }
+             }
+         nrels = next;
+       }
+
+      shdr->sh_size = reldata->d_size = nrels * shdr->sh_entsize;
+      gelf_update_shdr (scn, shdr);
+    }
+
+  return result;
+}
+
 Dwfl_Error
 internal_function
-__libdwfl_relocate (Dwfl_Module *mod, Elf *debugfile)
+__libdwfl_relocate (Dwfl_Module *mod, Elf *debugfile, bool debug)
 {
   assert (mod->e_type == ET_REL);
 
@@ -188,23 +555,18 @@ __libdwfl_relocate (Dwfl_Module *mod, Elf *debugfile)
   if (ehdr == NULL)
     return DWFL_E_LIBELF;
 
-  /* Cache used by relocate_getsym.  */
-  Elf *reloc_symelf = NULL;
-  Elf_Data *reloc_symdata = NULL;
-  Elf_Data *reloc_symxndxdata = NULL;
-  size_t reloc_symshstrndx = SHN_UNDEF;
-
   size_t d_shstrndx;
   if (elf_getshstrndx (debugfile, &d_shstrndx) < 0)
     return DWFL_E_LIBELF;
-  if (mod->symfile->elf == debugfile)
-    reloc_symshstrndx = d_shstrndx;
+
+  RELOC_SYMTAB_CACHE (reloc_symtab);
 
   /* Look at each section in the debuginfo file, and process the
      relocation sections for debugging sections.  */
-  Dwfl_Error result = DWFL_E_NO_DWARF;
+  Dwfl_Error result = DWFL_E_NOERROR;
   Elf_Scn *scn = NULL;
-  while ((scn = elf_nextscn (debugfile, scn)) != NULL)
+  while (result == DWFL_E_NOERROR
+        && (scn = elf_nextscn (debugfile, scn)) != NULL)
     {
       GElf_Shdr shdr_mem;
       GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
@@ -212,198 +574,39 @@ __libdwfl_relocate (Dwfl_Module *mod, Elf *debugfile)
       if ((shdr->sh_type == SHT_REL || shdr->sh_type == SHT_RELA)
          && shdr->sh_size != 0)
        {
-         /* It's a relocation section.  First, fetch the name of the
-            section these relocations apply to.  */
+         /* It's a relocation section.  */
 
          Elf_Scn *tscn = elf_getscn (debugfile, shdr->sh_info);
-         if (tscn == NULL)
-           return DWFL_E_LIBELF;
-
-         GElf_Shdr tshdr_mem;
-         GElf_Shdr *tshdr = gelf_getshdr (tscn, &tshdr_mem);
-         const char *tname = elf_strptr (debugfile, d_shstrndx,
-                                         tshdr->sh_name);
-         if (tname == NULL)
-           return DWFL_E_LIBELF;
-
-         if (! ebl_debugscn_p (mod->ebl, tname))
-           /* This relocation section is not for a debugging section.
-              Nothing to do here.  */
-           continue;
-
-         /* Fetch the section data that needs the relocations applied.  */
-         Elf_Data *tdata = elf_rawdata (tscn, NULL);
-         if (tdata == NULL)
-           return DWFL_E_LIBELF;
-
-         /* Apply one relocation.  Returns true for any invalid data.  */
-         Dwfl_Error relocate (GElf_Addr offset, const GElf_Sxword *addend,
-                              int rtype, int symndx)
-           {
-             /* First, resolve the symbol to an absolute value.  */
-             GElf_Addr value;
-
-             if (symndx == STN_UNDEF)
-               /* When strip removes a section symbol referring to a
-                  section moved into the debuginfo file, it replaces
-                  that symbol index in relocs with STN_UNDEF.  We
-                  don't actually need the symbol, because those relocs
-                  are always references relative to the nonallocated
-                  debugging sections, which start at zero.  */
-               value = 0;
-             else
-               {
-                 GElf_Sym sym;
-                 GElf_Word shndx;
-                 Dwfl_Error error = relocate_getsym (&reloc_symelf,
-                                                     &reloc_symdata,
-                                                     &reloc_symxndxdata,
-                                                     &reloc_symshstrndx,
-                                                     mod, debugfile,
-                                                     symndx, &sym, &shndx);
-                 if (unlikely (error != DWFL_E_NOERROR))
-                   return error;
-
-                 if (shndx == SHN_UNDEF || shndx == SHN_COMMON)
-                   return DWFL_E_RELUNDEF;
-
-                 value = sym.st_value;
-               }
-
-             /* These are the types we can relocate.  */
-#define TYPES          DO_TYPE (BYTE, Byte); DO_TYPE (HALF, Half); \
-                       DO_TYPE (WORD, Word); DO_TYPE (SWORD, Sword); \
-                       DO_TYPE (XWORD, Xword); DO_TYPE (SXWORD, Sxword)
-             size_t size;
-             Elf_Type type = ebl_reloc_simple_type (mod->ebl, rtype);
-             switch (type)
-               {
-#define DO_TYPE(NAME, Name)                            \
-                 case ELF_T_##NAME:                    \
-                   size = sizeof (GElf_##Name);        \
-                 break
-                 TYPES;
-#undef DO_TYPE
-               default:
-                 /* This might be because ebl_openbackend failed to find
-                    any libebl_CPU.so library.  Diagnose that clearly.  */
-                 if (ebl_get_elfmachine (mod->ebl) == EM_NONE)
-                   return DWFL_E_UNKNOWN_MACHINE;
-                 return DWFL_E_BADRELTYPE;
-               }
-
-             if (offset + size >= tdata->d_size)
-               return DWFL_E_BADRELOFF;
-
-#define DO_TYPE(NAME, Name) GElf_##Name Name;
-             union { TYPES; } tmpbuf;
-#undef DO_TYPE
-             Elf_Data tmpdata =
-               {
-                 .d_type = type,
-                 .d_buf = &tmpbuf,
-                 .d_size = size,
-                 .d_version = EV_CURRENT,
-               };
-             Elf_Data rdata =
-               {
-                 .d_type = type,
-                 .d_buf = tdata->d_buf + offset,
-                 .d_size = size,
-                 .d_version = EV_CURRENT,
-               };
-
-             /* XXX check for overflow? */
-             if (addend)
-               {
-                 /* For the addend form, we have the value already.  */
-                 value += *addend;
-                 switch (type)
-                   {
-#define DO_TYPE(NAME, Name)                    \
-                     case ELF_T_##NAME:        \
-                       tmpbuf.Name = value;    \
-                     break
-                     TYPES;
-#undef DO_TYPE
-                   default:
-                     abort ();
-                   }
-               }
-             else
-               {
-                 /* Extract the original value and apply the reloc.  */
-                 Elf_Data *d = gelf_xlatetom (debugfile, &tmpdata, &rdata,
-                                              ehdr->e_ident[EI_DATA]);
-                 if (d == NULL)
-                   return DWFL_E_LIBELF;
-                 assert (d == &tmpdata);
-                 switch (type)
-                   {
-#define DO_TYPE(NAME, Name)                                    \
-                     case ELF_T_##NAME:                        \
-                       tmpbuf.Name += (GElf_##Name) value;     \
-                     break
-                     TYPES;
-#undef DO_TYPE
-                   default:
-                     abort ();
-                   }
-               }
-
-             /* Now convert the relocated datum back to the target
-                format.  This will write into rdata.d_buf, which
-                points into the raw section data being relocated.  */
-             Elf_Data *s = gelf_xlatetof (debugfile, &rdata, &tmpdata,
-                                          ehdr->e_ident[EI_DATA]);
-             if (s == NULL)
-               return DWFL_E_LIBELF;
-             assert (s == &rdata);
-
-             /* We have applied this relocation!  */
-             return DWFL_E_NOERROR;
-           }
-
-         /* Fetch the relocation section and apply each reloc in it.  */
-         Elf_Data *reldata = elf_getdata (scn, NULL);
-         if (reldata == NULL)
-           return DWFL_E_LIBELF;
-
-         result = DWFL_E_NOERROR;
-         size_t nrels = shdr->sh_size / shdr->sh_entsize;
-         if (shdr->sh_type == SHT_REL)
-           for (size_t relidx = 0; !result && relidx < nrels; ++relidx)
-             {
-               GElf_Rel rel_mem, *r = gelf_getrel (reldata, relidx, &rel_mem);
-               if (r == NULL)
-                 return DWFL_E_LIBELF;
-               result = relocate (r->r_offset, NULL,
-                                  GELF_R_TYPE (r->r_info),
-                                  GELF_R_SYM (r->r_info));
-             }
+         if (unlikely (tscn == NULL))
+           result = DWFL_E_LIBELF;
          else
-           for (size_t relidx = 0; !result && relidx < nrels; ++relidx)
-             {
-               GElf_Rela rela_mem, *r = gelf_getrela (reldata, relidx,
-                                                      &rela_mem);
-               if (r == NULL)
-                 return DWFL_E_LIBELF;
-               result = relocate (r->r_offset, &r->r_addend,
-                                  GELF_R_TYPE (r->r_info),
-                                  GELF_R_SYM (r->r_info));
-             }
-         if (result != DWFL_E_NOERROR)
-           break;
-
-         /* Mark this relocation section as being empty now that we have
-            done its work.  This affects unstrip -R, so e.g. it emits an
-            empty .rela.debug_info along with a .debug_info that has
-            already been fully relocated.  */
-         shdr->sh_size = 0;
-         reldata->d_size = 0;
-         gelf_update_shdr (scn, shdr);
+           result = relocate_section (mod, debugfile, ehdr, d_shstrndx,
+                                      &reloc_symtab, scn, shdr, tscn,
+                                      debug, !debug);
        }
     }
 
   return result;
 }
+
+Dwfl_Error
+internal_function
+__libdwfl_relocate_section (Dwfl_Module *mod, Elf *relocated,
+                           Elf_Scn *relocscn, Elf_Scn *tscn, bool partial)
+{
+  GElf_Ehdr ehdr_mem;
+  GElf_Shdr shdr_mem;
+
+  RELOC_SYMTAB_CACHE (reloc_symtab);
+
+  size_t shstrndx;
+  if (elf_getshstrndx (relocated, &shstrndx) < 0)
+    return DWFL_E_LIBELF;
+
+  return (__libdwfl_module_getebl (mod)
+         ?: relocate_section (mod, relocated,
+                              gelf_getehdr (relocated, &ehdr_mem), shstrndx,
+                              &reloc_symtab,
+                              relocscn, gelf_getshdr (relocscn, &shdr_mem),
+                              tscn, false, partial));
+}
index ae31b8f273c51424b230016d17b83c431b8477f0..86c917814bae4ffe38365433571165a279673050 100644 (file)
@@ -1,3 +1,7 @@
+2007-10-18  Roland McGrath  <roland@redhat.com>
+
+       * eblcorenotetypename.c (ebl_core_note_type_name): Handle NT_PPC_VMX.
+
 2007-10-11  Roland McGrath  <roland@redhat.com>
 
        * eblobjnote.c (ebl_object_note): Translate target format (byte-swap)
index c2b57f9ec0d471c8688166edec07882d577851f4..44b0237645fa33c159dc5a3e66c1e5dc8887b87d 100644 (file)
@@ -86,20 +86,26 @@ ebl_core_note_type_name (ebl, type, buf, len)
          KNOWNSTYPE (LWPSTATUS),
          KNOWNSTYPE (LWPSINFO),
          KNOWNSTYPE (PRFPXREG)
+#undef KNOWNSTYPE
        };
 
       /* Handle standard names.  */
       if (type < sizeof (knowntypes) / sizeof (knowntypes[0])
          && knowntypes[type] != NULL)
        res = knowntypes[type];
-      else if (type == NT_PRXFPREG)
-       res = "PRXFPREG";
       else
-       {
-         snprintf (buf, len, "%s: %" PRIu32, gettext ("<unknown>"), type);
+       switch (type)
+         {
+#define KNOWNSTYPE(name) case NT_##name: res = #name; break
+           KNOWNSTYPE (PRXFPREG);
+           KNOWNSTYPE (PPC_VMX);
+#undef KNOWNSTYPE
+
+         default:
+           snprintf (buf, len, "%s: %" PRIu32, gettext ("<unknown>"), type);
 
-         res = buf;
-       }
+           res = buf;
+         }
     }
 
   return res;
index 247598c7b89fa1309ad5446b5fef10c788f88a32..3488c2e53c4454a6537b211aff7e54f423faa167 100644 (file)
@@ -1,3 +1,7 @@
+2007-10-18  Roland McGrath  <roland@redhat.com>
+
+       * elf.h: Update from glibc.
+
 2007-10-07  Roland McGrath  <roland@redhat.com>
 
        * elf_begin.c (__libelf_next_arhdr): Fix fencepost error and wrong
index 6cc547ef6030a208b47a3510f3fcb8f07c13a2fd..acd3d3e15563836504fcda5998a104ae4ab9013a 100644 (file)
@@ -604,6 +604,7 @@ typedef struct
 #define NT_LWPSINFO    17              /* Contains copy of lwpinfo struct */
 #define NT_PRFPXREG    20              /* Contains copy of fprxregset struct */
 #define NT_PRXFPREG    0x46e62b7f      /* Contains copy of user_fxsr_struct */
+#define NT_PPC_VMX     0x100           /* PowerPC Altivec/VMX registers */
 
 /* Legal values for the note segment descriptor types for object files.  */
 
@@ -1723,6 +1724,8 @@ typedef Elf32_Addr Elf32_Conflict;
 #define R_PARISC_LTOFF_FPTR14R 62      /* LT-rel. fct ptr, right 14 bits. */
 #define R_PARISC_FPTR64                64      /* 64 bits function address.  */
 #define R_PARISC_PLABEL32      65      /* 32 bits function address.  */
+#define R_PARISC_PLABEL21L     66      /* Left 21 bits of fdesc address.  */
+#define R_PARISC_PLABEL14R     70      /* Right 14 bits of fdesc address.  */
 #define R_PARISC_PCREL64       72      /* 64 bits PC-rel. address.  */
 #define R_PARISC_PCREL22F      74      /* 22 bits PC-rel. address.  */
 #define R_PARISC_PCREL14WR     75      /* PC-rel. address, right 14 bits.  */
@@ -1783,6 +1786,26 @@ typedef Elf32_Addr Elf32_Conflict;
 #define R_PARISC_LTOFF_TP16F   229     /* 16 bits LT-TP-rel. address.  */
 #define R_PARISC_LTOFF_TP16WF  230     /* 16 bits LT-TP-rel. address.  */
 #define R_PARISC_LTOFF_TP16DF  231     /* 16 bits LT-TP-rel. address.  */
+#define R_PARISC_GNU_VTENTRY   232
+#define R_PARISC_GNU_VTINHERIT 233
+#define R_PARISC_TLS_GD21L     234     /* GD 21-bit left.  */
+#define R_PARISC_TLS_GD14R     235     /* GD 14-bit right.  */
+#define R_PARISC_TLS_GDCALL    236     /* GD call to __t_g_a.  */
+#define R_PARISC_TLS_LDM21L    237     /* LD module 21-bit left.  */
+#define R_PARISC_TLS_LDM14R    238     /* LD module 14-bit right.  */
+#define R_PARISC_TLS_LDMCALL   239     /* LD module call to __t_g_a.  */
+#define R_PARISC_TLS_LDO21L    240     /* LD offset 21-bit left.  */
+#define R_PARISC_TLS_LDO14R    241     /* LD offset 14-bit right.  */
+#define R_PARISC_TLS_DTPMOD32  242     /* DTP module 32-bit.  */
+#define R_PARISC_TLS_DTPMOD64  243     /* DTP module 64-bit.  */
+#define R_PARISC_TLS_DTPOFF32  244     /* DTP offset 32-bit.  */
+#define R_PARISC_TLS_DTPOFF64  245     /* DTP offset 32-bit.  */
+#define R_PARISC_TLS_LE21L     R_PARISC_TPREL21L
+#define R_PARISC_TLS_LE14R     R_PARISC_TPREL14R
+#define R_PARISC_TLS_IE21L     R_PARISC_LTOFF_TP21L
+#define R_PARISC_TLS_IE14R     R_PARISC_LTOFF_TP14R
+#define R_PARISC_TLS_TPREL32   R_PARISC_TPREL32
+#define R_PARISC_TLS_TPREL64   R_PARISC_TPREL64
 #define R_PARISC_HIRESERVE     255
 
 /* Legal values for p_type field of Elf32_Phdr/Elf64_Phdr.  */
index ebd729fec6acb5d5fcaa41d5156f6daed5267350..df4305c23f4f40df426a682e9b4f1e8d9ecaab68 100644 (file)
@@ -1,3 +1,31 @@
+2007-10-20  Roland McGrath  <roland@redhat.com>
+
+       * unstrip.c (options): Update -R description.
+       (struct symbol): Put symbol details a union with a size_t pointer
+       `duplicate'.
+       (compare_symbols_output): Use null ->name as marker for discard
+       symbols, not zero *->map.
+       (copy_elided_sections): Record forwarding pointers for discarded
+       duplicates and fill SYMNDX_MAP elements through them.
+
+       * readelf.c (process_file): Set offline_next_address to 0 at start.
+       (struct process_dwflmod_args): New type.
+       (process_dwflmod): Take args in it, pass fd to process_elf_file.
+       (process_file): Update caller; dup FD for passing to libdwfl.
+       (process_elf_file): Take new arg FD.  For ET_REL file when
+       displaying data affected by libdwfl relocation, open a new Elf handle.
+
+2007-10-17  Roland McGrath  <roland@redhat.com>
+
+       * readelf.c (print_debug_line_section): For invalid data inside a
+       unit with plausible length, keep printing at the next unit boundary.
+
+       * readelf.c (attr_callback): Use dwarf_formref_die, not dwarf_formref.
+
+2007-10-16  Roland McGrath  <roland@redhat.com>
+
+       * readelf.c (hex_dump): Fix rounding error in whitespace calculation.
+
 2007-10-15  Roland McGrath  <roland@redhat.com>
 
        * make-debug-archive.in: New file.
index c591b3227b3f04caf53a71d3dcf023ace69b0357..2197d84089196524fc75bf66364c2725c0f2c720 100644 (file)
@@ -54,6 +54,7 @@
 #include "../libelf/libelfP.h"
 #include "../libebl/libeblP.h"
 #include "../libdw/libdwP.h"
+#include "../libdwfl/libdwflP.h"
 #include "../libdw/memory-access.h"
 
 
@@ -200,7 +201,7 @@ static size_t shnum;
 
 /* Declarations of local functions.  */
 static void process_file (int fd, const char *fname, bool only_one);
-static void process_elf_file (Dwfl_Module *dwflmod);
+static void process_elf_file (Dwfl_Module *dwflmod, int fd);
 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);
@@ -460,6 +461,12 @@ count_dwflmod (Dwfl_Module *dwflmod __attribute__ ((unused)),
   return DWARF_CB_ABORT;
 }
 
+struct process_dwflmod_args
+{
+  int fd;
+  bool only_one;
+};
+
 static int
 process_dwflmod (Dwfl_Module *dwflmod,
                 void **userdata __attribute__ ((unused)),
@@ -467,10 +474,10 @@ process_dwflmod (Dwfl_Module *dwflmod,
                 Dwarf_Addr base __attribute__ ((unused)),
                 void *arg)
 {
-  bool only_one = *(bool *) arg;
+  const struct process_dwflmod_args *a = arg;
 
   /* Print the file name.  */
-  if (!only_one)
+  if (!a->only_one)
     {
       const char *fname;
       dwfl_module_info (dwflmod, NULL, NULL, NULL, NULL, NULL, &fname, NULL);
@@ -478,7 +485,7 @@ process_dwflmod (Dwfl_Module *dwflmod,
       printf ("\n%s:\n\n", fname);
     }
 
-  process_elf_file (dwflmod);
+  process_elf_file (dwflmod, a->fd);
 
   return DWARF_CB_OK;
 }
@@ -507,6 +514,11 @@ process_file (int fd, const char *fname, bool only_one)
   if (!any_control_option)
     return;
 
+  /* Duplicate an fd for dwfl_report_offline to swallow.  */
+  int dwfl_fd = dup (fd);
+  if (unlikely (dwfl_fd < 0))
+    error (EXIT_FAILURE, errno, "dup");
+
   /* 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 =
@@ -515,7 +527,10 @@ process_file (int fd, const char *fname, bool only_one)
       .find_debuginfo = find_no_debuginfo
     };
   Dwfl *dwfl = dwfl_begin (&callbacks);
-  if (dwfl_report_offline (dwfl, fname, fname, fd) == NULL)
+  if (likely (dwfl != NULL))
+    /* Let 0 be the logical address of the file (or first in archive).  */
+    dwfl->offline_next_address = 0;
+  if (dwfl_report_offline (dwfl, fname, fname, dwfl_fd) == NULL)
     {
       struct stat64 st;
       if (fstat64 (fd, &st) != 0)
@@ -535,7 +550,8 @@ process_file (int fd, const char *fname, bool only_one)
        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);
+      struct process_dwflmod_args a = { .fd = fd, .only_one = only_one };
+      dwfl_getmodules (dwfl, &process_dwflmod, &a, 0);
     }
   dwfl_end (dwfl);
 }
@@ -543,7 +559,7 @@ process_file (int fd, const char *fname, bool only_one)
 
 /* Process one ELF file.  */
 static void
-process_elf_file (Dwfl_Module *dwflmod)
+process_elf_file (Dwfl_Module *dwflmod, int fd)
 {
   GElf_Addr dwflbias;
   Elf *elf = dwfl_module_getelf (dwflmod, &dwflbias);
@@ -553,6 +569,7 @@ process_elf_file (Dwfl_Module *dwflmod)
 
   if (ehdr == NULL)
     {
+    elf_error:
       error (0, 0, gettext ("cannot read ELF header: %s"), elf_errmsg (-1));
       return;
     }
@@ -560,6 +577,7 @@ process_elf_file (Dwfl_Module *dwflmod)
   Ebl *ebl = ebl_openbackend (elf);
   if (ebl == NULL)
     {
+    ebl_error:
       error (0, errno, gettext ("cannot create EBL handle"));
       return;
     }
@@ -570,10 +588,40 @@ process_elf_file (Dwfl_Module *dwflmod)
           gettext ("cannot determine number of sections: %s"),
           elf_errmsg (-1));
 
+  /* For an ET_REL file, libdwfl has adjusted the in-core shdrs
+     and may have applied relocation to some sections.
+     So we need to get a fresh Elf handle on the file to display those.  */
+  bool print_unrelocated = (print_section_header
+                           || print_relocations
+                           || dump_data_sections != NULL
+                           || print_notes);
+
+  Elf *pure_elf = NULL;
+  Ebl *pure_ebl = ebl;
+  if (ehdr->e_type == ET_REL && print_unrelocated)
+    {
+      /* Read the file afresh.  */
+      pure_elf = elf_begin (fd, ELF_C_READ_MMAP, NULL);
+      off64_t aroff = elf_getaroff (elf);
+      if (aroff > 0)
+       {
+         /* Archive member.  */
+         (void) elf_rand (pure_elf, aroff);
+         Elf *armem = elf_begin (-1, ELF_C_READ_MMAP, pure_elf);
+         elf_end (pure_elf);
+         pure_elf = armem;
+       }
+      if (pure_elf == NULL)
+       goto elf_error;
+      pure_ebl = ebl_openbackend (pure_elf);
+      if (pure_ebl == NULL)
+       goto ebl_error;
+    }
+
   if (print_file_header)
     print_ehdr (ebl, ehdr);
   if (print_section_header)
-    print_shdr (ebl, ehdr);
+    print_shdr (pure_ebl, ehdr);
   if (print_program_header)
     print_phdr (ebl, ehdr);
   if (print_section_groups)
@@ -581,7 +629,7 @@ process_elf_file (Dwfl_Module *dwflmod)
   if (print_dynamic_table)
     print_dynamic (ebl, ehdr);
   if (print_relocations)
-    print_relocs (ebl);
+    print_relocs (pure_ebl);
   if (print_histogram)
     handle_hash (ebl);
   if (print_symbol_table)
@@ -593,17 +641,23 @@ process_elf_file (Dwfl_Module *dwflmod)
   if (print_arch)
     print_liblist (ebl);
   if (dump_data_sections != NULL)
-    dump_data (ebl);
+    dump_data (pure_ebl);
   if (string_sections != NULL)
     dump_strings (ebl);
   if (print_debug_sections != 0)
     print_debug (dwflmod, ebl, ehdr);
   if (print_notes)
-    handle_notes (ebl, ehdr);
+    handle_notes (pure_ebl, ehdr);
   if (print_string_sections)
     print_strings (ebl);
 
   ebl_closebackend (ebl);
+
+  if (pure_ebl != ebl)
+    {
+      ebl_closebackend (pure_ebl);
+      elf_end (pure_elf);
+    }
 }
 
 
@@ -4027,13 +4081,13 @@ attr_callback (Dwarf_Attribute *attrp, void *arg)
     case DW_FORM_ref4:
     case DW_FORM_ref2:
     case DW_FORM_ref1:;
-      Dwarf_Off ref;
-      if (unlikely (dwarf_formref (attrp, &ref) != 0))
+      Dwarf_Die ref;
+      if (unlikely (dwarf_formref_die (attrp, &ref) == NULL))
        goto attrval_out;
 
       printf ("           %*s%-20s [%6" PRIxMAX "]\n",
              (int) (level * 2), "", dwarf_attr_string (attr),
-             (uintmax_t) (ref + cbargs->cu_offset));
+             (uintmax_t) dwarf_dieoffset (&ref));
       break;
 
     case DW_FORM_udata:
@@ -4384,7 +4438,15 @@ print_debug_line_section (Dwfl_Module *dwflmod, Ebl *ebl,
              line_range, opcode_base);
 
       if (unlikely (linep + opcode_base - 1 >= lineendp))
-       goto invalid_data;
+       {
+       invalid_unit:
+         error (0, 0,
+                gettext ("invalid data at offset %tu in section [%zu] '%s'"),
+                linep - (const unsigned char *) data->d_buf,
+                elf_ndxscn (scn), ".debug_line");
+         linep = lineendp;
+         continue;
+       }
       int opcode_base_l10 = 1;
       unsigned int tmp = opcode_base;
       while (tmp > 10)
@@ -4400,14 +4462,14 @@ print_debug_line_section (Dwfl_Module *dwflmod, Ebl *ebl,
                opcode_base_l10, cnt, linep[cnt - 1]);
       linep += opcode_base - 1;
       if (unlikely (linep >= lineendp))
-       goto invalid_data;
+       goto invalid_unit;
 
       puts (gettext ("\nDirectory table:"));
       while (*linep != 0)
        {
          unsigned char *endp = memchr (linep, '\0', lineendp - linep);
          if (endp == NULL)
-           goto invalid_data;
+           goto invalid_unit;
 
          printf (" %s\n", (char *) linep);
 
@@ -4417,7 +4479,7 @@ print_debug_line_section (Dwfl_Module *dwflmod, Ebl *ebl,
       ++linep;
 
       if (unlikely (linep >= lineendp))
-       goto invalid_data;
+       goto invalid_unit;
       puts (gettext ("\nFile name table:\n"
                     " Entry Dir   Time      Size      Name"));
       for (unsigned int cnt = 1; *linep != 0; ++cnt)
@@ -4426,7 +4488,7 @@ print_debug_line_section (Dwfl_Module *dwflmod, Ebl *ebl,
          char *fname = (char *) linep;
          unsigned char *endp = memchr (fname, '\0', lineendp - linep);
          if (endp == NULL)
-           goto invalid_data;
+           goto invalid_unit;
          linep = endp + 1;
 
          /* Then the index.  */
@@ -4517,13 +4579,13 @@ print_debug_line_section (Dwfl_Module *dwflmod, Ebl *ebl,
            {
              /* This an extended opcode.  */
              if (unlikely (linep + 2 > lineendp))
-               goto invalid_data;
+               goto invalid_unit;
 
              /* The length.  */
              unsigned int len = *linep++;
 
              if (unlikely (linep + len > lineendp))
-               goto invalid_data;
+               goto invalid_unit;
 
              /* The sub-opcode.  */
              opcode = *linep++;
@@ -4559,7 +4621,7 @@ print_debug_line_section (Dwfl_Module *dwflmod, Ebl *ebl,
                    unsigned char *endp = memchr (linep, '\0',
                                                  lineendp - linep);
                    if (endp == NULL)
-                     goto invalid_data;
+                     goto invalid_unit;
                    linep = endp + 1;
 
                    unsigned int diridx;
@@ -4626,7 +4688,7 @@ define new file: dir=%u, mtime=%" PRIu64 ", length=%" PRIu64 ", name=%s\n"),
                case DW_LNS_set_column:
                  /* Takes one uleb128 parameter which is stored in column.  */
                  if (unlikely (standard_opcode_lengths[opcode] != 1))
-                   goto invalid_data;
+                   goto invalid_unit;
 
                  get_uleb128 (u128, linep);
                  printf (gettext (" set column to %" PRIu64 "\n"),
@@ -4662,7 +4724,7 @@ define new file: dir=%u, mtime=%" PRIu64 ", length=%" PRIu64 ", name=%s\n"),
                  /* Takes one 16 bit parameter which is added to the
                     address.  */
                  if (unlikely (standard_opcode_lengths[opcode] != 1))
-                   goto invalid_data;
+                   goto invalid_unit;
 
                  u128 = read_2ubyte_unaligned_inc (dbg, linep);
                  address += u128;
@@ -5888,7 +5950,7 @@ hex_dump (const uint8_t *data, size_t len)
          printf ("%02x", data[pos + i]);
 
       if (chunk < 16)
-       printf ("%*s", (int) ((16 - chunk) * 2 + (16 - chunk) / 4), "");
+       printf ("%*s", (int) ((16 - chunk) * 2 + (16 - chunk + 3) / 4), "");
 
       for (size_t i = 0; i < chunk; ++i)
        {
index d19ad27e092d4ffeff9ff5cb4b989ddc0a06b1c3..2670835af98660dfb593a227173996ee04c69d17 100644 (file)
@@ -87,7 +87,7 @@ static const struct argp_option options[] =
     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 },
+    N_("Apply relocations to section contents 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 }
@@ -719,17 +719,27 @@ struct symbol
     const char *name;
     struct Ebl_Strent *strent;
   };
-  GElf_Addr value;
-  GElf_Xword size;
-  GElf_Word shndx;
   union
   {
     struct
     {
-      uint8_t info;
-      uint8_t other;
-    } info;
-    int16_t compare;
+      GElf_Addr value;
+      GElf_Xword size;
+      GElf_Word shndx;
+      union
+      {
+       struct
+       {
+         uint8_t info;
+         uint8_t other;
+       } info;
+       int16_t compare;
+      };
+    };
+
+    /* For a symbol discarded after first sort, this matches its better's
+       map pointer.  */
+    size_t *duplicate;
   };
 };
 
@@ -819,7 +829,7 @@ compare_symbols_output (const void *a, const void *b)
   int cmp;
 
   /* Sort discarded symbols last.  */
-  cmp = (*s1->map == 0) - (*s2->map == 0);
+  cmp = (s1->name == NULL) - (s2->name == NULL);
 
   if (cmp == 0)
     /* Local symbols must come first.  */
@@ -1605,31 +1615,65 @@ copy_elided_sections (Elf *unstripped, Elf *stripped,
         new slots, collecting a map from old indices to new.  */
       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);
+       {
+         /* Skip a section symbol for a removed section.  */
+         if (s->shndx == SHN_UNDEF
+             && GELF_ST_TYPE (s->info.info) == STT_SECTION)
+           {
+             s->name = NULL;   /* Mark as discarded. */
+             *s->map = STN_UNDEF;
+             s->duplicate = NULL;
+             continue;
+           }
+
+         struct symbol *n = s;
+         while (n + 1 < &symbols[total_syms] && !compare_symbols (s, n + 1))
+           ++n;
+
+         while (s < n)
+           {
+             /* This is a duplicate.  Its twin will get the next slot.  */
+             s->name = NULL;   /* Mark as discarded. */
+             s->duplicate = n->map;
+             ++s;
+           }
+
+         /* Allocate the next slot.  */
+         *s->map = ++nsym;
+       }
 
       /* Now we sort again, to determine the order in the output.  */
       qsort (symbols, total_syms, sizeof symbols[0], compare_symbols_output);
 
       if (nsym < total_syms)
        /* The discarded symbols are now at the end of the table.  */
-       assert (*symbols[nsym].map == 0);
+       assert (symbols[nsym].name == NULL);
 
       /* Now a final pass updates the map with the final order,
         and builds up the new string table.  */
       symstrtab = ebl_strtabinit (true);
       for (size_t i = 0; i < nsym; ++i)
        {
+         assert (symbols[i].name != NULL);
          assert (*symbols[i].map != 0);
-         *symbols[i].map = i;
+         *symbols[i].map = 1 + i;
          symbols[i].strent = ebl_strtabadd (symstrtab, symbols[i].name, 0);
        }
 
+      /* Scan the discarded symbols too, just to update their slots
+        in SYMNDX_MAP to refer to their live duplicates.  */
+      for (size_t i = nsym; i < total_syms; ++i)
+       {
+         assert (symbols[i].name == NULL);
+         if (symbols[i].duplicate == NULL)
+           assert (*symbols[i].map == STN_UNDEF);
+         else
+           {
+             assert (*symbols[i].duplicate != STN_UNDEF);
+             *symbols[i].map = *symbols[i].duplicate;
+           }
+       }
+
       /* Now we are ready to write the new symbol table.  */
       symdata = elf_getdata (unstripped_symtab, NULL);
       symstrdata = elf_getdata (unstripped_strtab, NULL);
index 1284b134e7757929ed19e46dc4131bd162b5c180..f029f15666ddcf481cec25465dc8a497c1a803e9 100644 (file)
@@ -1,3 +1,24 @@
+2007-10-20  Roland McGrath  <roland@redhat.com>
+
+       * run-dwfl-addr-sect.sh: Change expected output, no errors.
+
+2007-10-19  Roland McGrath  <roland@redhat.com>
+
+       * dwfl-addr-sect.c (handle_address): Return int.
+       Don't exit on error, just return nonzero.
+       (main): Collect results.
+       * run-dwfl-addr-sect.sh: New file.
+       * testfile43.bz2: New data file.
+       * Makefile.am (EXTRA_DIST, TESTS): Add them.
+
+2007-10-18  Roland McGrath  <roland@redhat.com>
+
+       * run-allregs.sh: Update expected ppc output for vrsave/vscr.
+
+2007-10-16  Roland McGrath  <roland@redhat.com>
+
+       * test-subr.sh (remove_files): Don't pass -Bb to diff.
+
 2007-10-09  Roland McGrath  <roland@redhat.com>
 
        * dwflmodtest.c (print_module): Don't use %p in output.
index ab325c7afd0f9f3619ad185a6781ad63294960a4..4556078889537daf746faffd546827679530964e 100644 (file)
@@ -83,7 +83,7 @@ TESTS = run-arextract.sh run-arsymtest.sh newfile test-nlist \
        run-native-test.sh run-bug1-test.sh \
        dwfl-bug-addr-overflow run-addrname-test.sh \
        dwfl-bug-fd-leak dwfl-bug-report \
-       run-dwfl-bug-offline-rel.sh
+       run-dwfl-bug-offline-rel.sh run-dwfl-addr-sect.sh
 # run-show-ciefde.sh
 
 if !STANDALONE
@@ -114,6 +114,7 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.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-dwfl-bug-offline-rel.sh \
+            run-dwfl-addr-sect.sh \
             testfile15.bz2 testfile15.debug.bz2 \
             testfile16.bz2 testfile16.debug.bz2 \
             testfile17.bz2 testfile17.debug.bz2 \
@@ -132,7 +133,7 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh \
             testfile36.bz2 testfile36.debug.bz2 \
             testfile37.bz2 testfile37.debug.bz2 \
             testfile38.bz2 testfile39.bz2 testfile40.bz2 testfile40.debug.bz2 \
-            testfile41.bz2 testfile42.bz2
+            testfile41.bz2 testfile42.bz2 testfile43.bz2
 
 installed_TESTS_ENVIRONMENT = libdir=$(DESTDIR)$(libdir) \
                              bindir=$(DESTDIR)$(bindir) \
index b1f29f7c8f9de93caaefc04511740b913463d18e..62d1154177372ab3aac8361395d69c817d765ff5 100644 (file)
@@ -37,8 +37,7 @@
 #include ELFUTILS_HEADER(dwfl)
 #include <dwarf.h>
 
-
-static void
+static int
 handle_address (Dwfl *dwfl, Dwarf_Addr address)
 {
   Dwfl_Module *mod = dwfl_addrmodule (dwfl, address);
@@ -46,12 +45,16 @@ handle_address (Dwfl *dwfl, Dwarf_Addr address)
   Dwarf_Addr bias;
   Elf_Scn *scn = dwfl_module_address_section (mod, &adjusted, &bias);
   if (scn == NULL)
-    error (EXIT_FAILURE, 0, "%#" PRIx64 ": dwfl_module_address_section: %s",
-          address, dwfl_errmsg (-1));
+    {
+      error (0, 0, "%#" PRIx64 ": dwfl_module_address_section: %s",
+            address, dwfl_errmsg (-1));
+      return 1;
+    }
   printf ("address %#" PRIx64 " => module \"%s\" section %zu + %#" PRIx64 "\n",
          address,
          dwfl_module_info (mod, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
          elf_ndxscn (scn), adjusted);
+  return 0;
 }
 
 int
@@ -74,7 +77,7 @@ main (int argc, char **argv)
       char *endp;
       uintmax_t addr = strtoumax (argv[remaining], &endp, 0);
       if (endp != argv[remaining])
-       handle_address (dwfl, addr);
+       result |= handle_address (dwfl, addr);
       else
        result = 1;
     }
index c7f46b0b9a7acc582227bbcf922704fcda1bfc1b..82d5740970b1b84ab869f256d3b2a1e9591fe671 100755 (executable)
@@ -502,7 +502,6 @@ privileged registers:
        353: spr253 (spr253), unsigned 32 bits
        354: spr254 (spr254), unsigned 32 bits
        355: spr255 (spr255), unsigned 32 bits
-       356: vrsave (vrsave), unsigned 32 bits
        357: spr257 (spr257), unsigned 32 bits
        358: spr258 (spr258), unsigned 32 bits
        359: spr259 (spr259), unsigned 32 bits
@@ -1147,6 +1146,8 @@ privileged registers:
        998: spr898 (spr898), unsigned 32 bits
        999: spr899 (spr899), unsigned 32 bits
 vector registers:
+        67: vscr (vscr), unsigned 32 bits
+       356: vrsave (vrsave), unsigned 32 bits
        1124: vr0 (vr0), unsigned 128 bits
        1125: vr1 (vr1), unsigned 128 bits
        1126: vr2 (vr2), unsigned 128 bits
@@ -1524,7 +1525,6 @@ privileged registers:
        353: spr253 (spr253), unsigned 64 bits
        354: spr254 (spr254), unsigned 64 bits
        355: spr255 (spr255), unsigned 64 bits
-       356: vrsave (vrsave), unsigned 64 bits
        357: spr257 (spr257), unsigned 64 bits
        358: spr258 (spr258), unsigned 64 bits
        359: spr259 (spr259), unsigned 64 bits
@@ -2169,6 +2169,8 @@ privileged registers:
        998: spr898 (spr898), unsigned 64 bits
        999: spr899 (spr899), unsigned 64 bits
 vector registers:
+        67: vscr (vscr), unsigned 32 bits
+       356: vrsave (vrsave), unsigned 32 bits
        1124: vr0 (vr0), unsigned 128 bits
        1125: vr1 (vr1), unsigned 128 bits
        1126: vr2 (vr2), unsigned 128 bits
diff --git a/tests/run-dwfl-addr-sect.sh b/tests/run-dwfl-addr-sect.sh
new file mode 100755 (executable)
index 0000000..69280f5
--- /dev/null
@@ -0,0 +1,37 @@
+#! /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 testfile43
+
+export LC_ALL=C
+testrun_compare ./dwfl-addr-sect -e testfile43 0x64 0x8 0x98 <<\EOF
+address 0x64 => module "" section 4 + 0
+address 0x8 => module "" section 1 + 0x8
+address 0x98 => module "" section 7 + 0
+EOF
+
+exit 0
index d92c602e3ae344afb4d07cfd53f2534c146afdaf..7fda05a0ddde7079940bf18ee05af4590d8e85e1 100644 (file)
@@ -1,5 +1,5 @@
 #! /bin/sh
-# Copyright (C) 2005 Red Hat, Inc.
+# Copyright (C) 2005, 2007 Red Hat, Inc.
 # This file is part of Red Hat elfutils.
 #
 # Red Hat elfutils is free software; you can redistribute it and/or modify
@@ -58,7 +58,7 @@ testrun_compare()
 {
   outfile="${1##*/}.out"
   testrun_out $outfile "$@"
-  diff -Bbu $outfile -
+  diff -u $outfile -
   # diff's exit status will kill the script.
 }
 
diff --git a/tests/testfile43.bz2 b/tests/testfile43.bz2
new file mode 100644 (file)
index 0000000..c99db24
Binary files /dev/null and b/tests/testfile43.bz2 differ