This is the mail archive of the
binutils@sourceware.org
mailing list for the binutils project.
[patch,PE-COFF,head+2.22] Don't generate bogus base relocs in .eh_frame of DLLs.
- From: Dave Korn <dave dot korn dot cygwin at gmail dot com>
- To: "binutils at sourceware dot org" <binutils at sourceware dot org>
- Date: Sun, 25 Sep 2011 19:05:22 +0100
- Subject: [patch,PE-COFF,head+2.22] Don't generate bogus base relocs in .eh_frame of DLLs.
[ I'm back after a couple of months unexpected hiatus. If there's anything
important that I've missed, please feel free to mail me off-list to draw my
attention to it. ]
Hi list,
The latest release of the libstdc++ DLL for Cygwin breaks exception handling
if it is rebased away from its default address. Debugging this shows that one
of the Dwarf-2 FDEs in the .eh_frame section gets mangled, which causes the
libgcc unwind code to get confused and fail to find the FDE for a given return
address from the stack frame, resulting in an early exit through terminate().
It turns out that the FDE in question refers to an inline function, which
exists in one of the input object files linked to build the final DLL in a
COMDAT section. When the COMDAT resolution picks a copy of the section from a
different object file, the .eh_frame FDE data contains a reloc that now points
to a discarded section. This happens a lot because there's only one .eh_frame
section per object file that covers all the text sections, any one of which
might get discarded.
The generic linker handles it just fine; it gets a value of zero against the
*ABS* section, resulting in a zero in the relocated section contents, and the
libgcc unwind code interprets a zero start address in an FDE to mean that the
FDE is discarded. However the PE linker still goes and generates a base reloc
corresponding to the effectively-disregarded BFD reloc. (PE DLLs are
pre-linked to assume they will be loaded at a chosen base address; if they end
up loaded elsewhere at runtime, the OS loader applies a set of PE-specific
relocations known as "base relocations".) Thus when the DLL is rebased, the
start address field in the FDE gets written with the offset by which the DLL
is being rebased, and libgcc interprets this as an FDE for a PC range
somewhere in low or high memory (according to the direction in which the DLL
is rebased) and gets its internal list of memory regions in a twist.
The attached patch is conservative; it checks just for BFD against symbols
from discarded input sections, and if the reloc itself is in an EH section it
doesn't generate a corresponding base reloc. It doesn't cause any failures in
the ld testsuite, but then I don't think we have anything to verify base reloc
generation in there anyway, so I'm also running a GCC bootstrap and test cycle
using the patched binutils. (I already checked that the libstdc++ DLL that it
builds omits the specific base relocs that were causing the problem and
handles exceptions just fine even after rebasing). It'll probably be another
48 hours before the results from that are in, so this is advance warning for
Tristan; I'd like to get this into the 2.22 release so that it can be in the
next Cygwin binutils package. Ok, Tristan?
ld/ChangeLog:
2011-09-25 Dave Korn <dave.korn.cygwin@...
* pe-dll.c (generate_reloc): Don't emit base reloc if underlying
BFD reloc is from an EH section and refers to a discarded input
COMDAT section.
cheers,
DaveK
Index: ld/pe-dll.c
===================================================================
RCS file: /cvs/src/src/ld/pe-dll.c,v
retrieving revision 1.138
diff -p -u -r1.138 pe-dll.c
--- ld/pe-dll.c 16 Sep 2011 01:15:19 -0000 1.138
+++ ld/pe-dll.c 25 Sep 2011 12:59:34 -0000
@@ -1395,6 +1395,16 @@ generate_reloc (bfd *abfd, struct bfd_li
else if (!blhe || blhe->type != bfd_link_hash_defined)
continue;
}
+ /* Nor for Dwarf FDE references to discarded sections. */
+ else if (bfd_is_abs_section (sym->section->output_section))
+ {
+ /* These are the same section names that
+ _bfd_elf_default_action_discarded chooses to discard
+ relocs against. */
+ if (!strcmp (s->name, ".eh_frame")
+ || !strcmp (s->name, ".gcc_except_table"))
+ continue;
+ }
reloc_data[total_relocs].vma = sec_vma + relocs[i]->address;