summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJan Kratochvil <jan.kratochvil@redhat.com>2011-01-06 11:20:58 +0100
committerJan Kratochvil <jan.kratochvil@redhat.com>2011-01-06 11:20:58 +0100
commit58d75e754c909f36a9fe268848ada57ce557e349 (patch)
tree84f1e749a624dd612a351f66bd582c7f8c92290f
parent83cc3d8ad663fe99a789bcb7ced6c07121627f31 (diff)
parent2c7d0ddfb8a08b8c450d0d3a5e297ec5d624661d (diff)
downloadelfutils-58d75e754c909f36a9fe268848ada57ce557e349.tar.gz
Merge branch 'master' into jkratoch/gdbserver
-rw-r--r--libdwfl/ChangeLog7
-rw-r--r--libdwfl/dwfl_module_getdwarf.c96
-rw-r--r--libdwfl/libdwflP.h12
-rw-r--r--src/ChangeLog6
-rw-r--r--src/strip.c58
5 files changed, 165 insertions, 14 deletions
diff --git a/libdwfl/ChangeLog b/libdwfl/ChangeLog
index c5746c87..9aebb786 100644
--- a/libdwfl/ChangeLog
+++ b/libdwfl/ChangeLog
@@ -1,3 +1,10 @@
+2011-01-04 Roland McGrath <roland@redhat.com>
+
+ * dwfl_module_getdwarf.c (open_elf): Enhance address_sync calculation
+ logic to consider section addresses, the better to survive all the
+ possible prelink machinations.
+ * libdwflP.h (struct dwfl_file): Comment change.
+
2010-11-30 Roland McGrath <roland@redhat.com>
* derelocate.c (dwfl_module_relocations): Remove over-eager assert.
diff --git a/libdwfl/dwfl_module_getdwarf.c b/libdwfl/dwfl_module_getdwarf.c
index d89081b7..0bd231f9 100644
--- a/libdwfl/dwfl_module_getdwarf.c
+++ b/libdwfl/dwfl_module_getdwarf.c
@@ -1,5 +1,5 @@
/* Find debugging and symbol information for a module in libdwfl.
- Copyright (C) 2005-2010 Red Hat, Inc.
+ Copyright (C) 2005-2011 Red Hat, Inc.
This file is part of Red Hat elfutils.
Red Hat elfutils is free software; you can redistribute it and/or modify
@@ -95,10 +95,58 @@ open_elf (Dwfl_Module *mod, struct dwfl_file *file)
if (mod->e_type != ET_REL)
{
+ /* In any non-ET_REL file, we compute the "synchronization address".
+
+ We start with the address at the end of the first PT_LOAD
+ segment. When prelink converts REL to RELA in an ET_DYN
+ file, it expands the space between the beginning of the
+ segment and the actual code/data addresses. Since that
+ change wasn't made in the debug file, the distance from
+ p_vaddr to an address of interest (in an st_value or DWARF
+ data) now differs between the main and debug files. The
+ distance from address_sync to an address of interest remains
+ consistent.
+
+ If there are no section headers at all (full stripping), then
+ the end of the first segment is a valid synchronization address.
+ This cannot happen in a prelinked file, since prelink itself
+ relies on section headers for prelinking and for undoing it.
+ (If you do full stripping on a prelinked file, then you get what
+ you deserve--you can neither undo the prelinking, nor expect to
+ line it up with a debug file separated before prelinking.)
+
+ However, when prelink processes an ET_EXEC file, it can do
+ something different. There it juggles the "special" sections
+ (SHT_DYNSYM et al) to make space for the additional prelink
+ special sections. Sometimes it will do this by moving a special
+ section like .dynstr after the real program sections in the
+ first PT_LOAD segment--i.e. to the end. That changes the end
+ address of the segment, so it no longer lines up correctly and
+ is not a valid synchronization address to use.
+
+ So, instead we use a method based on the section headers. We
+ look at the allocated SHT_PROGBITS (or SHT_NOBITS) sections.
+ (Most every file will have some SHT_PROGBITS sections, but it's
+ possible to have one with nothing but .bss, i.e. SHT_NOBITS.)
+ The special sections that can be moved around have different
+ sh_type values--except for .interp, the section that became the
+ PT_INTERP segment. So we exclude the SHT_PROGBITS section whose
+ address matches the PT_INTERP p_vaddr.
+
+ Since the debug file will always have section headers, we must
+ choose a method of examining section headers that will also line
+ up with the end of the first PT_LOAD segment, in case the main
+ file was fully stripped so we are synchronizing between a
+ PT_LOAD-based and a section-based calculation. To that end, we
+ use the highest section end address that lies inside the first
+ segment. If none does, then we use the highest end address of
+ any non-special section. */
+
size_t phnum;
if (unlikely (elf_getphdrnum (file->elf, &phnum) != 0))
goto elf_error;
+ GElf_Addr interp = 0;
file->vaddr = file->address_sync = 0;
for (size_t i = 0; i < phnum; ++i)
{
@@ -106,12 +154,52 @@ open_elf (Dwfl_Module *mod, struct dwfl_file *file)
GElf_Phdr *ph = gelf_getphdr (file->elf, i, &ph_mem);
if (unlikely (ph == NULL))
goto elf_error;
- if (ph->p_type == PT_LOAD)
+ switch (ph->p_type)
{
- file->vaddr = ph->p_vaddr & -ph->p_align;
- file->address_sync = ph->p_vaddr + ph->p_memsz;
+ case PT_INTERP:
+ interp = ph->p_vaddr;
+ break;
+ case PT_LOAD:
+ if (file->address_sync == 0)
+ {
+ file->vaddr = ph->p_vaddr & -ph->p_align;
+ file->address_sync = ph->p_vaddr + ph->p_memsz;
+ }
break;
+ default:
+ continue;
+ }
+ if (interp != 0 && file->address_sync != 0)
+ break;
+ }
+
+ if (file->address_sync != 0)
+ {
+ GElf_Addr highest_end = 0;
+ GElf_Addr highest_end_in_seg = 0;
+ Elf_Scn *scn = NULL;
+ while ((scn = elf_nextscn (file->elf, scn)) != NULL)
+ {
+ GElf_Shdr sh_mem;
+ GElf_Shdr *sh = gelf_getshdr (scn, &sh_mem);
+ if (unlikely (sh == NULL))
+ goto elf_error;
+ if ((sh->sh_flags & SHF_ALLOC)
+ && ((sh->sh_type == SHT_PROGBITS && sh->sh_addr != interp)
+ || sh->sh_type == SHT_NOBITS))
+ {
+ const GElf_Addr sh_end = sh->sh_addr + sh->sh_size;
+ if (sh_end > highest_end)
+ highest_end = sh_end;
+ if (sh_end <= file->address_sync
+ && sh_end > highest_end_in_seg)
+ highest_end_in_seg = sh_end;
+ }
}
+ if (highest_end_in_seg >= file->vaddr && highest_end_in_seg != 0)
+ file->address_sync = highest_end_in_seg;
+ else if (highest_end != 0)
+ file->address_sync = highest_end;
}
}
diff --git a/libdwfl/libdwflP.h b/libdwfl/libdwflP.h
index 1003b2d3..93db5298 100644
--- a/libdwfl/libdwflP.h
+++ b/libdwfl/libdwflP.h
@@ -1,5 +1,5 @@
/* Internal definitions for libdwfl.
- Copyright (C) 2005-2010 Red Hat, Inc.
+ Copyright (C) 2005-2011 Red Hat, Inc.
This file is part of Red Hat elfutils.
Red Hat elfutils is free software; you can redistribute it and/or modify
@@ -146,15 +146,7 @@ struct dwfl_file
GElf_Addr vaddr;
/* This is an address chosen for synchronization between the main file
- and the debug file. In a file without phdrs, this is zero. In
- other files it is the address at the end of the first PT_LOAD
- segment. When prelink converts REL to RELA in an ET_DYN file, it
- expands the space between the beginning of the segment and the
- actual code/data addresses. Since that change wasn't made in the
- debug file, the distance from p_vaddr to an address of interest (in
- an st_value or DWARF data) now differs between the main and debug
- files. The distance from address_sync to an address of interest
- remains consistent. */
+ and the debug file. See dwfl_module_getdwarf.c for how it's chosen. */
GElf_Addr address_sync;
};
diff --git a/src/ChangeLog b/src/ChangeLog
index 7a151927..9d42c55e 100644
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -1,3 +1,9 @@
+2011-01-04 Roland McGrath <roland@redhat.com>
+
+ * strip.c (remove_shdrs): New variable.
+ (options, parse_opt): Grok --strip-sections to set it.
+ (handle_elf): When that's set, truncate off .shstrtab and shdrs.
+
2010-11-10 Roland McGrath <roland@redhat.com>
* findtextrel.c (process_file): Don't assume order of sections.
diff --git a/src/strip.c b/src/strip.c
index 7b2b889a..9b2d24a3 100644
--- a/src/strip.c
+++ b/src/strip.c
@@ -65,6 +65,7 @@ ARGP_PROGRAM_BUG_ADDRESS_DEF = PACKAGE_BUGREPORT;
/* Values for the parameters which have no short form. */
#define OPT_REMOVE_COMMENT 0x100
#define OPT_PERMISSIVE 0x101
+#define OPT_STRIP_SECTIONS 0x102
/* Definitions of arguments for argp functions. */
@@ -80,6 +81,8 @@ static const struct argp_option options[] =
{ "strip-debug", 'g', NULL, 0, N_("Remove all debugging symbols"), 0 },
{ NULL, 'd', NULL, OPTION_ALIAS, NULL, 0 },
{ NULL, 'S', NULL, OPTION_ALIAS, NULL, 0 },
+ { "strip-sections", OPT_STRIP_SECTIONS, NULL, 0,
+ N_("Remove section headers (not recommended)"), 0 },
{ "preserve-dates", 'p', NULL, 0,
N_("Copy modified/access timestamps to the output"), 0 },
{ "remove-comment", OPT_REMOVE_COMMENT, NULL, 0,
@@ -140,6 +143,9 @@ static bool remove_comment;
/* If true remove all debug sections. */
static bool remove_debug;
+/* If true remove all section headers. */
+static bool remove_shdrs;
+
/* If true relax some ELF rules for input files. */
static bool permissive;
@@ -268,6 +274,10 @@ parse_opt (int key, char *arg, struct argp_state *state)
remove_debug = true;
break;
+ case OPT_STRIP_SECTIONS:
+ remove_shdrs = true;
+ break;
+
case OPT_PERMISSIVE:
permissive = true;
break;
@@ -1639,6 +1649,7 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname,
newehdr->e_entry = ehdr->e_entry;
newehdr->e_flags = ehdr->e_flags;
newehdr->e_phoff = ehdr->e_phoff;
+
/* We need to position the section header table. */
const size_t offsize = gelf_fsize (elf, ELF_T_OFF, 1, EV_CURRENT);
newehdr->e_shoff = ((shdr_info[shdridx].shdr.sh_offset
@@ -1692,6 +1703,53 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname,
result = 1;
}
+ if (remove_shdrs)
+ {
+ /* libelf can't cope without the section headers being properly intact.
+ So we just let it write them normally, and then we nuke them later. */
+
+ if (newehdr->e_ident[EI_CLASS] == ELFCLASS32)
+ {
+ assert (offsetof (Elf32_Ehdr, e_shentsize) + sizeof (Elf32_Half)
+ == offsetof (Elf32_Ehdr, e_shnum));
+ assert (offsetof (Elf32_Ehdr, e_shnum) + sizeof (Elf32_Half)
+ == offsetof (Elf32_Ehdr, e_shstrndx));
+ const Elf32_Off zero_off = 0;
+ const Elf32_Half zero[3] = { 0, 0, SHN_UNDEF };
+ if (pwrite_retry (fd, &zero_off, sizeof zero_off,
+ offsetof (Elf32_Ehdr, e_shoff)) != sizeof zero_off
+ || (pwrite_retry (fd, zero, sizeof zero,
+ offsetof (Elf32_Ehdr, e_shentsize))
+ != sizeof zero)
+ || ftruncate64 (fd, shdr_info[shdridx].shdr.sh_offset) < 0)
+ {
+ error (0, errno, gettext ("while writing '%s'"),
+ fname);
+ result = 1;
+ }
+ }
+ else
+ {
+ assert (offsetof (Elf64_Ehdr, e_shentsize) + sizeof (Elf64_Half)
+ == offsetof (Elf64_Ehdr, e_shnum));
+ assert (offsetof (Elf64_Ehdr, e_shnum) + sizeof (Elf64_Half)
+ == offsetof (Elf64_Ehdr, e_shstrndx));
+ const Elf64_Off zero_off = 0;
+ const Elf64_Half zero[3] = { 0, 0, SHN_UNDEF };
+ if (pwrite_retry (fd, &zero_off, sizeof zero_off,
+ offsetof (Elf64_Ehdr, e_shoff)) != sizeof zero_off
+ || (pwrite_retry (fd, zero, sizeof zero,
+ offsetof (Elf64_Ehdr, e_shentsize))
+ != sizeof zero)
+ || ftruncate64 (fd, shdr_info[shdridx].shdr.sh_offset) < 0)
+ {
+ error (0, errno, gettext ("while writing '%s'"),
+ fname);
+ result = 1;
+ }
+ }
+ }
+
fail_close:
if (shdr_info != NULL)
{