diff options
author | Jan Kratochvil <jan.kratochvil@redhat.com> | 2011-01-06 11:20:58 +0100 |
---|---|---|
committer | Jan Kratochvil <jan.kratochvil@redhat.com> | 2011-01-06 11:20:58 +0100 |
commit | 58d75e754c909f36a9fe268848ada57ce557e349 (patch) | |
tree | 84f1e749a624dd612a351f66bd582c7f8c92290f | |
parent | 83cc3d8ad663fe99a789bcb7ced6c07121627f31 (diff) | |
parent | 2c7d0ddfb8a08b8c450d0d3a5e297ec5d624661d (diff) | |
download | elfutils-58d75e754c909f36a9fe268848ada57ce557e349.tar.gz |
Merge branch 'master' into jkratoch/gdbserver
-rw-r--r-- | libdwfl/ChangeLog | 7 | ||||
-rw-r--r-- | libdwfl/dwfl_module_getdwarf.c | 96 | ||||
-rw-r--r-- | libdwfl/libdwflP.h | 12 | ||||
-rw-r--r-- | src/ChangeLog | 6 | ||||
-rw-r--r-- | src/strip.c | 58 |
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) { |