summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoland McGrath <roland@redhat.com>2007-04-25 03:09:33 +0000
committerRoland McGrath <roland@redhat.com>2007-04-25 03:09:33 +0000
commit4be1524398af8e24011cfdfa77c66832f8654a56 (patch)
tree84d4745346b459516ec67743a1b86b47793f146f
parent89d6b4c552a469482d2a358fb492d39eb22beb13 (diff)
downloadelfutils-4be1524398af8e24011cfdfa77c66832f8654a56.tar.gz
2007-04-24 Roland McGrath <roland@redhat.com>
* run-strip-test.sh: When we saved the debug info, test unstrip too.
-rw-r--r--NEWS6
-rw-r--r--backends/ChangeLog9
-rw-r--r--backends/i386_regs.c4
-rw-r--r--backends/ppc_regs.c35
-rw-r--r--backends/x86_64_regs.c1
-rw-r--r--config/ChangeLog4
-rw-r--r--config/elfutils.spec.in3
-rw-r--r--libdwfl/ChangeLog10
-rw-r--r--libdwfl/argp-std.c12
-rw-r--r--libdwfl/dwfl_module.c1
-rw-r--r--libebl/ChangeLog4
-rw-r--r--libebl/eblcorenotetypename.c4
-rw-r--r--libelf/ChangeLog4
-rw-r--r--libelf/elf.h21
-rw-r--r--src/ChangeLog16
-rw-r--r--src/Makefile.am3
-rw-r--r--src/elfcmp.c115
-rw-r--r--src/strip.c2
-rw-r--r--src/unstrip.c1768
-rw-r--r--tests/ChangeLog8
-rwxr-xr-xtests/run-allregs.sh102
-rwxr-xr-xtests/run-strip-test.sh10
22 files changed, 2064 insertions, 78 deletions
diff --git a/NEWS b/NEWS
index 99b66e28..95374f88 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,9 @@
+Version 0.128:
+
+new program: unstrip
+
+elfcmp: new option --hash-inexact
+
Version 0.127:
libdw: new function dwarf_getsrcdirs
diff --git a/backends/ChangeLog b/backends/ChangeLog
index e9f34158..646e8110 100644
--- a/backends/ChangeLog
+++ b/backends/ChangeLog
@@ -1,3 +1,12 @@
+2007-04-22 Roland McGrath <roland@redhat.com>
+
+ * ppc_regs.c (ppc_register_info): Use some names instead of sprNNN:
+ mq, xer, lr, ctr, dsisr, dar, dec, vrsave.
+ Set *BITS to 64 for FPU registers.
+
+ * i386_regs.c (i386_register_info): Set *BITS to 16 for fctrl, fstat.
+ * x86_64_regs.c (x86_64_register_info): Likewise for fcw, fsw.
+
2007-04-01 Roland McGrath <roland@redhat.com>
* x86_64_regs.c (x86_64_register_info): Add more registers from newer
diff --git a/backends/i386_regs.c b/backends/i386_regs.c
index a63c5439..5cf0d810 100644
--- a/backends/i386_regs.c
+++ b/backends/i386_regs.c
@@ -1,5 +1,5 @@
/* Register names and numbers for i386 DWARF.
- Copyright (C) 2005, 2006 Red Hat, Inc.
+ Copyright (C) 2005, 2006, 2007 Red Hat, Inc.
This file is part of Red Hat elfutils.
Red Hat elfutils is free software; you can redistribute it and/or modify
@@ -125,8 +125,10 @@ i386_register_info (Ebl *ebl __attribute__ ((unused)),
break;
case 37:
+ *bits = 16;
return stpcpy (name, "fctrl") + 1 - name;
case 38:
+ *bits = 16;
return stpcpy (name, "fstat") + 1 - name;
case 39:
return stpcpy (name, "mxcsr") + 1 - name;
diff --git a/backends/ppc_regs.c b/backends/ppc_regs.c
index 3d47d3d0..4cf5abc6 100644
--- a/backends/ppc_regs.c
+++ b/backends/ppc_regs.c
@@ -1,5 +1,5 @@
/* Register names and numbers for PowerPC DWARF.
- Copyright (C) 2005, 2006 Red Hat, Inc.
+ Copyright (C) 2005, 2006, 2007 Red Hat, Inc.
This file is part of Red Hat elfutils.
Red Hat elfutils is free software; you can redistribute it and/or modify
@@ -53,7 +53,11 @@ ppc_register_info (Ebl *ebl __attribute__ ((unused)),
if (regno < 32 || regno == 64 || regno == 66)
*setname = "integer";
else if (regno < 64 || regno == 65)
- *setname = "FPU";
+ {
+ *setname = "FPU";
+ if (ebl->machine != EM_PPC64 && regno < 64)
+ *bits = 64;
+ }
else if (regno < 1124)
*setname = "privileged";
else
@@ -112,7 +116,25 @@ ppc_register_info (Ebl *ebl __attribute__ ((unused)),
namelen = 4;
break;
- case 100 ... 109:
+ case 101:
+ return stpcpy (name, "xer") + 1 - name;
+ case 108:
+ return stpcpy (name, "lr") + 1 - name;
+ case 109:
+ return stpcpy (name, "ctr") + 1 - name;
+ case 118:
+ return stpcpy (name, "dsisr") + 1 - name;
+ case 119:
+ return stpcpy (name, "dar") + 1 - name;
+ case 122:
+ return stpcpy (name, "dec") + 1 - name;
+ case 356:
+ return stpcpy (name, "vrsave") + 1 - name;
+ case 100:
+ if (*bits == 32)
+ return stpcpy (name, "mq") + 1 - name;
+
+ case 102 ... 107:
name[0] = 's';
name[1] = 'p';
name[2] = 'r';
@@ -120,7 +142,9 @@ ppc_register_info (Ebl *ebl __attribute__ ((unused)),
namelen = 4;
break;
- case 110 ... 199:
+ case 110 ... 117:
+ case 120 ... 121:
+ case 123 ... 199:
name[0] = 's';
name[1] = 'p';
name[2] = 'r';
@@ -129,7 +153,8 @@ ppc_register_info (Ebl *ebl __attribute__ ((unused)),
namelen = 5;
break;
- case 200 ... 999:
+ case 200 ... 355:
+ case 357 ... 999:
name[0] = 's';
name[1] = 'p';
name[2] = 'r';
diff --git a/backends/x86_64_regs.c b/backends/x86_64_regs.c
index c17af85e..cfa40508 100644
--- a/backends/x86_64_regs.c
+++ b/backends/x86_64_regs.c
@@ -165,6 +165,7 @@ x86_64_register_info (Ebl *ebl __attribute__ ((unused)),
return stpcpy (name, "mxcsr") + 1 - name;
case 65 ... 66:
+ *bits = 16;
name[0] = 'f';
name[1] = "cs"[regno - 65];
name[2] = 'w';
diff --git a/config/ChangeLog b/config/ChangeLog
index 43d44ac9..5f712829 100644
--- a/config/ChangeLog
+++ b/config/ChangeLog
@@ -1,3 +1,7 @@
+2007-04-23 Roland McGrath <roland@redhat.com>
+
+ * elfutils.spec.in: Distribute eu-unstrip.
+
2005-08-13 Roland McGrath <roland@redhat.com>
* Makefile.am ($(srcdir)/elfutils.spec.in): Add missing $.
diff --git a/config/elfutils.spec.in b/config/elfutils.spec.in
index 25c2f7b9..9dc436ad 100644
--- a/config/elfutils.spec.in
+++ b/config/elfutils.spec.in
@@ -139,6 +139,7 @@ rm -rf ${RPM_BUILD_ROOT}
%{_bindir}/eu-strings
%{_bindir}/eu-objdump
%{_bindir}/eu-ar
+%{_bindir}/eu-unstrip
#%{_bindir}/eu-ld
#%{_libdir}/libasm-%{version}.so
%{_libdir}/libdw-%{version}.so
@@ -225,7 +226,7 @@ hes.
- Bug fixes.
- dwarf.h updated for DWARF 3.0 final specification.
- libdwfl: New function dwfl_version.
-- The license is now GPL for most files. The libelf, libebl, libdw,and
+- The license is now GPL for most files. The libelf, libebl, libdw,and
libdwfl libraries have additional exceptions. Add reference toOIN.
* Thu Jan 12 2006 Roland McGrath <roland@redhat.com> 0.119-1
diff --git a/libdwfl/ChangeLog b/libdwfl/ChangeLog
index 3e030d30..39c7ee29 100644
--- a/libdwfl/ChangeLog
+++ b/libdwfl/ChangeLog
@@ -1,3 +1,13 @@
+2007-04-23 Roland McGrath <roland@redhat.com>
+
+ * argp-std.c (options): Fix group title string.
+
+ * argp-std.c (parse_opt): Handle ARGP_KEY_ERROR, free the Dwfl.
+ Update via STATE->input every time we set STATE->hook, not only at
+ ARGP_KEY_SUCCESS.
+
+ * dwfl_module.c (free_file): Free FILE->name.
+
2007-04-16 Roland McGrath <roland@redhat.com>
* derelocate.c (cache_sections): Apply bias to sh_addr.
diff --git a/libdwfl/argp-std.c b/libdwfl/argp-std.c
index c12cb1dd..11397889 100644
--- a/libdwfl/argp-std.c
+++ b/libdwfl/argp-std.c
@@ -61,7 +61,7 @@
static const struct argp_option options[] =
{
- { NULL, 0, NULL, 0, N_("Input Selection:"), 0 },
+ { NULL, 0, NULL, 0, N_("Input selection options:"), 0 },
{ "executable", 'e', "FILE", 0, N_("Find addresses in FILE"), 0 },
{ "pid", 'p', "PID", 0,
N_("Find addresses in files mapped into process PID"), 0 },
@@ -244,14 +244,20 @@ parse_opt (int key, char *arg, struct argp_state *state)
int result = INTUSE(dwfl_report_end) (dwfl, NULL, NULL);
assert (result == 0);
-
- *(Dwfl **) state->input = dwfl;
}
break;
+ case ARGP_KEY_ERROR:
+ dwfl_end (state->hook);
+ state->hook = NULL;
+ break;
+
default:
return ARGP_ERR_UNKNOWN;
}
+
+ /* Update the input all along, so a parent parser can see it. */
+ *(Dwfl **) state->input = state->hook;
return 0;
}
diff --git a/libdwfl/dwfl_module.c b/libdwfl/dwfl_module.c
index e3bce544..a47b068a 100644
--- a/libdwfl/dwfl_module.c
+++ b/libdwfl/dwfl_module.c
@@ -67,6 +67,7 @@ nofree (void *arg __attribute__ ((unused)))
static void
free_file (struct dwfl_file *file)
{
+ free (file->name);
if (file->elf != NULL)
{
elf_end (file->elf);
diff --git a/libebl/ChangeLog b/libebl/ChangeLog
index 6465e720..cda50af4 100644
--- a/libebl/ChangeLog
+++ b/libebl/ChangeLog
@@ -1,3 +1,7 @@
+2007-04-22 Roland McGrath <roland@redhat.com>
+
+ * eblcorenotetypename.c (ebl_core_note_type_name): Handle NT_PRXFPREG.
+
2007-03-10 Roland McGrath <roland@redhat.com>
* eblcorenote.c (ebl_core_note): For normally-zero types,
diff --git a/libebl/eblcorenotetypename.c b/libebl/eblcorenotetypename.c
index b439188b..c2b57f9e 100644
--- a/libebl/eblcorenotetypename.c
+++ b/libebl/eblcorenotetypename.c
@@ -1,5 +1,5 @@
/* Return note type name.
- Copyright (C) 2002 Red Hat, Inc.
+ Copyright (C) 2002, 2007 Red Hat, Inc.
This file is part of Red Hat elfutils.
Written by Ulrich Drepper <drepper@redhat.com>, 2002.
@@ -92,6 +92,8 @@ ebl_core_note_type_name (ebl, type, buf, len)
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);
diff --git a/libelf/ChangeLog b/libelf/ChangeLog
index b634054b..be1735fb 100644
--- a/libelf/ChangeLog
+++ b/libelf/ChangeLog
@@ -1,3 +1,7 @@
+2007-04-22 Roland McGrath <roland@redhat.com>
+
+ * elf.h: Update from glibc.
+
2007-03-18 Roland McGrath <roland@redhat.com>
* elf_begin.c (get_shnum): Fix test for e_shoff being out of bounds.
diff --git a/libelf/elf.h b/libelf/elf.h
index dcadd60f..6c2d54c1 100644
--- a/libelf/elf.h
+++ b/libelf/elf.h
@@ -1,5 +1,5 @@
/* This file defines standard ELF types, structures, and macros.
- Copyright (C) 1995-2003, 2004, 2005, 2006 Free Software Foundation, Inc.
+ Copyright (C) 1995-2003,2004,2005,2006,2007 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
@@ -329,8 +329,8 @@ typedef struct
#define SHT_GROUP 17 /* Section group */
#define SHT_SYMTAB_SHNDX 18 /* Extended section indeces */
#define SHT_NUM 19 /* Number of defined types. */
-#define SHT_LOOS 0x60000000 /* Start OS-specific */
-#define SHT_GNU_HASH 0x6ffffff6 /* GNU style symbol hash table. */
+#define SHT_LOOS 0x60000000 /* Start OS-specific. */
+#define SHT_GNU_HASH 0x6ffffff6 /* GNU-style hash table. */
#define SHT_GNU_LIBLIST 0x6ffffff7 /* Prelink library list */
#define SHT_CHECKSUM 0x6ffffff8 /* Checksum for DSO content. */
#define SHT_LOSUNW 0x6ffffffa /* Sun-specific low bound. */
@@ -603,6 +603,7 @@ typedef struct
#define NT_LWPSTATUS 16 /* Contains copy of lwpstatus 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*/
/* Legal values for the note segment descriptor types for object files. */
@@ -692,7 +693,7 @@ typedef struct
#define DT_SYMINENT 0x6ffffdff /* Entry size of syminfo */
#define DT_VALRNGHI 0x6ffffdff
#define DT_VALTAGIDX(tag) (DT_VALRNGHI - (tag)) /* Reverse order! */
-#define DT_VALNUM 11
+#define DT_VALNUM 12
/* DT_* entries which fall between DT_ADDRRNGHI & DT_ADDRRNGLO use the
Dyn.d_un.d_ptr field of the Elf*_Dyn structure.
@@ -700,7 +701,7 @@ typedef struct
If any adjustment is made to the ELF object after it has been
built these entries will need to be adjusted. */
#define DT_ADDRRNGLO 0x6ffffe00
-#define DT_GNU_HASH 0x6ffffef5 /* Address of GNU symbol hash table */
+#define DT_GNU_HASH 0x6ffffef5 /* GNU-style hash table. */
#define DT_TLSDESC_PLT 0x6ffffef6
#define DT_TLSDESC_GOT 0x6ffffef7
#define DT_GNU_CONFLICT 0x6ffffef8 /* Start of conflict section */
@@ -1254,14 +1255,15 @@ typedef struct
#define DT_SPARC_REGISTER 0x70000001
#define DT_SPARC_NUM 2
-/* Bits present in AT_HWCAP, primarily for Sparc32. */
+/* Bits present in AT_HWCAP on SPARC. */
-#define HWCAP_SPARC_FLUSH 1 /* The cpu supports flush insn. */
+#define HWCAP_SPARC_FLUSH 1 /* The CPU supports flush insn. */
#define HWCAP_SPARC_STBAR 2
#define HWCAP_SPARC_SWAP 4
#define HWCAP_SPARC_MULDIV 8
-#define HWCAP_SPARC_V9 16 /* The cpu is v9, so v8plus is ok. */
+#define HWCAP_SPARC_V9 16 /* The CPU is v9, so v8plus is ok. */
#define HWCAP_SPARC_ULTRA3 32
+#define HWCAP_SPARC_BLKINIT 64 /* Sun4v with block-init/load-twin. */
/* MIPS R3000 specific definitions. */
@@ -1510,8 +1512,9 @@ typedef struct
#define R_MIPS_TLS_TPREL64 48 /* TP-relative offset, 64 bit */
#define R_MIPS_TLS_TPREL_HI16 49 /* TP-relative offset, high 16 bits */
#define R_MIPS_TLS_TPREL_LO16 50 /* TP-relative offset, low 16 bits */
+#define R_MIPS_GLOB_DAT 51
/* Keep this the last entry. */
-#define R_MIPS_NUM 51
+#define R_MIPS_NUM 52
/* Legal values for p_type field of Elf32_Phdr. */
diff --git a/src/ChangeLog b/src/ChangeLog
index 2828da94..39b64ff0 100644
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -1,3 +1,19 @@
+2007-04-24 Roland McGrath <roland@redhat.com>
+
+ * elfcmp.c (OPT_HASH_INEXACT): New macro.
+ (hash_inexact): New variable.
+ (options, parse_opt): Add --hash-inexact option to set it.
+ (hash_content_equivalent): New function.
+ (main): Call it for differing SHT_HASH sections under --hash-inexact.
+
+2007-04-23 Roland McGrath <roland@redhat.com>
+
+ * unstrip.c: New file.
+ * Makefile.am (bin_PROGRAMS): Add it.
+ (unstrip_LDADD): New variable.
+
+ * strip.c (options): Allow --output for -o.
+
2007-02-15 Ulrich Drepper <drepper@redhat.com>
* readelf.c: Remove unused code. Add a few consts.
diff --git a/src/Makefile.am b/src/Makefile.am
index 230a8178..6444cd13 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -52,7 +52,7 @@ native_ld = @native_ld@
base_cpu = @base_cpu@
bin_PROGRAMS = readelf nm size strip ld elflint findtextrel addr2line \
- elfcmp objdump ranlib strings ar
+ elfcmp objdump ranlib strings ar unstrip
ld_dsos = libld_elf_i386_pic.a
@@ -123,6 +123,7 @@ ranlib_LDADD = libar.a $(libelf) $(libeu) $(libmudflap)
strings_LDADD = $(libelf) $(libeu) $(libmudflap)
ar_LDADD = libar.a $(libelf) $(libeu) $(libmudflap)
CFLAGS_ar = -DAR=\"$(shell echo ar|sed '$(transform)')\"
+unstrip_LDADD = $(libebl) $(libelf) $(libdw) $(libeu) $(libmudflap) -ldl
ldlex.o: ldscript.c
ldlex_no_Werror = yes
diff --git a/src/elfcmp.c b/src/elfcmp.c
index c5023b9e..445a8ea0 100644
--- a/src/elfcmp.c
+++ b/src/elfcmp.c
@@ -59,13 +59,16 @@ void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
const char *argp_program_bug_address = PACKAGE_BUGREPORT;
/* Values for the parameters which have no short form. */
-#define OPT_GAPS 0x100
+#define OPT_GAPS 0x100
+#define OPT_HASH_INEXACT 0x101
/* Definitions of arguments for argp functions. */
static const struct argp_option options[] =
{
{ NULL, 0, NULL, 0, N_("Control options:"), 0 },
{ "gaps", OPT_GAPS, "ACTION", 0, N_("Control treatment of gaps in loadable segments [ignore|match] (default: ignore)"), 0 },
+ { "hash-inexact", OPT_HASH_INEXACT, NULL, 0,
+ N_("Ignore permutation of buckets in SHT_HASH section"), 0 },
{ "quiet", 'q', NULL, 0, N_("Output nothing; yield exit status only"), 0 },
{ NULL, 0, NULL, 0, N_("Miscellaneous:"), 0 },
@@ -108,6 +111,11 @@ struct region
/* Nonzero if only exit status is wanted. */
static bool quiet;
+/* True iff SHT_HASH treatment should be generous. */
+static bool hash_inexact;
+
+static bool hash_content_equivalent (size_t entsize, Elf_Data *, Elf_Data *);
+
int
main (int argc, char *argv[])
@@ -353,6 +361,12 @@ main (int argc, char *argv[])
&& memcmp (data1->d_buf, data2->d_buf,
data1->d_size) != 0)))
{
+ if (hash_inexact
+ && shdr1->sh_type == SHT_HASH
+ && data1->d_size == data2->d_size
+ && hash_content_equivalent (shdr1->sh_entsize, data1, data2))
+ break;
+
if (! quiet)
{
if (elf_ndxscn (scn1) == elf_ndxscn (scn2))
@@ -360,7 +374,6 @@ main (int argc, char *argv[])
%s %s differ: section [%zu] '%s' content"),
fname1, fname2, elf_ndxscn (scn1), sname1);
else
- if (elf_ndxscn (scn1) == elf_ndxscn (scn2))
error (0, 0, gettext ("\
%s %s differ: section [%zu,%zu] '%s' content"),
fname1, fname2, elf_ndxscn (scn1),
@@ -539,6 +552,10 @@ parse_opt (int key, char *arg,
}
break;
+ case OPT_HASH_INEXACT:
+ hash_inexact = true;
+ break;
+
default:
return ARGP_ERR_UNKNOWN;
}
@@ -625,7 +642,7 @@ search_for_copy_reloc (Ebl *ebl, size_t scnndx, int symndx)
}
-static int
+static int
regioncompare (const void *p1, const void *p2)
{
const struct region *r1 = (const struct region *) p1;
@@ -635,3 +652,95 @@ regioncompare (const void *p1, const void *p2)
return -1;
return 1;
}
+
+
+static int
+compare_Elf32_Word (const void *p1, const void *p2)
+{
+ const Elf32_Word *w1 = p1;
+ const Elf32_Word *w2 = p2;
+ assert (sizeof (int) >= sizeof (*w1));
+ return (int) *w1 - (int) *w2;
+}
+
+static int
+compare_Elf64_Xword (const void *p1, const void *p2)
+{
+ const Elf64_Xword *w1 = p1;
+ const Elf64_Xword *w2 = p2;
+ return *w1 < *w2 ? -1 : *w1 > *w2 ? 1 : 0;
+}
+
+static bool
+hash_content_equivalent (size_t entsize, Elf_Data *data1, Elf_Data *data2)
+{
+#define CHECK_HASH(Hash_Word) \
+ { \
+ const Hash_Word *const hash1 = data1->d_buf; \
+ const Hash_Word *const hash2 = data2->d_buf; \
+ const size_t nbucket = hash1[0]; \
+ const size_t nchain = hash1[1]; \
+ if (data1->d_size != (2 + nbucket + nchain) * sizeof hash1[0] \
+ || hash2[0] != nbucket || hash2[1] != nchain) \
+ return false; \
+ \
+ const Hash_Word *const bucket1 = &hash1[2]; \
+ const Hash_Word *const chain1 = &bucket1[nbucket]; \
+ const Hash_Word *const bucket2 = &hash2[2]; \
+ const Hash_Word *const chain2 = &bucket2[nbucket]; \
+ \
+ bool chain_ok[nchain]; \
+ Hash_Word temp1[nchain - 1]; \
+ Hash_Word temp2[nchain - 1]; \
+ memset (chain_ok, 0, sizeof chain_ok); \
+ for (size_t i = 0; i < nbucket; ++i) \
+ { \
+ if (bucket1[i] >= nchain || bucket2[i] >= nchain) \
+ return false; \
+ \
+ size_t b1 = 0; \
+ for (size_t p = bucket1[i]; p != STN_UNDEF; p = chain1[p]) \
+ if (p >= nchain || b1 >= nchain - 1) \
+ return false; \
+ else \
+ temp1[b1++] = p; \
+ \
+ size_t b2 = 0; \
+ for (size_t p = bucket2[i]; p != STN_UNDEF; p = chain2[p]) \
+ if (p >= nchain || b2 >= nchain - 1) \
+ return false; \
+ else \
+ temp2[b2++] = p; \
+ \
+ if (b1 != b2) \
+ return false; \
+ \
+ qsort (temp1, b1, sizeof temp1[0], compare_##Hash_Word); \
+ qsort (temp2, b2, sizeof temp2[0], compare_##Hash_Word); \
+ \
+ for (b1 = 0; b1 < b2; ++b1) \
+ if (temp1[b1] != temp2[b1]) \
+ return false; \
+ else \
+ chain_ok[temp1[b1]] = true; \
+ } \
+ \
+ for (size_t i = 0; i < nchain; ++i) \
+ if (!chain_ok[i] && chain1[i] != chain2[i]) \
+ return false; \
+ \
+ return true; \
+ }
+
+ switch (entsize)
+ {
+ case 4:
+ CHECK_HASH (Elf32_Word);
+ break;
+ case 8:
+ CHECK_HASH (Elf64_Xword);
+ break;
+ }
+
+ return false;
+}
diff --git a/src/strip.c b/src/strip.c
index 511321ff..95eded65 100644
--- a/src/strip.c
+++ b/src/strip.c
@@ -70,7 +70,7 @@ const char *argp_program_bug_address = PACKAGE_BUGREPORT;
static const struct argp_option options[] =
{
{ NULL, 0, NULL, 0, N_("Output selection:"), 0 },
- { NULL, 'o', "FILE", 0, N_("Place stripped output into FILE"), 0 },
+ { "output", 'o', "FILE", 0, N_("Place stripped output into FILE"), 0 },
{ NULL, 'f', "FILE", 0, N_("Extract the removed sections into FILE"), 0 },
{ NULL, 'F', "FILE", 0, N_("Embed name FILE instead of -f argument"), 0 },
diff --git a/src/unstrip.c b/src/unstrip.c
new file mode 100644
index 00000000..25ce1f37
--- /dev/null
+++ b/src/unstrip.c
@@ -0,0 +1,1768 @@
+/* Combine stripped files with separate symbols and debug information.
+ Copyright (C) 2007 Red Hat, Inc.
+ This file is part of Red Hat elfutils.
+ Written by Roland McGrath <roland@redhat.com>, 2007.
+
+ 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>. */
+
+/* TODO:
+
+ * SHX_XINDEX
+
+ * prelink vs .debug_* linked addresses
+
+ * merge many inputs? -> ET_EXEC with union phdrs + new phdrs for ET_REL mods
+ ** with applied relocs to ET_REL mods, use data modified by dwfl
+ *** still must apply relocs to SHF_ALLOC
+ ** useless unless merge all symtabs and dwarf sections
+
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <argp.h>
+#include <assert.h>
+#include <errno.h>
+#include <error.h>
+#include <fcntl.h>
+#include <fnmatch.h>
+#include <libintl.h>
+#include <locale.h>
+#include <mcheck.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdio_ext.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <gelf.h>
+#include <libebl.h>
+#include <libdwfl.h>
+#include "system.h"
+
+#ifndef _
+# define _(str) gettext (str)
+#endif
+
+/* Name and version of program. */
+static void print_version (FILE *stream, struct argp_state *state);
+void (*argp_program_version_hook) (FILE *, struct argp_state *)
+ = print_version;
+
+/* Bug report address. */
+const char *argp_program_bug_address = PACKAGE_BUGREPORT;
+
+/* Definitions of arguments for argp functions. */
+static const struct argp_option options[] =
+{
+ /* Group 2 will follow group 1 from dwfl_standard_argp. */
+ { "match-file-names", 'f', NULL, 0,
+ N_("Match MODULE against file names, not module names"), 2 },
+ { "ignore-missing", 'i', NULL, 0, N_("Silently skip unfindable files"), 0 },
+
+ { NULL, 0, NULL, 0, N_("Output options:"), 0 },
+ { "output", 'o', "FILE", 0, N_("Place output into FILE"), 0 },
+ { "output-directory", 'd', "DIRECTORY",
+ 0, N_("Create multiple output files under DIRECTORY"), 0 },
+ { "module-names", 'm', NULL, 0, N_("Use module rather than file names"), 0 },
+ { "all", 'a', NULL, 0,
+ N_("Create output for modules that have no separate debug information"),
+ 0 },
+ { NULL, 0, NULL, 0, NULL, 0 }
+};
+
+struct arg_info
+{
+ const char *output_file;
+ const char *output_dir;
+ Dwfl *dwfl;
+ char **args;
+ bool all;
+ bool ignore;
+ bool modnames;
+ bool match_files;
+};
+
+/* Handle program arguments. */
+static error_t
+parse_opt (int key, char *arg, struct argp_state *state)
+{
+ struct arg_info *info = state->input;
+
+ switch (key)
+ {
+ case ARGP_KEY_INIT:
+ state->child_inputs[0] = &info->dwfl;
+ break;
+
+ case 'o':
+ if (info->output_file != NULL)
+ {
+ argp_error (state, _("-o option specified twice"));
+ return EINVAL;
+ }
+ info->output_file = arg;
+ break;
+
+ case 'd':
+ if (info->output_dir != NULL)
+ {
+ argp_error (state, _("-d option specified twice"));
+ return EINVAL;
+ }
+ info->output_dir = arg;
+ break;
+
+ case 'm':
+ info->modnames = true;
+ break;
+ case 'f':
+ info->match_files = true;
+ break;
+ case 'a':
+ info->all = true;
+ break;
+ case 'i':
+ info->ignore = true;
+ break;
+
+ case ARGP_KEY_ARGS:
+ case ARGP_KEY_NO_ARGS:
+ /* We "consume" all the arguments here. */
+ info->args = &state->argv[state->next];
+
+ if (info->output_file != NULL && info->output_dir != NULL)
+ {
+ argp_error (state, _("only one of -o or -d allowed"));
+ return EINVAL;
+ }
+
+ if (info->output_dir != NULL)
+ {
+ struct stat64 st;
+ error_t fail = 0;
+ if (stat64 (info->output_dir, &st) < 0)
+ fail = errno;
+ else if (!S_ISDIR (st.st_mode))
+ fail = ENOTDIR;
+ if (fail)
+ {
+ argp_failure (state, EXIT_FAILURE, fail,
+ _("output directory '%s'"), info->output_dir);
+ return fail;
+ }
+ }
+
+ if (info->dwfl == NULL)
+ {
+ if (state->next + 2 != state->argc)
+ {
+ argp_error (state, _("exactly two file arguments are required"));
+ return EINVAL;
+ }
+
+ if (info->ignore || info->all || info->modnames)
+ {
+ argp_error (state, _("\
+-m, -a, and -i options not allowed with explicit files"));
+ return EINVAL;
+ }
+
+ /* Bail out immediately to prevent dwfl_standard_argp's parser
+ from defaulting to "-e a.out". */
+ return ENOSYS;
+ }
+ else if (info->output_file == NULL && info->output_dir == NULL)
+ {
+ argp_error (state,
+ _("-o or -d is required when using implicit files"));
+ return EINVAL;
+ }
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+/* Print the version information. */
+static void
+print_version (FILE *stream, struct argp_state *state __attribute__ ((unused)))
+{
+ fprintf (stream, "unstrip (%s) %s\n", PACKAGE_NAME, VERSION);
+ fprintf (stream, _("\
+Copyright (C) %s Red Hat, Inc.\n\
+This is free software; see the source for copying conditions. There is NO\n\
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
+"), "2007");
+ fprintf (stream, gettext ("Written by %s.\n"), "Roland McGrath");
+}
+
+#define ELF_CHECK(call, msg) \
+ do \
+ { \
+ if (!(call)) \
+ error (EXIT_FAILURE, 0, msg, elf_errmsg (-1)); \
+ } while (0)
+
+/* Copy INELF to newly-created OUTELF, exit via error for any problems. */
+static void
+copy_elf (Elf *outelf, Elf *inelf)
+{
+ ELF_CHECK (gelf_newehdr (outelf, gelf_getclass (inelf)),
+ _("cannot create ELF header: %s"));
+
+ GElf_Ehdr ehdr_mem;
+ GElf_Ehdr *ehdr = gelf_getehdr (inelf, &ehdr_mem);
+ ELF_CHECK (gelf_update_ehdr (outelf, ehdr),
+ _("cannot copy ELF header: %s"));
+
+ if (ehdr->e_phnum > 0)
+ {
+ ELF_CHECK (gelf_newphdr (outelf, ehdr->e_phnum),
+ _("cannot create program headers: %s"));
+
+ GElf_Phdr phdr_mem;
+ for (uint_fast16_t i = 0; i < ehdr->e_phnum; ++i)
+ ELF_CHECK (gelf_update_phdr (outelf, i,
+ gelf_getphdr (inelf, i, &phdr_mem)),
+ _("cannot copy program header: %s"));
+ }
+
+ Elf_Scn *scn = NULL;
+ while ((scn = elf_nextscn (inelf, scn)) != NULL)
+ {
+ Elf_Scn *newscn = elf_newscn (outelf);
+
+ GElf_Shdr shdr_mem;
+ ELF_CHECK (gelf_update_shdr (newscn, gelf_getshdr (scn, &shdr_mem)),
+ _("cannot copy section header: %s"));
+
+ Elf_Data *data = elf_getdata (scn, NULL);
+ ELF_CHECK (data != NULL, _("cannot get section data: %s"));
+ Elf_Data *newdata = elf_newdata (newscn);
+ ELF_CHECK (newdata != NULL, _("cannot copy section data: %s"));
+ *newdata = *data;
+ elf_flagdata (newdata, ELF_C_SET, ELF_F_DIRTY);
+ }
+}
+
+
+/* The binutils linker leaves gratuitous section symbols in .symtab
+ that strip has to remove. Older linkers likewise include a
+ symbol for every section, even unallocated ones, in .dynsym.
+ Because of this, the related sections can shrink in the stripped
+ file from their original size. Older versions of strip do not
+ adjust the sh_size field in the debuginfo file's SHT_NOBITS
+ version of the section header, so it can appear larger. */
+static bool
+section_can_shrink (const GElf_Shdr *shdr)
+{
+ switch (shdr->sh_type)
+ {
+ case SHT_SYMTAB:
+ case SHT_DYNSYM:
+ case SHT_HASH:
+ case SHT_GNU_versym:
+ return true;
+ }
+ return false;
+}
+
+/* See if this symbol table has a leading section symbol for every single
+ section, in order. The binutils linker produces this. */
+static size_t
+symtab_count_leading_section_symbols (Elf_Scn *scn, size_t shnum)
+{
+ Elf_Data *data = elf_getdata (scn, NULL);
+ Elf_Data *shndxdata = NULL; /* XXX */
+
+ for (size_t i = 1; i < shnum; ++i)
+ {
+ GElf_Sym sym_mem;
+ GElf_Word shndx = SHN_UNDEF;
+ GElf_Sym *sym = gelf_getsymshndx (data, shndxdata, i, &sym_mem, &shndx);
+ ELF_CHECK (sym != NULL, _("cannot get symbol table entry: %s"));
+ if (sym->st_shndx != SHN_XINDEX)
+ shndx = sym->st_shndx;
+
+ if (shndx != i || GELF_ST_TYPE (sym->st_info) != STT_SECTION)
+ return i;
+ }
+
+ return shnum;
+}
+
+/* We expanded the output section, so update its header. */
+static void
+update_sh_size (Elf_Scn *outscn, const Elf_Data *data)
+{
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *newshdr = gelf_getshdr (outscn, &shdr_mem);
+ ELF_CHECK (newshdr != NULL, _("cannot get section header: %s"));
+
+ newshdr->sh_size = data->d_size;
+
+ ELF_CHECK (gelf_update_shdr (outscn, newshdr),
+ _("cannot update section header: %s"));
+}
+
+/* Update relocation sections using the symbol table. */
+static void
+adjust_relocs (Elf_Scn *outscn, Elf_Scn *inscn, const GElf_Shdr *shdr,
+ size_t map[], const GElf_Shdr *symshdr)
+{
+ Elf_Data *data = elf_getdata (outscn, NULL);
+
+ inline void adjust_reloc (GElf_Xword *info)
+ {
+ size_t ndx = GELF_R_SYM (*info);
+ if (ndx != STN_UNDEF)
+ *info = GELF_R_INFO (map[ndx - 1], GELF_R_TYPE (*info));
+ }
+
+ switch (shdr->sh_type)
+ {
+ case SHT_REL:
+ for (size_t i = 0; i < shdr->sh_size / shdr->sh_entsize; ++i)
+ {
+ GElf_Rel rel_mem;
+ GElf_Rel *rel = gelf_getrel (data, i, &rel_mem);
+ adjust_reloc (&rel->r_info);
+ ELF_CHECK (gelf_update_rel (data, i, rel),
+ _("cannot update relocation: %s"));
+ }
+ break;
+
+ case SHT_RELA:
+ for (size_t i = 0; i < shdr->sh_size / shdr->sh_entsize; ++i)
+ {
+ GElf_Rela rela_mem;
+ GElf_Rela *rela = gelf_getrela (data, i, &rela_mem);
+ adjust_reloc (&rela->r_info);
+ ELF_CHECK (gelf_update_rela (data, i, rela),
+ _("cannot update relocation: %s"));
+ }
+ break;
+
+ case SHT_GROUP:
+ {
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *newshdr = gelf_getshdr (outscn, &shdr_mem);
+ ELF_CHECK (newshdr != NULL, _("cannot get section header: %s"));
+ if (newshdr->sh_info != STN_UNDEF)
+ {
+ newshdr->sh_info = map[newshdr->sh_info - 1];
+ ELF_CHECK (gelf_update_shdr (outscn, newshdr),
+ _("cannot update section header: %s"));
+ }
+ break;
+ }
+
+ case SHT_HASH:
+ /* We must expand the table and rejigger its contents. */
+ {
+ const size_t nsym = symshdr->sh_size / symshdr->sh_entsize;
+ const size_t onent = shdr->sh_size / shdr->sh_entsize;
+ assert (data->d_size == shdr->sh_size);
+
+#define CONVERT_HASH(Hash_Word) \
+ { \
+ const Hash_Word *const old_hash = data->d_buf; \
+ const size_t nbucket = old_hash[0]; \
+ const size_t nchain = old_hash[1]; \
+ const Hash_Word *const old_bucket = &old_hash[2]; \
+ const Hash_Word *const old_chain = &old_bucket[nbucket]; \
+ assert (onent == 2 + nbucket + nchain); \
+ \
+ const size_t nent = 2 + nbucket + nsym; \
+ Hash_Word *const new_hash = xcalloc (nent, sizeof new_hash[0]); \
+ Hash_Word *const new_bucket = &new_hash[2]; \
+ Hash_Word *const new_chain = &new_bucket[nbucket]; \
+ \
+ new_hash[0] = nbucket; \
+ new_hash[1] = nsym; \
+ for (size_t i = 0; i < nbucket; ++i) \
+ if (old_bucket[i] != STN_UNDEF) \
+ new_bucket[i] = map[old_bucket[i] - 1]; \
+ \
+ for (size_t i = 1; i < nchain; ++i) \
+ if (old_chain[i] != STN_UNDEF) \
+ new_chain[map[i - 1]] = map[old_chain[i] - 1]; \
+ \
+ data->d_buf = new_hash; \
+ data->d_size = nent * sizeof new_hash[0]; \
+ }
+
+ switch (shdr->sh_entsize)
+ {
+ case 4:
+ CONVERT_HASH (Elf32_Word);
+ break;
+ case 8:
+ CONVERT_HASH (Elf64_Xword);
+ break;
+ default:
+ abort ();
+ }
+
+ elf_flagdata (data, ELF_C_SET, ELF_F_DIRTY);
+ update_sh_size (outscn, data);
+
+#undef CONVERT_HASH
+ }
+ break;
+
+ case SHT_GNU_versym:
+ /* We must expand the table and move its elements around. */
+ {
+ const size_t nent = symshdr->sh_size / symshdr->sh_entsize;
+ const size_t onent = shdr->sh_size / shdr->sh_entsize;
+ assert (nent >= onent);
+
+ /* We don't bother using gelf_update_versym because there is
+ really no conversion to be done. */
+ assert (sizeof (Elf32_Versym) == sizeof (GElf_Versym));
+ assert (sizeof (Elf64_Versym) == sizeof (GElf_Versym));
+ GElf_Versym *versym = xcalloc (nent, sizeof versym[0]);
+
+ for (size_t i = 1; i < onent; ++i)
+ {
+ GElf_Versym *v = gelf_getversym (data, i, &versym[map[i - 1]]);
+ ELF_CHECK (v != NULL, _("cannot get symbol version: %s"));
+ }
+
+ data->d_buf = versym;
+ data->d_size = nent * shdr->sh_entsize;
+ elf_flagdata (data, ELF_C_SET, ELF_F_DIRTY);
+ update_sh_size (outscn, data);
+ }
+ break;
+
+ default:
+ error (EXIT_FAILURE, 0,
+ _("unexpected section type in [%Zu] with sh_link to symtab"),
+ elf_ndxscn (inscn));
+ }
+}
+
+/* Adjust all the relocation sections in the file. */
+static void
+adjust_all_relocs (Elf *elf, Elf_Scn *symtab, const GElf_Shdr *symshdr,
+ size_t map[])
+{
+ size_t new_sh_link = elf_ndxscn (symtab);
+ Elf_Scn *scn = NULL;
+ while ((scn = elf_nextscn (elf, scn)) != NULL)
+ if (scn != symtab)
+ {
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+ ELF_CHECK (shdr != NULL, _("cannot get section header: %s"));
+ if (shdr->sh_type != SHT_NOBITS && shdr->sh_link == new_sh_link)
+ adjust_relocs (scn, scn, shdr, map, symshdr);
+ }
+}
+
+/* The original file probably had section symbols for all of its
+ sections, even the unallocated ones. To match it as closely as
+ possible, to add in section symbols for the added sections. */
+static Elf_Data *
+add_new_section_symbols (Elf_Scn *old_symscn, size_t old_shnum,
+ Elf *elf, Elf_Scn *symscn, size_t shnum)
+{
+ const size_t added = shnum - old_shnum;
+
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (symscn, &shdr_mem);
+ ELF_CHECK (shdr != NULL, _("cannot get section header: %s"));
+
+ const size_t nsym = shdr->sh_size / shdr->sh_entsize;
+ size_t symndx_map[nsym - 1];
+
+ shdr->sh_info += added;
+ shdr->sh_size += added * shdr->sh_entsize;
+
+ ELF_CHECK (gelf_update_shdr (symscn, shdr),
+ _("cannot update section header: %s"));
+
+ Elf_Data *symdata = elf_getdata (symscn, NULL);
+ Elf_Data *shndxdata = NULL; /* XXX */
+
+ symdata->d_size = shdr->sh_size;
+ symdata->d_buf = xmalloc (symdata->d_size);
+
+ /* Copy the existing section symbols. */
+ Elf_Data *old_symdata = elf_getdata (old_symscn, NULL);
+ for (size_t i = 0; i < old_shnum; ++i)
+ {
+ GElf_Sym sym_mem;
+ GElf_Word shndx = SHN_UNDEF;
+ GElf_Sym *sym = gelf_getsymshndx (old_symdata, shndxdata,
+ i, &sym_mem, &shndx);
+ ELF_CHECK (gelf_update_symshndx (symdata, shndxdata, i,
+ sym, shndx),
+ _("cannot update symbol table: %s"));
+
+ if (i > 0)
+ symndx_map[i - 1] = i;
+ }
+
+ /* Add in the new section symbols. */
+ for (size_t i = old_shnum; i < shnum; ++i)
+ {
+ GElf_Sym sym =
+ {
+ .st_info = GELF_ST_INFO (STB_LOCAL, STT_SECTION),
+ .st_shndx = i < SHN_LORESERVE ? i : SHN_XINDEX
+ };
+ GElf_Word shndx = i < SHN_LORESERVE ? SHN_UNDEF : i;
+ ELF_CHECK (gelf_update_symshndx (symdata, shndxdata, i,
+ &sym, shndx),
+ _("cannot update symbol table: %s"));
+ }
+
+ /* Now copy the rest of the existing symbols. */
+ for (size_t i = old_shnum; i < nsym; ++i)
+ {
+ GElf_Sym sym_mem;
+ GElf_Word shndx = SHN_UNDEF;
+ GElf_Sym *sym = gelf_getsymshndx (old_symdata, shndxdata,
+ i, &sym_mem, &shndx);
+ ELF_CHECK (gelf_update_symshndx (symdata, shndxdata,
+ i + added, sym, shndx),
+ _("cannot update symbol table: %s"));
+
+ symndx_map[i - 1] = i + added;
+ }
+
+ /* Adjust any relocations referring to the old symbol table. */
+ adjust_all_relocs (elf, symscn, shdr, symndx_map);
+
+ return symdata;
+}
+
+static Elf_Data *
+check_symtab_section_symbols (Elf *elf, Elf_Scn *scn,
+ size_t shnum, size_t shstrndx,
+ Elf_Scn *oscn, size_t oshnum, size_t oshstrndx,
+ size_t debuglink)
+{
+ size_t n = symtab_count_leading_section_symbols (oscn, oshnum);
+
+ if (n == oshnum)
+ return add_new_section_symbols (oscn, n, elf, scn, shnum);
+
+ if (n == oshstrndx || (n == debuglink && n == oshstrndx - 1))
+ return add_new_section_symbols (oscn, n, elf, scn, shstrndx);
+
+ return NULL;
+}
+
+struct section
+{
+ Elf_Scn *scn;
+ const char *name;
+ Elf_Scn *outscn;
+ struct Ebl_Strent *strent;
+ GElf_Shdr shdr;
+};
+
+static int
+compare_alloc_sections (const struct section *s1, const struct section *s2)
+{
+ /* Sort by address. */
+ if (s1->shdr.sh_addr < s2->shdr.sh_addr)
+ return -1;
+ if (s1->shdr.sh_addr > s2->shdr.sh_addr)
+ return 1;
+
+ return 0;
+}
+
+static int
+compare_unalloc_sections (const GElf_Shdr *shdr1, const GElf_Shdr *shdr2,
+ const char *name1, const char *name2)
+{
+ /* Sort by sh_flags as an arbitrary ordering. */
+ if (shdr1->sh_flags < shdr2->sh_flags)
+ return -1;
+ if (shdr1->sh_flags > shdr2->sh_flags)
+ return 1;
+
+ /* Sort by name as last resort. */
+ return strcmp (name1, name2);
+}
+
+static int
+compare_sections (const void *a, const void *b)
+{
+ const struct section *s1 = a;
+ const struct section *s2 = b;
+
+ /* Sort all non-allocated sections last. */
+ if ((s1->shdr.sh_flags ^ s2->shdr.sh_flags) & SHF_ALLOC)
+ return (s1->shdr.sh_flags & SHF_ALLOC) ? -1 : 1;
+
+ return ((s1->shdr.sh_flags & SHF_ALLOC)
+ ? compare_alloc_sections (s1, s2)
+ : compare_unalloc_sections (&s1->shdr, &s2->shdr,
+ s1->name, s2->name));
+}
+
+
+struct symbol
+{
+ size_t *map;
+
+ union
+ {
+ 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;
+ };
+};
+
+/* Collect input symbols into our internal form. */
+static void
+collect_symbols (Elf_Scn *symscn, Elf_Scn *strscn,
+ const size_t nent, const GElf_Addr bias,
+ const size_t scnmap[], struct symbol *table, size_t *map)
+{
+ Elf_Data *symdata = elf_getdata (symscn, NULL);
+ Elf_Data *strdata = elf_getdata (strscn, NULL);
+ Elf_Data *shndxdata = NULL; /* XXX */
+
+ for (size_t i = 1; i < nent; ++i)
+ {
+ GElf_Sym sym_mem;
+ GElf_Word shndx = SHN_UNDEF;
+ GElf_Sym *sym = gelf_getsymshndx (symdata, shndxdata, i,
+ &sym_mem, &shndx);
+ ELF_CHECK (sym != NULL, _("cannot get symbol table entry: %s"));
+ if (sym->st_shndx != SHN_XINDEX)
+ shndx = sym->st_shndx;
+
+ if (scnmap != NULL && shndx != SHN_UNDEF && shndx < SHN_LORESERVE)
+ shndx = scnmap[shndx - 1];
+
+ if (sym->st_name >= strdata->d_size)
+ error (EXIT_FAILURE, 0,
+ _("invalid string offset in symbol [%Zu]"), i);
+
+ struct symbol *s = &table[i - 1];
+ s->map = &map[i - 1];
+ s->name = strdata->d_buf + sym->st_name;
+ s->value = sym->st_value + bias;
+ s->size = sym->st_size;
+ s->shndx = shndx;
+ s->info.info = sym->st_info;
+ s->info.other = sym->st_other;
+ }
+}
+
+
+#define CMP(value) \
+ if (s1->value < s2->value) \
+ return -1; \
+ if (s1->value > s2->value) \
+ return 1
+
+/* Compare symbols with a consistent ordering,
+ but one only meaningful for equality. */
+static int
+compare_symbols (const void *a, const void *b)
+{
+ const struct symbol *s1 = a;
+ const struct symbol *s2 = b;
+
+ CMP (value);
+ CMP (size);
+ CMP (shndx);
+
+ return (s1->compare - s2->compare) ?: strcmp (s1->name, s2->name);
+}
+
+/* Compare symbols for output order after slots have been assigned. */
+static int
+compare_symbols_output (const void *a, const void *b)
+{
+ const struct symbol *s1 = a;
+ const struct symbol *s2 = b;
+ int cmp;
+
+ /* Sort discarded symbols last. */
+ cmp = (*s1->map == 0) - (*s2->map == 0);
+
+ if (cmp == 0)
+ /* Local symbols must come first. */
+ cmp = ((GELF_ST_BIND (s2->info.info) == STB_LOCAL)
+ - (GELF_ST_BIND (s1->info.info) == STB_LOCAL));
+
+ if (cmp == 0)
+ /* binutils always puts section symbols first. */
+ cmp = ((GELF_ST_TYPE (s2->info.info) == STT_SECTION)
+ - (GELF_ST_TYPE (s1->info.info) == STT_SECTION));
+
+ if (cmp == 0)
+ {
+ if (GELF_ST_TYPE (s1->info.info) == STT_SECTION)
+ {
+ /* binutils always puts section symbols in section index order. */
+ CMP (shndx);
+ else
+ assert (s1 == s2);
+ }
+
+ /* Nothing really matters, so preserve the original order. */
+ CMP (map);
+ else
+ assert (s1 == s2);
+ }
+
+ return cmp;
+}
+
+#undef CMP
+
+/* Fill in any SHT_NOBITS sections in UNSTRIPPED by
+ copying their contents and sh_type from STRIPPED. */
+static void
+copy_elided_sections (Elf *unstripped, Elf *stripped, uint_fast16_t e_type,
+ uint_fast16_t phnum, GElf_Addr bias)
+{
+ size_t unstripped_shstrndx;
+ ELF_CHECK (elf_getshstrndx (unstripped, &unstripped_shstrndx) == 0,
+ _("cannot get section header string table section index: %s"));
+
+ size_t stripped_shstrndx;
+ ELF_CHECK (elf_getshstrndx (stripped, &stripped_shstrndx) == 0,
+ _("cannot get section header string table section index: %s"));
+
+ size_t unstripped_shnum;
+ ELF_CHECK (elf_getshnum (unstripped, &unstripped_shnum) == 0,
+ _("cannot get section count: %s"));
+
+ size_t stripped_shnum;
+ ELF_CHECK (elf_getshnum (stripped, &stripped_shnum) == 0,
+ _("cannot get section count: %s"));
+
+ /* Cache the stripped file's section details. */
+ struct section sections[stripped_shnum - 1];
+ Elf_Scn *scn = NULL;
+ while ((scn = elf_nextscn (stripped, scn)) != NULL)
+ {
+ size_t i = elf_ndxscn (scn) - 1;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &sections[i].shdr);
+ ELF_CHECK (shdr != NULL, _("cannot get section header: %s"));
+ sections[i].name = elf_strptr (stripped, stripped_shstrndx,
+ shdr->sh_name);
+ if (sections[i].name == NULL)
+ error (EXIT_FAILURE, 0, _("cannot read section [%Zu] name: %s"),
+ elf_ndxscn (scn), elf_errmsg (-1));
+ sections[i].scn = scn;
+ sections[i].outscn = NULL;
+ sections[i].strent = NULL;
+ }
+
+ const struct section *stripped_symtab = NULL;
+
+ /* Sort the sections, allocated by address and others after. */
+ qsort (sections, stripped_shnum - 1, sizeof sections[0], compare_sections);
+ size_t nalloc = stripped_shnum - 1;
+ while (nalloc > 0 && !(sections[nalloc - 1].shdr.sh_flags & SHF_ALLOC))
+ {
+ --nalloc;
+ if (sections[nalloc].shdr.sh_type == SHT_SYMTAB)
+ stripped_symtab = &sections[nalloc];
+ }
+
+ /* Locate a matching allocated section in SECTIONS. */
+ inline struct section *find_alloc_section (const GElf_Shdr *shdr,
+ const char *name)
+ {
+ const GElf_Addr addr = shdr->sh_addr + bias;
+ size_t l = 0, u = nalloc;
+ while (l < u)
+ {
+ size_t i = (l + u) / 2;
+ if (addr < sections[i].shdr.sh_addr)
+ u = i;
+ else if (addr > sections[i].shdr.sh_addr)
+ l = i + 1;
+ else
+ {
+ /* We've found allocated sections with this address.
+ Find one with matching size, flags, and name. */
+ while (i > 0 && sections[i - 1].shdr.sh_addr == addr)
+ --i;
+ for (; i < nalloc && sections[i].shdr.sh_addr == addr;
+ ++i)
+ if (sections[i].shdr.sh_flags == shdr->sh_flags
+ && (sections[i].shdr.sh_size == shdr->sh_size
+ || (sections[i].shdr.sh_size < shdr->sh_size
+ && section_can_shrink (&sections[i].shdr)))
+ && !strcmp (sections[i].name, name))
+ return &sections[i];
+ break;
+ }
+ }
+ return NULL;
+ }
+
+ /* Locate a matching unallocated section in SECTIONS. */
+ inline struct section *find_unalloc_section (const GElf_Shdr *shdr,
+ const char *name)
+ {
+ size_t l = nalloc, u = stripped_shnum - 1;
+ while (l < u)
+ {
+ size_t i = (l + u) / 2;
+ struct section *sec = &sections[i];
+ int cmp = compare_unalloc_sections (shdr, &sec->shdr,
+ name, sec->name);
+ if (cmp < 0)
+ u = i;
+ else if (cmp > 0)
+ l = i + 1;
+ else
+ return sec;
+ }
+ return NULL;
+ }
+
+ Elf_Data *shstrtab = elf_getdata (elf_getscn (unstripped,
+ unstripped_shstrndx), NULL);
+ ELF_CHECK (shstrtab != NULL,
+ _("cannot read section header string table: %s"));
+ inline const char *unstripped_section_name (Elf_Scn *sec,
+ const GElf_Shdr *shdr)
+ {
+ if (shdr->sh_name >= shstrtab->d_size)
+ error (EXIT_FAILURE, 0, _("cannot read section [%Zu] name: %s"),
+ elf_ndxscn (sec), elf_errmsg (-1));
+ return shstrtab->d_buf + shdr->sh_name;
+ }
+
+ /* Match each debuginfo section with its corresponding stripped section. */
+ Elf_Scn *unstripped_symtab = NULL;
+ size_t unstripped_strtab_ndx = SHN_UNDEF;
+ scn = NULL;
+ while ((scn = elf_nextscn (unstripped, scn)) != NULL)
+ {
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+ ELF_CHECK (shdr != NULL, _("cannot get section header: %s"));
+
+ /* Anything not already SHT_NOBITS is fine as it stands. */
+ if (shdr->sh_type != SHT_NOBITS)
+ {
+ if (shdr->sh_type == SHT_SYMTAB)
+ {
+ unstripped_symtab = scn;
+ unstripped_strtab_ndx = shdr->sh_link;
+ }
+ continue;
+ }
+
+ const char *name = unstripped_section_name (scn, shdr);
+
+ /* Look for the section that matches. */
+ struct section *sec = ((shdr->sh_flags & SHF_ALLOC)
+ ? find_alloc_section (shdr, name)
+ : find_unalloc_section (shdr, name));
+ if (sec == NULL)
+ error (EXIT_FAILURE, 0,
+ _("cannot find matching section for [%Zu] '%s'"),
+ elf_ndxscn (scn), name);
+
+ sec->outscn = scn;
+ }
+
+ /* Make sure each main file section has a place to go. */
+ const struct section *stripped_dynsym = NULL;
+ size_t debuglink = SHN_UNDEF;
+ size_t ndx_section[stripped_shnum - 1];
+ struct Ebl_Strtab *strtab = NULL;
+ for (struct section *sec = sections;
+ sec < &sections[stripped_shnum - 1];
+ ++sec)
+ {
+ size_t secndx = elf_ndxscn (sec->scn);
+
+ if (sec->outscn == NULL)
+ {
+ /* We didn't find any corresponding section for this. */
+
+ if (secndx == stripped_shstrndx)
+ {
+ /* We only need one .shstrtab. */
+ ndx_section[secndx - 1] = unstripped_shstrndx;
+ continue;
+ }
+
+ if (unstripped_symtab != NULL && sec == stripped_symtab)
+ {
+ /* We don't need a second symbol table. */
+ ndx_section[secndx - 1] = elf_ndxscn (unstripped_symtab);
+ continue;
+ }
+
+ if (unstripped_symtab != NULL && stripped_symtab != NULL
+ && secndx == stripped_symtab->shdr.sh_link)
+ {
+ /* ... nor its string table. */
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (unstripped_symtab, &shdr_mem);
+ ELF_CHECK (shdr != NULL, _("cannot get section header: %s"));
+ ndx_section[secndx - 1] = shdr->sh_link;
+ continue;
+ }
+
+ if (!(sec->shdr.sh_flags & SHF_ALLOC)
+ && !strcmp (sec->name, ".gnu_debuglink"))
+ {
+ /* This was created by stripping. We don't want it. */
+ debuglink = secndx;
+ continue;
+ }
+
+ sec->outscn = elf_newscn (unstripped);
+ Elf_Data *newdata = elf_newdata (sec->outscn);
+ ELF_CHECK (newdata != NULL && gelf_update_shdr (sec->outscn,
+ &sec->shdr),
+ _("cannot add new section: %s"));
+
+ if (strtab == NULL)
+ strtab = ebl_strtabinit (true);
+ sec->strent = ebl_strtabadd (strtab, sec->name, 0);
+ ELF_CHECK (sec->strent != NULL,
+ _("cannot add section name to string table: %s"));
+ }
+
+ /* Cache the mapping of original section indices to output sections. */
+ ndx_section[secndx - 1] = elf_ndxscn (sec->outscn);
+ }
+
+ Elf_Data *strtab_data = NULL;
+ if (strtab != NULL)
+ {
+ /* We added some sections, so we need a new shstrtab. */
+
+ struct Ebl_Strent *unstripped_strent[unstripped_shnum - 1];
+ memset (unstripped_strent, 0, sizeof unstripped_strent);
+ for (struct section *sec = sections;
+ sec < &sections[stripped_shnum - 1];
+ ++sec)
+ if (sec->outscn != NULL)
+ {
+ if (sec->strent == NULL)
+ {
+ sec->strent = ebl_strtabadd (strtab, sec->name, 0);
+ ELF_CHECK (sec->strent != NULL,
+ _("cannot add section name to string table: %s"));
+ }
+ unstripped_strent[elf_ndxscn (sec->outscn) - 1] = sec->strent;
+ }
+
+ /* Add names of sections we aren't touching. */
+ for (size_t i = 0; i < unstripped_shnum - 1; ++i)
+ if (unstripped_strent[i] == NULL)
+ {
+ scn = elf_getscn (unstripped, i + 1);
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+ const char *name = unstripped_section_name (scn, shdr);
+ unstripped_strent[i] = ebl_strtabadd (strtab, name, 0);
+ ELF_CHECK (unstripped_strent[i] != NULL,
+ _("cannot add section name to string table: %s"));
+ }
+ else
+ unstripped_strent[i] = NULL;
+
+ /* Now finalize the string table so we can get offsets. */
+ strtab_data = elf_getdata (elf_getscn (unstripped, unstripped_shstrndx),
+ NULL);
+ ELF_CHECK (elf_flagdata (strtab_data, ELF_C_SET, ELF_F_DIRTY),
+ _("cannot update section header string table data: %s"));
+ ebl_strtabfinalize (strtab, strtab_data);
+
+ /* Update the sh_name fields of sections we aren't modifying later. */
+ for (size_t i = 0; i < unstripped_shnum - 1; ++i)
+ if (unstripped_strent[i] != NULL)
+ {
+ scn = elf_getscn (unstripped, i + 1);
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+ shdr->sh_name = ebl_strtaboffset (unstripped_strent[i]);
+ if (i + 1 == unstripped_shstrndx)
+ shdr->sh_size = strtab_data->d_size;
+ ELF_CHECK (gelf_update_shdr (scn, shdr),
+ _("cannot update section header: %s"));
+ }
+ }
+
+ /* Get the updated section count. */
+ ELF_CHECK (elf_getshnum (unstripped, &unstripped_shnum) == 0,
+ _("cannot get section count: %s"));
+
+ bool placed[unstripped_shnum - 1];
+ memset (placed, 0, sizeof placed);
+
+ /* Now update the output sections and copy in their data. */
+ GElf_Off offset = 0;
+ for (const struct section *sec = sections;
+ sec < &sections[stripped_shnum - 1];
+ ++sec)
+ if (sec->outscn != NULL)
+ {
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (sec->outscn, &shdr_mem);
+ ELF_CHECK (shdr != NULL, _("cannot get section header: %s"));
+
+ shdr_mem.sh_addr = sec->shdr.sh_addr;
+ shdr_mem.sh_type = sec->shdr.sh_type;
+ shdr_mem.sh_size = sec->shdr.sh_size;
+ shdr_mem.sh_info = sec->shdr.sh_info;
+ shdr_mem.sh_link = sec->shdr.sh_link;
+ if (sec->shdr.sh_link != SHN_UNDEF)
+ shdr_mem.sh_link = ndx_section[sec->shdr.sh_link - 1];
+ if (shdr_mem.sh_flags & SHF_INFO_LINK)
+ shdr_mem.sh_info = ndx_section[sec->shdr.sh_info - 1];
+
+ if (strtab != NULL)
+ shdr_mem.sh_name = ebl_strtaboffset (sec->strent);
+
+ Elf_Data *indata = elf_getdata (sec->scn, NULL);
+ ELF_CHECK (indata != NULL, _("cannot get section data: %s"));
+ Elf_Data *outdata = elf_getdata (sec->outscn, NULL);
+ ELF_CHECK (outdata != NULL, _("cannot copy section data: %s"));
+ *outdata = *indata;
+ elf_flagdata (outdata, ELF_C_SET, ELF_F_DIRTY);
+
+ /* Preserve the file layout of the allocated sections. */
+ if (e_type != ET_REL && (shdr_mem.sh_flags & SHF_ALLOC))
+ {
+ shdr_mem.sh_offset = sec->shdr.sh_offset;
+ placed[elf_ndxscn (sec->outscn) - 1] = true;
+
+ const GElf_Off end_offset = (shdr_mem.sh_offset
+ + (shdr_mem.sh_type == SHT_NOBITS
+ ? 0 : shdr_mem.sh_size));
+ if (end_offset > offset)
+ offset = end_offset;
+ }
+
+ ELF_CHECK (gelf_update_shdr (sec->outscn, &shdr_mem),
+ _("cannot update section header: %s"));
+
+ if (shdr_mem.sh_type == SHT_SYMTAB || shdr_mem.sh_type == SHT_DYNSYM)
+ {
+ /* We must adjust all the section indices in the symbol table. */
+
+ Elf_Data *shndxdata = NULL; /* XXX */
+
+ for (size_t i = 1; i < shdr_mem.sh_size / shdr_mem.sh_entsize; ++i)
+ {
+ GElf_Sym sym_mem;
+ GElf_Word shndx = SHN_UNDEF;
+ GElf_Sym *sym = gelf_getsymshndx (outdata, shndxdata,
+ i, &sym_mem, &shndx);
+ ELF_CHECK (sym != NULL,
+ _("cannot get symbol table entry: %s"));
+ if (sym->st_shndx != SHN_XINDEX)
+ shndx = sym->st_shndx;
+
+ if (shndx != SHN_UNDEF && shndx < SHN_LORESERVE)
+ {
+ if (shndx >= stripped_shnum)
+ error (EXIT_FAILURE, 0,
+ _("symbol [%Zu] has invalid section index"), i);
+
+ shndx = ndx_section[shndx - 1];
+ if (shndx < SHN_LORESERVE)
+ {
+ sym->st_shndx = shndx;
+ shndx = SHN_UNDEF;
+ }
+ else
+ sym->st_shndx = SHN_XINDEX;
+
+ ELF_CHECK (gelf_update_symshndx (outdata, shndxdata,
+ i, sym, shndx),
+ _("cannot update symbol table: %s"));
+ }
+ }
+
+ if (shdr_mem.sh_type == SHT_SYMTAB)
+ stripped_symtab = sec;
+ if (shdr_mem.sh_type == SHT_DYNSYM)
+ stripped_dynsym = sec;
+ }
+ }
+
+ /* We may need to update the symbol table. */
+ Elf_Data *symdata = NULL;
+ struct Ebl_Strtab *symstrtab = NULL;
+ Elf_Data *symstrdata = NULL;
+ if (unstripped_symtab != NULL && (stripped_symtab != NULL
+ || (e_type != ET_REL && bias != 0)))
+ {
+ /* Merge the stripped file's symbol table into the unstripped one. */
+ const size_t stripped_nsym = (stripped_symtab == NULL ? 1
+ : (stripped_symtab->shdr.sh_size
+ / stripped_symtab->shdr.sh_entsize));
+
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (unstripped_symtab, &shdr_mem);
+ ELF_CHECK (shdr != NULL, _("cannot get section header: %s"));
+ const size_t unstripped_nsym = shdr->sh_size / shdr->sh_entsize;
+
+ /* First collect all the symbols from both tables. */
+
+ const size_t total_syms = stripped_nsym - 1 + unstripped_nsym - 1;
+ struct symbol symbols[total_syms];
+ size_t symndx_map[total_syms];
+
+ if (stripped_symtab != NULL)
+ collect_symbols (stripped_symtab->scn,
+ elf_getscn (stripped, stripped_symtab->shdr.sh_link),
+ stripped_nsym, 0, ndx_section,
+ symbols, symndx_map);
+
+ Elf_Scn *unstripped_strtab = elf_getscn (unstripped, shdr->sh_link);
+ collect_symbols (unstripped_symtab, unstripped_strtab,
+ unstripped_nsym, e_type == ET_REL ? 0 : bias, NULL,
+ &symbols[stripped_nsym - 1],
+ &symndx_map[stripped_nsym - 1]);
+
+ /* Next, sort our array of all symbols. */
+ qsort (symbols, total_syms, sizeof symbols[0], compare_symbols);
+
+ /* Now we can weed out the duplicates. Assign remaining symbols
+ new slots, collecting a map from old indices to new. */
+ size_t nsym = *symbols[0].map = 1;
+ for (size_t i = 1; i < total_syms; ++i)
+ *symbols[i].map = (!compare_symbols (&symbols[i - 1], &symbols[i])
+ ? 0 /* This is a duplicate. */
+ : ++nsym); /* Allocate the next slot. */
+
+ /* 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);
+
+ /* 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].map != 0);
+ *symbols[i].map = i;
+ symbols[i].strent = ebl_strtabadd (symstrtab, symbols[i].name, 0);
+ }
+
+ /* Now we are ready to write the new symbol table. */
+ symdata = elf_getdata (unstripped_symtab, NULL);
+ symstrdata = elf_getdata (unstripped_strtab, NULL);
+ Elf_Data *shndxdata = NULL; /* XXX */
+
+ ebl_strtabfinalize (symstrtab, symstrdata);
+ elf_flagdata (symstrdata, ELF_C_SET, ELF_F_DIRTY);
+
+ shdr->sh_size = symdata->d_size = (1 + nsym) * shdr->sh_entsize;
+ symdata->d_buf = xmalloc (symdata->d_size);
+
+ GElf_Sym sym;
+ memset (&sym, 0, sizeof sym);
+ ELF_CHECK (gelf_update_symshndx (symdata, shndxdata, 0, &sym, SHN_UNDEF),
+ _("cannot update symbol table: %s"));
+
+ shdr->sh_info = 1;
+ for (size_t i = 0; i < nsym; ++i)
+ {
+ struct symbol *s = &symbols[i];
+
+ /* Fill in the symbol details. */
+ sym.st_name = ebl_strtaboffset (s->strent);
+ sym.st_value = s->value; /* Already biased to output address. */
+ sym.st_size = s->size;
+ sym.st_shndx = s->shndx; /* Already mapped to output index. */
+ sym.st_info = s->info.info;
+ sym.st_other = s->info.other;
+
+ /* Keep track of the number of leading local symbols. */
+ if (GELF_ST_BIND (sym.st_info) == STB_LOCAL)
+ {
+ assert (shdr->sh_info == 1 + i);
+ shdr->sh_info = 1 + i + 1;
+ }
+
+ ELF_CHECK (gelf_update_symshndx (symdata, shndxdata, 1 + i,
+ &sym, SHN_UNDEF),
+ _("cannot update symbol table: %s"));
+
+ }
+ elf_flagdata (symdata, ELF_C_SET, ELF_F_DIRTY);
+ ELF_CHECK (gelf_update_shdr (unstripped_symtab, shdr),
+ _("cannot update section header: %s"));
+
+ /* Adjust any relocations referring to the old symbol table. */
+ const size_t old_sh_link = elf_ndxscn (stripped_symtab->scn);
+ for (const struct section *sec = sections;
+ sec < &sections[stripped_shnum - 1];
+ ++sec)
+ if (sec->outscn != NULL && sec->shdr.sh_link == old_sh_link)
+ adjust_relocs (sec->outscn, sec->scn, &sec->shdr, symndx_map, shdr);
+
+ /* Also adjust references to the other old symbol table. */
+ adjust_all_relocs (unstripped, unstripped_symtab, shdr,
+ &symndx_map[stripped_nsym - 1]);
+ }
+ else if (stripped_symtab != NULL && stripped_shnum != unstripped_shnum)
+ check_symtab_section_symbols (unstripped, stripped_symtab->scn,
+ unstripped_shnum, unstripped_shstrndx,
+ stripped_symtab->outscn,
+ stripped_shnum, stripped_shstrndx,
+ debuglink);
+
+ if (stripped_dynsym != NULL)
+ (void) check_symtab_section_symbols (unstripped, stripped_dynsym->outscn,
+ unstripped_shnum,
+ unstripped_shstrndx,
+ stripped_dynsym->scn, stripped_shnum,
+ stripped_shstrndx, debuglink);
+
+ /* We need to preserve the layout of the stripped file so the
+ phdrs will match up. This requires us to do our own layout of
+ the added sections. We do manual layout even for ET_REL just
+ so we can try to match what the original probably had. */
+
+ elf_flagelf (unstripped, ELF_C_SET, ELF_F_LAYOUT);
+
+ if (offset == 0)
+ /* For ET_REL we are starting the layout from scratch. */
+ offset = gelf_fsize (unstripped, ELF_T_EHDR, 1, EV_CURRENT);
+
+ bool skip_reloc = false;
+ do
+ {
+ skip_reloc = !skip_reloc;
+ for (size_t i = 0; i < unstripped_shnum - 1; ++i)
+ if (!placed[i])
+ {
+ scn = elf_getscn (unstripped, 1 + i);
+
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+ ELF_CHECK (shdr != NULL, _("cannot get section header: %s"));
+
+ if (skip_reloc
+ && (shdr->sh_type == SHT_REL || shdr->sh_type == SHT_RELA))
+ continue;
+
+ GElf_Off align = shdr->sh_addralign ?: 1;
+ offset = (offset + align - 1) & -align;
+ shdr->sh_offset = offset;
+ if (shdr->sh_type != SHT_NOBITS)
+ offset += shdr->sh_size;
+
+ ELF_CHECK (gelf_update_shdr (scn, shdr),
+ _("cannot update section header: %s"));
+
+ if (unstripped_shstrndx == 1 + i)
+ {
+ /* Place the section headers immediately after
+ .shstrtab, and update the ELF header. */
+
+ GElf_Ehdr ehdr_mem;
+ GElf_Ehdr *ehdr = gelf_getehdr (unstripped, &ehdr_mem);
+ ELF_CHECK (ehdr != NULL, _("cannot get ELF header: %s"));
+
+ GElf_Off sh_align = gelf_getclass (unstripped) * 4;
+ offset = (offset + sh_align - 1) & -sh_align;
+ ehdr->e_shnum = unstripped_shnum;
+ ehdr->e_shoff = offset;
+ offset += unstripped_shnum * ehdr->e_shentsize;
+ ELF_CHECK (gelf_update_ehdr (unstripped, ehdr),
+ _("cannot update ELF header: %s"));
+ }
+
+ placed[i] = true;
+ }
+ } while (skip_reloc);
+
+ /* Copy each program header from the stripped file. */
+ for (uint_fast16_t i = 0; i < phnum; ++i)
+ {
+ GElf_Phdr phdr_mem;
+ GElf_Phdr *phdr = gelf_getphdr (stripped, i, &phdr_mem);
+ ELF_CHECK (phdr != NULL, _("cannot get program header: %s"));
+
+ ELF_CHECK (gelf_update_phdr (unstripped, i, phdr),
+ _("cannot update program header: %s"));
+ }
+
+ /* Finally, write out the file. */
+ ELF_CHECK (elf_update (unstripped, ELF_C_WRITE) > 0,
+ _("cannot write output file: %s"));
+
+ if (strtab != NULL)
+ {
+ ebl_strtabfree (strtab);
+ free (strtab_data->d_buf);
+ }
+
+ if (symdata != NULL)
+ free (symdata->d_buf);
+ if (symstrtab != NULL)
+ {
+ ebl_strtabfree (symstrtab);
+ free (symstrdata->d_buf);
+ }
+}
+
+/* Process one pair of files, already opened. */
+static void
+handle_file (const char *output_file,
+ Elf *stripped, const GElf_Ehdr *stripped_ehdr,
+ Elf *unstripped)
+{
+ /* Determine the address bias between the debuginfo file and the main
+ file, which may have been modified by prelinking. */
+ GElf_Addr bias = 0;
+ if (unstripped != NULL)
+ for (uint_fast16_t i = 0; i < stripped_ehdr->e_phnum; ++i)
+ {
+ GElf_Phdr phdr_mem;
+ GElf_Phdr *phdr = gelf_getphdr (stripped, i, &phdr_mem);
+ ELF_CHECK (phdr != NULL, _("cannot get program header: %s"));
+ if (phdr->p_type == PT_LOAD)
+ {
+ GElf_Phdr unstripped_phdr_mem;
+ GElf_Phdr *unstripped_phdr = gelf_getphdr (unstripped, i,
+ &unstripped_phdr_mem);
+ ELF_CHECK (unstripped_phdr != NULL,
+ _("cannot get program header: %s"));
+ bias = phdr->p_vaddr - unstripped_phdr->p_vaddr;
+ break;
+ }
+ }
+
+ /* One day we could adjust all the DWARF data (like prelink itself does). */
+ if (bias != 0)
+ error (0, 0,
+ _("DWARF output not adjusted for prelinking bias; use prelink -u"));
+
+ if (output_file == NULL)
+ /* Modify the unstripped file in place. */
+ copy_elided_sections (unstripped, stripped, stripped_ehdr->e_type,
+ stripped_ehdr->e_phnum, bias);
+ else
+ {
+ /* Copy the unstripped file and then modify it. */
+ int outfd = open64 (output_file, O_RDWR | O_CREAT,
+ stripped_ehdr->e_type == ET_REL ? 0666 : 0777);
+ if (outfd < 0)
+ error (EXIT_FAILURE, errno, _("cannot open '%s'"), output_file);
+ Elf *outelf = elf_begin (outfd, ELF_C_WRITE, NULL);
+ ELF_CHECK (outelf != NULL, _("cannot create ELF descriptor: %s"));
+
+ if (unstripped == NULL)
+ {
+ /* Actually, we are just copying out the main file as it is. */
+ copy_elf (outelf, stripped);
+ if (stripped_ehdr->e_type != ET_REL)
+ elf_flagelf (outelf, ELF_C_SET, ELF_F_LAYOUT);
+ ELF_CHECK (elf_update (outelf, ELF_C_WRITE) > 0,
+ _("cannot write output file: %s"));
+ }
+ else
+ {
+ copy_elf (outelf, unstripped);
+ copy_elided_sections (outelf, stripped, stripped_ehdr->e_type,
+ stripped_ehdr->e_phnum, bias);
+ }
+
+ elf_end (outelf);
+ close (outfd);
+ }
+}
+
+static int
+open_file (const char *file, bool writable)
+{
+ int fd = open64 (file, writable ? O_RDWR : O_RDONLY);
+ if (fd < 0)
+ error (EXIT_FAILURE, errno, _("cannot open '%s'"), file);
+ return fd;
+}
+
+/* Handle a pair of files we need to open by name. */
+static void
+handle_explicit_files (const char *output_file,
+ const char *stripped_file, const char *unstripped_file)
+{
+ int stripped_fd = open_file (stripped_file, false);
+ Elf *stripped = elf_begin (stripped_fd, ELF_C_READ, NULL);
+ GElf_Ehdr stripped_ehdr;
+ ELF_CHECK (gelf_getehdr (stripped, &stripped_ehdr),
+ _("cannot create ELF descriptor: %s"));
+
+ int unstripped_fd = -1;
+ Elf *unstripped = NULL;
+ if (unstripped_file != NULL)
+ {
+ unstripped_fd = open_file (unstripped_file, output_file == NULL);
+ unstripped = elf_begin (unstripped_fd,
+ (output_file == NULL ? ELF_C_RDWR : ELF_C_READ),
+ NULL);
+ GElf_Ehdr unstripped_ehdr;
+ ELF_CHECK (gelf_getehdr (unstripped, &unstripped_ehdr),
+ _("cannot create ELF descriptor: %s"));
+
+ if (memcmp (stripped_ehdr.e_ident, unstripped_ehdr.e_ident, EI_NIDENT)
+ || stripped_ehdr.e_type != unstripped_ehdr.e_type
+ || stripped_ehdr.e_machine != unstripped_ehdr.e_machine
+ || stripped_ehdr.e_phnum != unstripped_ehdr.e_phnum)
+ error (EXIT_FAILURE, 0, _("'%s' and '%s' do not seem to match"),
+ stripped_file, unstripped_file);
+ }
+
+ handle_file (output_file, stripped, &stripped_ehdr, unstripped);
+
+ elf_end (stripped);
+ close (stripped_fd);
+
+ elf_end (unstripped);
+ close (unstripped_fd);
+}
+
+
+/* Handle a pair of files opened implicitly by libdwfl for one module. */
+static void
+handle_dwfl_module (const char *output_file, Dwfl_Module *mod,
+ bool all, bool ignore)
+{
+ GElf_Addr bias;
+ Elf *stripped = dwfl_module_getelf (mod, &bias);
+ if (stripped == NULL)
+ {
+ if (ignore)
+ return;
+
+ const char *file;
+ const char *modname = dwfl_module_info (mod, NULL, NULL, NULL,
+ NULL, NULL, &file, NULL);
+ if (file == NULL)
+ error (EXIT_FAILURE, 0,
+ _("cannot find stripped file for module '%s': %s"),
+ modname, dwfl_errmsg (-1));
+ else
+ error (EXIT_FAILURE, 0,
+ _("cannot open stripped file '%s' for module '%s': %s"),
+ modname, file, dwfl_errmsg (-1));
+ }
+
+ Elf *debug = dwarf_getelf (dwfl_module_getdwarf (mod, &bias));
+ if (debug == NULL && !all)
+ {
+ if (ignore)
+ return;
+
+ const char *file;
+ const char *modname = dwfl_module_info (mod, NULL, NULL, NULL,
+ NULL, NULL, NULL, &file);
+ if (file == NULL)
+ error (EXIT_FAILURE, 0,
+ _("cannot find debug file for module '%s': %s"),
+ modname, dwfl_errmsg (-1));
+ else
+ error (EXIT_FAILURE, 0,
+ _("cannot open debug file '%s' for module '%s': %s"),
+ modname, file, dwfl_errmsg (-1));
+ }
+
+ if (debug == stripped)
+ {
+ if (all)
+ debug = NULL;
+ else
+ {
+ const char *file;
+ const char *modname = dwfl_module_info (mod, NULL, NULL, NULL,
+ NULL, NULL, &file, NULL);
+ error (EXIT_FAILURE, 0, _("module '%s' file '%s' is not stripped"),
+ modname, file);
+ }
+ }
+
+ GElf_Ehdr stripped_ehdr;
+ ELF_CHECK (gelf_getehdr (stripped, &stripped_ehdr),
+ _("cannot create ELF descriptor: %s"));
+
+ if (stripped_ehdr.e_type == ET_REL)
+ {
+ /* We can't use the Elf handles already open,
+ because the DWARF sections have been relocated. */
+
+ const char *stripped_file = NULL;
+ const char *unstripped_file = NULL;
+ (void) dwfl_module_info (mod, NULL, NULL, NULL, NULL, NULL,
+ &stripped_file, &unstripped_file);
+
+ handle_explicit_files (output_file, stripped_file, unstripped_file);
+ }
+ else
+ handle_file (output_file, stripped, &stripped_ehdr, debug);
+}
+
+/* Create directories containing PATH. */
+static void
+make_directories (const char *path)
+{
+ const char *lastslash = strrchr (path, '/');
+ if (lastslash == NULL)
+ return;
+
+ while (lastslash > path && lastslash[-1] == '/')
+ --lastslash;
+ if (lastslash == path)
+ return;
+
+ char *dir = strndupa (path, lastslash - path);
+ while (mkdir (dir, 0777) < 0 && errno != EEXIST)
+ if (errno == ENOENT)
+ make_directories (dir);
+ else
+ error (EXIT_FAILURE, errno, _("cannot create directory '%s'"), dir);
+}
+
+/* Handle one module being written to the output directory. */
+static void
+handle_output_dir_module (const char *output_dir, Dwfl_Module *mod,
+ bool all, bool ignore, bool modnames)
+{
+ if (! modnames)
+ {
+ /* Make sure we've searched for the ELF file. */
+ GElf_Addr bias;
+ (void) dwfl_module_getelf (mod, &bias);
+ }
+
+ const char *file;
+ const char *name = dwfl_module_info (mod, NULL, NULL, NULL,
+ NULL, NULL, &file, NULL);
+
+ if (file == NULL && ignore)
+ return;
+
+ char *output_file;
+ if (asprintf (&output_file, "%s/%s", output_dir, modnames ? name : file) < 0)
+ error (EXIT_FAILURE, 0, _("memory exhausted"));
+
+ make_directories (output_file);
+ handle_dwfl_module (output_file, mod, all, ignore);
+}
+
+
+struct match_module_info
+{
+ char **patterns;
+ Dwfl_Module *found;
+ bool match_files;
+};
+
+static int
+match_module (Dwfl_Module *mod,
+ void **userdata __attribute__ ((unused)),
+ const char *name,
+ Dwarf_Addr start __attribute__ ((unused)),
+ void *arg)
+{
+ struct match_module_info *info = arg;
+
+ if (info->patterns[0] == NULL) /* Match all. */
+ {
+ match:
+ info->found = mod;
+ return DWARF_CB_ABORT;
+ }
+
+ if (info->match_files)
+ {
+ /* Make sure we've searched for the ELF file. */
+ GElf_Addr bias;
+ (void) dwfl_module_getelf (mod, &bias);
+
+ const char *file;
+ const char *check = dwfl_module_info (mod, NULL, NULL, NULL,
+ NULL, NULL, &file, NULL);
+ assert (check == name);
+ if (file == NULL)
+ return DWARF_CB_OK;
+
+ name = file;
+ }
+
+ for (char **p = info->patterns; *p != NULL; ++p)
+ if (fnmatch (*p, name, 0) == 0)
+ goto match;
+
+ return DWARF_CB_OK;
+}
+
+/* Handle files opened implicitly via libdwfl. */
+static void
+handle_implicit_modules (const struct arg_info *info)
+{
+ struct match_module_info mmi = { info->args, NULL, info->match_files };
+ inline ptrdiff_t next (ptrdiff_t offset)
+ {
+ return dwfl_getmodules (info->dwfl, &match_module, &mmi, offset);
+ }
+ ptrdiff_t offset = next (0);
+ if (offset == 0)
+ error (EXIT_FAILURE, 0, _("no matching modules found"));
+
+ if (info->output_dir == NULL)
+ {
+ if (next (offset) != 0)
+ error (EXIT_FAILURE, 0, _("matched more than one module"));
+ handle_dwfl_module (info->output_file, mmi.found,
+ info->all, info->ignore);
+ }
+ else
+ do
+ handle_output_dir_module (info->output_dir, mmi.found,
+ info->all, info->ignore, info->modnames);
+ while ((offset = next (offset)) > 0);
+}
+
+int
+main (int argc, char **argv)
+{
+ /* Make memory leak detection possible. */
+ mtrace ();
+
+ /* We use no threads here which can interfere with handling a stream. */
+ __fsetlocking (stdin, FSETLOCKING_BYCALLER);
+ __fsetlocking (stdout, FSETLOCKING_BYCALLER);
+ __fsetlocking (stderr, FSETLOCKING_BYCALLER);
+
+ /* Set locale. */
+ setlocale (LC_ALL, "");
+
+ /* Make sure the message catalog can be found. */
+ bindtextdomain (PACKAGE, LOCALEDIR);
+
+ /* Initialize the message catalog. */
+ textdomain (PACKAGE);
+
+ /* Parse and process arguments. */
+ const struct argp_child argp_children[] =
+ {
+ {
+ .argp = dwfl_standard_argp (),
+ .header = N_("Input selection options:"),
+ .group = 1,
+ },
+ { .argp = NULL },
+ };
+ const struct argp argp =
+ {
+ .options = options,
+ .parser = parse_opt,
+ .children = argp_children,
+ .args_doc = N_("STRIPPED-FILE DEBUG-FILE\n[MODULE...]"),
+ .doc = N_("\
+Combine stripped files with separate symbols and debug information.\v\
+The first form puts the result in DEBUG-FILE if -o was not given.\n\
+\n\
+MODULE arguments give file name patterns matching modules to process.\n\
+With -f these match the file name of the main (stripped) file \
+(slashes are never special), otherwise they match the simple module names. \
+With no arguments, process all modules found.\n\
+\n\
+Multiple modules are written to files under OUTPUT-DIRECTORY, \
+creating subdirectories as needed. \
+With -m these files have simple module names, otherwise they have the \
+name of the main file complete with directory underneath OUTPUT-DIRECTORY.")
+ };
+
+ int remaining;
+ struct arg_info info = { .args = NULL };
+ error_t result = argp_parse (&argp, argc, argv, 0, &remaining, &info);
+ if (result == ENOSYS)
+ assert (info.dwfl == NULL);
+ else if (result)
+ return EXIT_FAILURE;
+ assert (info.args != NULL);
+
+ /* Tell the library which version we are expecting. */
+ elf_version (EV_CURRENT);
+
+ if (info.dwfl == NULL)
+ {
+ assert (result == ENOSYS);
+
+ if (info.output_dir != NULL)
+ {
+ char *file;
+ if (asprintf (&file, "%s/%s", info.output_dir, info.args[0]) < 0)
+ error (EXIT_FAILURE, 0, _("memory exhausted"));
+ make_directories (file);
+ handle_explicit_files (file, info.args[0], info.args[1]);
+ free (file);
+ }
+ else
+ handle_explicit_files (info.output_file, info.args[0], info.args[1]);
+ }
+ else
+ {
+ /* parse_opt checked this. */
+ assert (info.output_file != NULL || info.output_dir != NULL);
+
+ handle_implicit_modules (&info);
+
+ dwfl_end (info.dwfl);
+ }
+
+ return 0;
+}
diff --git a/tests/ChangeLog b/tests/ChangeLog
index df78c086..7d1f82fd 100644
--- a/tests/ChangeLog
+++ b/tests/ChangeLog
@@ -1,3 +1,11 @@
+2007-04-24 Roland McGrath <roland@redhat.com>
+
+ * run-strip-test.sh: When we saved the debug info, test unstrip too.
+
+2007-04-22 Roland McGrath <roland@redhat.com>
+
+ * run-allregs.sh: Update expected register info.
+
2007-04-16 Roland McGrath <roland@redhat.com>
* dwfl-addr-sect.c: New file.
diff --git a/tests/run-allregs.sh b/tests/run-allregs.sh
index bf2be9eb..a12e5c5e 100755
--- a/tests/run-allregs.sh
+++ b/tests/run-allregs.sh
@@ -48,8 +48,8 @@ integer registers:
9: %eflags (eflags), unsigned 32 bits
10: %trapno (trapno), unsigned 32 bits
FPU-control registers:
- 37: %fctrl (fctrl), unsigned 32 bits
- 38: %fstat (fstat), unsigned 32 bits
+ 37: %fctrl (fctrl), unsigned 16 bits
+ 38: %fstat (fstat), unsigned 16 bits
39: %mxcsr (mxcsr), unsigned 32 bits
MMX registers:
29: %mm0 (mm0), unsigned 64 bits
@@ -137,8 +137,8 @@ control registers:
62: %tr (tr), unsigned 64 bits
63: %ldtr (ldtr), unsigned 64 bits
64: %mxcsr (mxcsr), unsigned 64 bits
- 65: %fcw (fcw), unsigned 64 bits
- 66: %fsw (fsw), unsigned 64 bits
+ 65: %fcw (fcw), unsigned 16 bits
+ 66: %fsw (fsw), unsigned 16 bits
segment registers:
50: %es (es), unsigned 16 bits
51: %cs (cs), unsigned 16 bits
@@ -196,38 +196,38 @@ integer registers:
64: cr (cr), unsigned 32 bits
66: msr (msr), unsigned 32 bits
FPU registers:
- 32: f0 (f0), float 32 bits
- 33: f1 (f1), float 32 bits
- 34: f2 (f2), float 32 bits
- 35: f3 (f3), float 32 bits
- 36: f4 (f4), float 32 bits
- 37: f5 (f5), float 32 bits
- 38: f6 (f6), float 32 bits
- 39: f7 (f7), float 32 bits
- 40: f8 (f8), float 32 bits
- 41: f9 (f9), float 32 bits
- 42: f10 (f10), float 32 bits
- 43: f11 (f11), float 32 bits
- 44: f12 (f12), float 32 bits
- 45: f13 (f13), float 32 bits
- 46: f14 (f14), float 32 bits
- 47: f15 (f15), float 32 bits
- 48: f16 (f16), float 32 bits
- 49: f17 (f17), float 32 bits
- 50: f18 (f18), float 32 bits
- 51: f19 (f19), float 32 bits
- 52: f20 (f20), float 32 bits
- 53: f21 (f21), float 32 bits
- 54: f22 (f22), float 32 bits
- 55: f23 (f23), float 32 bits
- 56: f24 (f24), float 32 bits
- 57: f25 (f25), float 32 bits
- 58: f26 (f26), float 32 bits
- 59: f27 (f27), float 32 bits
- 60: f28 (f28), float 32 bits
- 61: f29 (f29), float 32 bits
- 62: f30 (f30), float 32 bits
- 63: f31 (f31), float 32 bits
+ 32: f0 (f0), float 64 bits
+ 33: f1 (f1), float 64 bits
+ 34: f2 (f2), float 64 bits
+ 35: f3 (f3), float 64 bits
+ 36: f4 (f4), float 64 bits
+ 37: f5 (f5), float 64 bits
+ 38: f6 (f6), float 64 bits
+ 39: f7 (f7), float 64 bits
+ 40: f8 (f8), float 64 bits
+ 41: f9 (f9), float 64 bits
+ 42: f10 (f10), float 64 bits
+ 43: f11 (f11), float 64 bits
+ 44: f12 (f12), float 64 bits
+ 45: f13 (f13), float 64 bits
+ 46: f14 (f14), float 64 bits
+ 47: f15 (f15), float 64 bits
+ 48: f16 (f16), float 64 bits
+ 49: f17 (f17), float 64 bits
+ 50: f18 (f18), float 64 bits
+ 51: f19 (f19), float 64 bits
+ 52: f20 (f20), float 64 bits
+ 53: f21 (f21), float 64 bits
+ 54: f22 (f22), float 64 bits
+ 55: f23 (f23), float 64 bits
+ 56: f24 (f24), float 64 bits
+ 57: f25 (f25), float 64 bits
+ 58: f26 (f26), float 64 bits
+ 59: f27 (f27), float 64 bits
+ 60: f28 (f28), float 64 bits
+ 61: f29 (f29), float 64 bits
+ 62: f30 (f30), float 64 bits
+ 63: f31 (f31), float 64 bits
65: fpscr (fpscr), unsigned 32 bits
privileged registers:
70: sr0 (sr0), unsigned 32 bits
@@ -246,16 +246,16 @@ privileged registers:
83: sr13 (sr13), unsigned 32 bits
84: sr14 (sr14), unsigned 32 bits
85: sr15 (sr15), unsigned 32 bits
- 100: spr0 (spr0), unsigned 32 bits
- 101: spr1 (spr1), unsigned 32 bits
+ 100: mq (mq), unsigned 32 bits
+ 101: xer (xer), unsigned 32 bits
102: spr2 (spr2), unsigned 32 bits
103: spr3 (spr3), unsigned 32 bits
104: spr4 (spr4), unsigned 32 bits
105: spr5 (spr5), unsigned 32 bits
106: spr6 (spr6), unsigned 32 bits
107: spr7 (spr7), unsigned 32 bits
- 108: spr8 (spr8), unsigned 32 bits
- 109: spr9 (spr9), unsigned 32 bits
+ 108: lr (lr), unsigned 32 bits
+ 109: ctr (ctr), unsigned 32 bits
110: spr10 (spr10), unsigned 32 bits
111: spr11 (spr11), unsigned 32 bits
112: spr12 (spr12), unsigned 32 bits
@@ -264,11 +264,11 @@ privileged registers:
115: spr15 (spr15), unsigned 32 bits
116: spr16 (spr16), unsigned 32 bits
117: spr17 (spr17), unsigned 32 bits
- 118: spr18 (spr18), unsigned 32 bits
- 119: spr19 (spr19), unsigned 32 bits
+ 118: dsisr (dsisr), unsigned 32 bits
+ 119: dar (dar), unsigned 32 bits
120: spr20 (spr20), unsigned 32 bits
121: spr21 (spr21), unsigned 32 bits
- 122: spr22 (spr22), unsigned 32 bits
+ 122: dec (dec), unsigned 32 bits
123: spr23 (spr23), unsigned 32 bits
124: spr24 (spr24), unsigned 32 bits
125: spr25 (spr25), unsigned 32 bits
@@ -502,7 +502,7 @@ privileged registers:
353: spr253 (spr253), unsigned 32 bits
354: spr254 (spr254), unsigned 32 bits
355: spr255 (spr255), unsigned 32 bits
- 356: spr256 (spr256), 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
@@ -1269,15 +1269,15 @@ privileged registers:
84: sr14 (sr14), unsigned 64 bits
85: sr15 (sr15), unsigned 64 bits
100: spr0 (spr0), unsigned 64 bits
- 101: spr1 (spr1), unsigned 64 bits
+ 101: xer (xer), unsigned 64 bits
102: spr2 (spr2), unsigned 64 bits
103: spr3 (spr3), unsigned 64 bits
104: spr4 (spr4), unsigned 64 bits
105: spr5 (spr5), unsigned 64 bits
106: spr6 (spr6), unsigned 64 bits
107: spr7 (spr7), unsigned 64 bits
- 108: spr8 (spr8), unsigned 64 bits
- 109: spr9 (spr9), unsigned 64 bits
+ 108: lr (lr), unsigned 64 bits
+ 109: ctr (ctr), unsigned 64 bits
110: spr10 (spr10), unsigned 64 bits
111: spr11 (spr11), unsigned 64 bits
112: spr12 (spr12), unsigned 64 bits
@@ -1286,11 +1286,11 @@ privileged registers:
115: spr15 (spr15), unsigned 64 bits
116: spr16 (spr16), unsigned 64 bits
117: spr17 (spr17), unsigned 64 bits
- 118: spr18 (spr18), unsigned 64 bits
- 119: spr19 (spr19), unsigned 64 bits
+ 118: dsisr (dsisr), unsigned 64 bits
+ 119: dar (dar), unsigned 64 bits
120: spr20 (spr20), unsigned 64 bits
121: spr21 (spr21), unsigned 64 bits
- 122: spr22 (spr22), unsigned 64 bits
+ 122: dec (dec), unsigned 64 bits
123: spr23 (spr23), unsigned 64 bits
124: spr24 (spr24), unsigned 64 bits
125: spr25 (spr25), unsigned 64 bits
@@ -1524,7 +1524,7 @@ privileged registers:
353: spr253 (spr253), unsigned 64 bits
354: spr254 (spr254), unsigned 64 bits
355: spr255 (spr255), unsigned 64 bits
- 356: spr256 (spr256), 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
diff --git a/tests/run-strip-test.sh b/tests/run-strip-test.sh
index 656e449b..9a82d53d 100755
--- a/tests/run-strip-test.sh
+++ b/tests/run-strip-test.sh
@@ -1,5 +1,5 @@
#! /bin/sh
-# Copyright (C) 1999, 2000, 2002, 2003, 2005 Red Hat, Inc.
+# Copyright (C) 1999, 2000, 2002, 2003, 2005, 2007 Red Hat, Inc.
# This file is part of Red Hat elfutils.
# Written by Ulrich Drepper <drepper@redhat.com>, 1999.
#
@@ -32,7 +32,7 @@ debugout=${debugfile:+-f testfile.debug.temp -F $debugfile}
testfiles $original $stripped $debugfile
-tempfiles testfile.temp testfile.debug.temp
+tempfiles testfile.temp testfile.debug.temp testfile.unstrip
testrun ../src/strip -o testfile.temp $debugout $original
@@ -46,6 +46,12 @@ cmp $debugfile testfile.debug.temp
# Check elflint and the expected result.
testrun ../src/elflint -q -d testfile.debug.temp
+
+# Now test unstrip recombining those files.
+testrun ../src/unstrip -o testfile.unstrip testfile.temp testfile.debug.temp
+
+# Check that it came back whole.
+testrun ../src/elfcmp --hash-inexact $original testfile.unstrip
}
exit 0