This is the mail archive of the
binutils@sourceware.org
mailing list for the binutils project.
[LONG] RF{C,D,A,H}: pe-x86 DLLs (shared libstdc++ and others) vs C++ RTTI vs LD
- From: "Dave Korn" <dave dot korn dot cygwin at googlemail dot com>
- To: binutils at sourceware dot org
- Date: Sun, 30 Nov 2008 01:17:18 +0000
- Subject: [LONG] RF{C,D,A,H}: pe-x86 DLLs (shared libstdc++ and others) vs C++ RTTI vs LD
The problem:
-============-
A naive attempt to build shared libstdc++ as a DLL (by trivially adding the
'-no-undefined' switch to the libtool link flags) shows massive regressions
in the testsuite, which on inspection turn out to be very largely caused by
"excess errors": the linker is emitting an awful lot of warnings such as:
"Info: resolving typeinfo for std::runtime_error by linking to
__imp___ZTISt13runtime_error (auto-import)
/usr/i686-pc-cygwin/bin/ld: warning: auto-importing has been activated
without --enable-auto-import specified on the command line.
This should work unless it involves constant data structures referencing
symbols from auto-imported DLLs."
There is also a major regression in the number of execution tests failing,
as compared to the default static libstdc++ build.
[ Note that in both cases (shared or static libstdc++), I have been testing
after applying a patch from Danny that adds '_GLIBCXX_IMPORT' declarations
through libstdc++ in order to add __attribute__((dllimport)) to the basic
class declarations, i.e. in libstdc++-v3/config/os/newlib/os_defines.h, add
#ifdef _DLL
#define _GLIBCXX_IMPORT __attribute__((dllimport))
#else
#define _GLIBCXX_IMPORT
#endif
and then mark all the basic classes in libstdc++ like so:
- extern template class basic_ios<char>;
+ extern template class _GLIBCXX_IMPORT basic_ios<char>;
or
- class strstreambuf : public basic_streambuf<char, char_traits<char> >
+ class _GLIBCXX_IMPORT strstreambuf : public basic_streambuf<char,
char_traits<char> >
or
- class failure : public exception
+ class _GLIBCXX_IMPORT failure : public exception
etc. ]
Analysis:
-=========-
On examination the failures show similar symptoms to the "can't apply relocs
to .rodata sections" bug: the compiled application exits during very early
pre-init, before any of the standard entry points are reached. As an
example, here's what happens when we try and manually run the testcase
libstdc++-v3/testsuite/19_diagnostics/runtime_error/what-2.cc (I cut and
pasted the command line from libstdc++.log of the failing test run):
----------------------------------<snip!>----------------------------------
/gnu/gcc/release/gcc4-4.3.2-2/build/i686-pc-cygwin/libstdc++-v3/testsuite
$ /gnu/gcc/release/gcc4-4.3.2-2/build/./gcc/g++ -shared-libgcc \
-B/gnu/gcc/release/gcc4-4.3.2-2/build/./gcc -nostdinc++ \
-L/gnu/gcc/release/gcc4-4.3.2-2/build/i686-pc-cygwin/libstdc++-v3/src \
-L/gnu/gcc/release/gcc4-4.3.2-2/build/i686-pc-cygwin/libstdc++-v3/src/.libs\
-B/usr/i686-pc-cygwin/bin/ -B/usr/i686-pc-cygwin/lib/ -isystem \
/usr/i686-pc-cygwin/include -isystem /usr/i686-pc-cygwin/sys-include -g -O2\
-D_GLIBCXX_ASSERT -fmessage-length=0 -ffunction-sections -fdata-sections \
-O2 -g -O2 -pipe -O2 -g -O2 -pipe -DLOCALEDIR="." -nostdinc++ \
-I/gnu/gcc/release/gcc4-4.3.2-2/build/i686-pc-cygwin/libstdc++-v3/include/i686-pc-cygwin
\
-I/gnu/gcc/release/gcc4-4.3.2-2/build/i686-pc-cygwin/libstdc++-v3/include \
-I/gnu/gcc/release/gcc4-4.3.2-2/src/gcc-4.3.2/libstdc++-v3/libsupc++ \
-I/gnu/gcc/release/gcc4-4.3.2-2/src/gcc-4.3.2/libstdc++-v3/include/backward\
-I/gnu/gcc/release/gcc4-4.3.2-2/src/gcc-4.3.2/libstdc++-v3/testsuite/util \
-Wl,--gc-sections /usr/lib/libiconv.a \
/gnu/gcc/release/gcc4-4.3.2-2/src/gcc-4.3.2/libstdc++-v3/testsuite/19_diagnostics/runtime_error/what-2.cc
-include bits/stdc++.h ./libtestc++.a -o ./what-2.exe
Info: resolving typeinfo for std::runtime_error by linking to
__imp___ZTISt13run time_error (auto-import)
/usr/i686-pc-cygwin/bin/ld: warning: auto-importing has been activated
without --enable-auto-import specified on the command line.
This should work unless it involves constant data structures referencing
symbols from auto-imported DLLs.
/gnu/gcc/release/gcc4-4.3.2-2/build/i686-pc-cygwin/libstdc++-v3/testsuite
$ gdb ./what-2.exe
GNU gdb 6.8.0.20080328-cvs (cygwin-special)
Copyright (C) 2008 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-pc-cygwin"...
(gdb) b main
Breakpoint 1 at 0x406a21: file
/gnu/gcc/release/gcc4-4.3.2-2/src/gcc-4.3.2/libstdc++-v3/testsuite/19_diagnostics/runtime_error/what-2.cc,
line 47.
(gdb) b __main
Breakpoint 2 at 0x404e0c
(gdb) b cygwin_crt0
Breakpoint 3 at 0x404db4
(gdb) b cygwin_premain0
Breakpoint 4 at 0x4050ef
(gdb) b WinMainCRTStartup
Breakpoint 5 at 0x401006
(gdb) r
Starting program:
/win/i/FSF-Gcc/release/gcc4-4.3.2-2/build/i686-pc-cygwin/libstdc++-v3/testsuite/what-2.exe
[New thread 1188.0x5bc]
gdb: unknown target exception 0xc0000139 at 0x77fb17a4
Program exited with code 0200.
You can't do that without a process to debug.
(gdb)
----------------------------------<snip!>----------------------------------
Adding "-Wl,--enable-auto-import" to the compile command line suppresses the
warning and informational messages, but the compiled executable fails in the
exact same way nonetheless.
Diagnosis:
-==========-
In gcc/config/i386/winnt-cxx.c, in i386_pe_adjust_class_at_definition(), is
this comment:
/* We leave typeinfo tables alone. We can't mark TI objects as
dllimport, since the address of a secondary VTT may be needed
for static initialization of a primary VTT. VTT's of
dllimport'd classes should always be link-once COMDAT. */
However, this is not what happens in practice. What actually happens is
that during the final link of the libstdc++ DLL (which generates the import
library at the same time), the typeinfos are emitted into the generated DLL
in the .rodata section:
----------------------------------<snip!>----------------------------------
/gnu/gcc/release/gcc4-4.3.2-2/build/i686-pc-cygwin/libstdc++-v3/src $ nm \
.libs/cygstdc++-6.dll | grep __ZTI
6c4d00a0 R __ZTIN10__cxxabiv115__forced_unwindE
6c4d00a8 R __ZTIN10__cxxabiv116__enum_type_infoE
6c4d00b4 R __ZTIN10__cxxabiv117__array_type_infoE
6c4d00c0 R __ZTIN10__cxxabiv117__class_type_infoE
[ many more omitted ]
----------------------------------<snip!>----------------------------------
and import stubs are generated for them in the import library:
----------------------------------<snip!>----------------------------------
/gnu/gcc/release/gcc4-4.3.2-2/build/i686-pc-cygwin/libstdc++-v3/src $ nm \
.libs/libstdc++.dll.a | grep __ZTI
00000000 I __imp___ZTISt9time_base
00000000 I __nm___ZTISt9time_base
00000000 I __imp___ZTISt9strstream
00000000 I __nm___ZTISt9strstream
[ many more omitted ]
----------------------------------<snip!>----------------------------------
The function import_export_decl() in gcc/cp/decl2.c says:
/* The ABI requires that all virtual tables be emitted with
COMDAT linkage. However, [ ... snip stuff only relevant to
systems without adequate COMDAT / weak support ... ]
and:
/* The generic C++ ABI says that class data is always
COMDAT, even if there is a key function. Some
variants [ ... snip stuff relevant to other variants ... ]
and:
/* Do not import tinfo nodes if the class has dllimport attribute.
Dllimports do not have a constant address at compile time, so
static initialization of tables with RTTI fields is a problem.
Set to comdat instead. */
Possible solution:
-==================-
So, I think that having the typeinfo imported from the DLL rather than
statically linked in a COMDAT section is likely to be the cause of the
early pre-initialisation failure seen during the what-2.cc testcase shown
above.
To test this, I hacked together a patched version of ld that doesn't export
typeinfos from the DLL: instead, it copies them wholesale into the import
library, from where they can be COMDAT-linked into the application at final
link time.
I don't know for sure if this is the right solution, but it certainly fixes
both problems in the what-2.cc test: no more warning about auto-imports,
and the executable runs successfully to completion.
Results:
-========-
I ran the libstdc++ testsuite after building the DLL and import lib using
an unpatched ld, and then again with my patch: the results were good -
@@ -3609,8 +1742,8 @@ FAIL: tr1/7_regular_expressions/match_re
=== libstdc++ Summary ===
-# of expected passes 5754
-# of unexpected failures 3544
+# of expected passes 7621
+# of unexpected failures 1677
# of unexpected successes 2
# of expected failures 121
# of unsupported tests 556
The differences amount to 627 fewer "test for excess errors" FAILs and 1244
fewer "execution test" FAILs. However there were four regressions which
I'm looking at now:
+FAIL: 21_strings/basic_string/cons/char/3.cc execution test
+FAIL: 21_strings/basic_string/cons/char/3.cc execution test
+FAIL: 21_strings/basic_string/cons/char/3.cc execution test
+FAIL: thread/18185.cc execution test
They both fail when trying to throw an object based on a string :-(
(gdb)
36 throw string_type("leak");
(gdb)
Program received signal SIGSEGV, Segmentation fault.
0x00000000 in ?? ()
(gdb) bt
#0 0x00000000 in ?? ()
#1 0x00405299 in get_adjusted_ptr (catch_type=0x409250, throw_type=0x409250,
thrown_ptr_p=0x6069cb18)
at /gnu/gcc/release/gcc4-4.3.2-1/src/gcc-4.3.2/libstdc++-v3/libsupc++/eh_personality.cc:244
#2 0x00406894 in __gxx_personality_v0 (version=1, actions=1,
exception_class=<value optimized out>, ue_header=0x642af8,
context=0x6069cc14)
at /gnu/gcc/release/gcc4-4.3.2-1/src/gcc-4.3.2/libstdc++-v3/libsupc++/eh_personality.cc:586
#3 0x635469ea in _Unwind_RaiseException (exc=0x642af8)
at /gnu/gcc/release/gcc4-4.3.2-2/src/gcc-4.3.2/libgcc/../gcc/unwind.inc:118
#4 0x004064a9 in __cxa_throw (obj=0x642b18, tinfo=0x409250,
dest=0x405d90 <~basic_string>)
at /gnu/gcc/release/gcc4-4.3.2-1/src/gcc-4.3.2/libstdc++-v3/libsupc++/eh_throw.cc:71
(gdb)
45 std::string str1(bogus);
(gdb)
Program received signal SIGSEGV, Segmentation fault.
0x00000000 in ?? ()
(gdb) bt
#0 0x00000000 in ?? ()
#1 0x004054f9 in get_adjusted_ptr (catch_type=0x4092b4,
throw_type=0x6c4d0498, thrown_ptr_p=0x22c938)
at /gnu/gcc/release/gcc4-4.3.2-1/src/gcc-4.3.2/libstdc++-v3/libsupc++/eh_personality.cc:244
#2 0x004067a4 in __gxx_personality_v0 (version=1, actions=1,
exception_class=<value optimized out>, ue_header=0x642ae0,
context=0x22ca34)
at /gnu/gcc/release/gcc4-4.3.2-1/src/gcc-4.3.2/libstdc++-v3/libsupc++/eh_personality.cc:586
#3 0x635469ea in _Unwind_RaiseException (exc=0x642ae0)
at /gnu/gcc/release/gcc4-4.3.2-2/src/gcc-4.3.2/libgcc/../gcc/unwind.inc:118
#4 0x6c4c16d9 in __cxa_throw (obj=0x642b00, tinfo=0x6c4d0498,
dest=0x6c4a9000 <~logic_error>)
at /gnu/gcc/release/gcc4-4.3.2-2/src/gcc-4.3.2/libstdc++-v3/libsupc++/eh_throw.cc:71
#5 0x6c4bc787 in std::__throw_logic_error (
__s=0x6c4cd64c "basic_string::_S_construct NULL not valid")
at /gnu/gcc/release/gcc4-4.3.2-2/src/gcc-4.3.2/libstdc++-v3/src/functexcept.cc:64
I don't know what the personality stuff does yet, I'll be taking a look at
it while I try to figure out what's gone wrong, but it looks bad.
Help!
-=====-
Although it fixes the crash in the testcase, I don't know if this is
entirely the right solution. The DLL still has its own copy of the RTTI,
just no longer exported; the executable has its own, and any other C++ DLLs
linked into the application will have their own copies. I think the RTTI
system is able to handle this situation - that's why it has the options to
perform typeid comparisons based on fully matching the type name strings
rather than just comparing the name string pointers for equality, see the
use of __GXX_MERGED_TYPEINFO_NAMES in libsupc++ for details. Normally this
isn't needed on systems that support weak symbols, but the limitations on
win32/PE DLLs mean we can't link RTTI once-only through COMDATs in the .exe
and have the DLLs import it, and the problem with putting it in the DLL is
that it leads to the crash above. Maybe I should just be relocating it
within the DLL to a non-read-only section? But the comment in winnt-cxx.c
seemed quite adamant that it needs to be COMDAT, and I imagine that whoever
wrote it would have considered that option, if it really was that simple.
Secondly, I'm not sure if I have chosen the right subset of class data
objects to give special treatment to. The way I wrote it so far, it copies
typeinfo, typeinfo names and vtables wholesale into the import lib, but not
VTTs, construction vtables, virtual thunks or non-virtual thunks. That's
a fairly random selection based on the info messages I saw coming out of the
linker during a testsuite run. I think it's ok to leave thunks as exports
from the DLL, because in winnt-cxx.c, in i386_pe_type_dllexport_p() it says:
/* Avoid exporting compiler-generated default dtors and copy ctors.
The only artificial methods that need to be exported are virtual
and non-virtual thunks. */
I suspect I'm likely to need to copy VTTs into the import library rather
than export them. I'm wondering also if it might be possible to export the
typeinfo name strings, rather than COMDAT them, so as to enable matching
based on pointers rather than string comparison? As to the vtables and
construction vtables, I just don't know. Anyone else got a better idea than
me?
Thirdly, do I maybe need to turn on auto-import always when linking against
C++ DLLs? I'm not really clear what would be the pros and cons of doing so,
but I'm still getting some excess error failures in the testsuite, with
stuff like:
Info: resolving VTT for std::basic_fstream<char,
std::char_traits<char> > by linking to
__imp___ZTTSt13basic_fstreamIcSt11char_traitsIcEE (auto-import)
Info: resolving VTT for std::basic_ifstream<char,
std::char_traits<char> > by linking to
__imp___ZTTSt14basic_ifstreamIcSt11char_traitsIcEE (auto-import)
Info: resolving VTT for std::basic_istringstream<char,
std::char_traits<char>, std::allocator<char> > by linking to
__imp___ZTTSt19basic_istringstreamIcSt11char_traitsIcESaIcEE
(auto-import)
Info: resolving VTT for std::basic_ofstream<char,
std::char_traits<char> > by linking to
__imp___ZTTSt14basic_ofstreamIcSt11char_traitsIcEE (auto-import)
Info: resolving VTT for std::basic_ostringstream<char,
std::char_traits<char>, std::allocator<char> > by linking to
__imp___ZTTSt19basic_ostringstreamIcSt11char_traitsIcESaIcEE
(auto-import)
Info: resolving VTT for std::basic_stringstream<char,
std::char_traits<char>, std::allocator<char> > by linking to
__imp___ZTTSt18basic_stringstreamIcSt11char_traitsIcESaIcEE
(auto-import)
Info: resolving std::__detail::__prime_list by linking to
__imp___ZNSt8__detail12__prime_listE (auto-import)
Info: resolving std::__num_base::_S_atoms_in by linking to
__imp___ZNSt10__num_base11_S_atoms_inE (auto-import)
Info: resolving std::__num_base::_S_atoms_out by linking to
__imp___ZNSt10__num_base12_S_atoms_outE (auto-import)
Info: resolving std::__timepunct_cache<char>::_S_timezones by linking
to __imp___ZNSt17__timepunct_cacheIcE12_S_timezonesE (auto-import)
Info: resolving std::basic_string<char, std::char_traits<char>,
std::allocator<char> >::_Rep::_S_empty_rep_storage by linking to
__imp___ZNSs4_Rep20_S_empty_rep_storageE (auto-import)
Info: resolving std::cerr by linking to __imp___ZSt4cerr (auto-import)
Info: resolving std::codecvt<char, char, _mbstate_t>::id by linking to
__imp___ZNSt7codecvtIcc10_mbstate_tE2idE (auto-import)
Info: resolving std::collate<char>::id by linking to
__imp___ZNSt7collateIcE2idE (auto-import)
Info: resolving std::cout by linking to __imp___ZSt4cout (auto-import)
Info: resolving std::ctype<char>::id by linking to
__imp___ZNSt5ctypeIcE2idE (auto-import)
Info: resolving std::money_base::_S_atoms by linking to
__imp___ZNSt10money_base8_S_atomsE (auto-import)
Info: resolving std::moneypunct<char, false>::id by linking to
__imp___ZNSt10moneypunctIcLb0EE2idE (auto-import)
Info: resolving std::moneypunct<char, true>::id by linking to
__imp___ZNSt10moneypunctIcLb1EE2idE (auto-import)
Info: resolving std::num_get<char, std::istreambuf_iterator<char,
std::char_traits<char> > >::id by linking to
__imp___ZNSt7num_getIcSt19istreambuf_iteratorIcSt11char_traitsIcEEE2idE
(auto-import)
Info: resolving std::num_put<char, std::ostreambuf_iterator<char,
std::char_traits<char> > >::id by linking to
__imp___ZNSt7num_putIcSt19ostreambuf_iteratorIcSt11char_traitsIcEEE2idE
(auto-import)
Info: resolving std::numpunct<char>::id by linking to
__imp___ZNSt8numpunctIcE2idE (auto-import)
Info: resolving std::time_get<char, std::istreambuf_iterator<char,
std::char_traits<char> > >::id by linking to
__imp___ZNSt8time_getIcSt19istreambuf_iteratorIcSt11char_traitsIcEEE2idE
(auto-import)
Info: resolving std::tr1::__detail::__prime_list by linking to
__imp___ZNSt3tr18__detail12__prime_listE (auto-import)
Finally, I've appended a copy of my work in progress patch for information.
It's a hideous mess, it even has debugging printfs and #if 1s, so it's not
a submission, and I don't want a proper review. What I would find helpful
is if anyone more experienced than me could cast an eye over the code in
sec_copier, where it creates a new BFD and adds an empty section to it in
order to add the COMDAT section from an input BFD to the import library
archive. I think I've done the right thing in copying over all the private
data and arch/mach/format/flags and calling bfd_make_readable at the end,
but this is one of those bits of BFD that is a bit underspecified and
underdocumented and you only learn by bitter experience which bits are
necessary and which are possibly a bad idea, so I'd appreciate any input on
that front, plus general comments on the design approach I've taken to the
task; before I go any further, I need to know if I'm barking up completely
the wrong tree!
cheers,
DaveK
--
In place of a witty .sigline, here is a summary and patch. It's probably
waaay too long for demon.local :)
To summarise how it works: after generating the import stubs for the import
library, iterate across all the input BFDs, and for each one scan all its
symbols. If the symbol looks like the mangled form of one of the class data
items we're concerned with adding as COMDAT sections in the import library,
mark the section of the BFD that the symbol refers to for copying. After
scanning the BFD, iterate over all sections in it, calling sec_copier to
copy the ones we marked when scanning the symbol table. For each marked
section, sec_copier creates a new BFD with an empty section, both based on
the input BFD and section. Copy across the section contents wholesale from
the input section to the new section in the new BFD. Create copies of all
the symbols from the input BFD that refer to the section being copied and
add them to the new BFDs symbol table. Create copies of all the relocs from
the input section, and adjust them so they point to the symbols in the new
BFD's table; this initially only contains symbols from within the section
itself, if a reloc is encountered based on a symbol that points outside the
section it is copied and added to the new BFD's symbol table as an undef.
The new BFD created is linked into the chain of BFDs for the import library
archive and made readable; we then return to iterating across the sections
of the current input BFD and across input BFDs.
Index: ld/pe-dll.c
===================================================================
RCS file: /cvs/src/src/ld/pe-dll.c,v
retrieving revision 1.109
diff -p -u -r1.109 pe-dll.c
--- ld/pe-dll.c 29 Sep 2008 14:01:50 -0000 1.109
+++ ld/pe-dll.c 26 Nov 2008 15:23:13 -0000
@@ -27,7 +27,7 @@
#include "safe-ctype.h"
#include <time.h>
-
+#include <errno.h>
#include "ld.h"
#include "ldexp.h"
#include "ldlang.h"
@@ -354,6 +354,10 @@ static const autofilter_entry_type autof
/* Don't export section labels or artificial symbols
(eg ".weak.foo". */
{ STRING_COMMA_LEN (".") },
+ /* Don't export c++ typeinfo/vtbl. */
+ { STRING_COMMA_LEN ("_ZTV") },
+ { STRING_COMMA_LEN ("_ZTI") },
+ { STRING_COMMA_LEN ("_ZTS") },
{ NULL, 0 }
};
@@ -465,6 +469,16 @@ static bfd_boolean
is_import (const char* n)
{
return (CONST_STRNEQ (n, "__imp_"));
+}
+
+/* abfd is a bfd containing n (or NULL)
+ It can be used for contextual checks. */
+
+static int
+is_typeinfo (const char *n)
+{
+ return (n[0] == '_') && (n[1] == 'Z') && (n[2] == 'T')
+ && ((n[3] == 'V') || (n[3] == 'I') || (n[3] == 'S'));
}
/* abfd is a bfd containing n (or NULL)
@@ -2410,16 +2424,289 @@ pe_create_import_fixup (arelent *rel, as
einfo ("%X");
}
}
-}
-
+}
+
+static void
+sec_counter (bfd *abfd, asection *sect, void *obj)
+{
+ int *count = (int *)obj;
+ (*count)++;
+ abfd = abfd;
+ sect = sect;
+}
+
+struct symhash_entry
+ {
+ struct bfd_hash_entry root;
+ asymbol *oldsym;
+ asymbol **newsym;
+ };
+
+struct copy_typeinfo_sec_args
+ {
+ char *secmarks;
+ bfd *head;
+ unsigned int *secnsyms;
+ unsigned int *secsymheads;
+ unsigned int *symchains;
+ asymbol **symbols;
+ int nsyms;
+ struct bfd_hash_table symhash;
+ };
+
+static struct bfd_hash_entry *
+symhash_new (struct bfd_hash_entry *entry,
+ struct bfd_hash_table *table,
+ const char *string)
+{
+ struct symhash_entry *ret = (struct symhash_entry *) entry;
+
+ /* Allocate the structure if it has not already been allocated by a
+ derived class. */
+ if (ret == NULL)
+ {
+ ret = bfd_hash_allocate (table, sizeof (* ret));
+ if (ret == NULL)
+ return NULL;
+ }
+
+ /* Call the allocation method of the base class. */
+ ret = (struct symhash_entry *)
+ bfd_hash_newfunc ((struct bfd_hash_entry *) ret, table, string);
+
+ /* Initialize the local fields here. */
+
+ return (struct bfd_hash_entry *) ret;
+}
+
+static struct symhash_entry *
+symhash_lookup (struct bfd_hash_table *table,
+ const char *string,
+ bfd_boolean create,
+ bfd_boolean copy)
+{
+ return (struct symhash_entry *) bfd_hash_lookup (table, string,
create, copy);
+}
+
+static void
+sec_copier (bfd *abfd, asection *sect, void *obj)
+{
+ struct copy_typeinfo_sec_args *args = (struct copy_typeinfo_sec_args *)obj;
+ bfd *newbfd;
+ arelent **relpp;
+ asection *osection;
+ long relcount, i;
+ long relsize;
+ bfd_size_type size;
+
+ if (!args->secmarks[sect->index])
+ return;
+
+ /* Section is marked for copying. Create a new bfd,
+ copy section contents to new section templated on this
+ section, copy section relocs across, then we need to
+ copy all syms that ref this section. D'oh, quadratic
+ behaviour - unless we were to build a list of the syms
+ that each section has as we go along. */
+ printf ("copy section #%d: %s\n", sect->index, sect->name);
+ abfd = abfd;
+
+ /* dump syms. */
+ unsigned int sychptr = args->secsymheads[sect->index];
+ printf ("%d syms: [idx %d]\n", args->secnsyms[sect->index], sect->index);
+ while (~sychptr)
+ {
+ printf ("with sym %d = '%s' %p\n", sychptr,
args->symbols[sychptr]->name, args->symbols[sychptr]->section);
+ sychptr = args->symchains[sychptr];
+ }
+
+ /* So: make new writeable bfd based on old one. */
+ size = bfd_get_section_size (sect);
+ newbfd = bfd_create (sect->name, abfd);
+ bfd_make_writable (newbfd);
+ //bfd_set_file_flags (newbfd, bfd_get_file_flags (abfd));
+ bfd_set_arch_mach (newbfd, bfd_get_arch (abfd), bfd_get_mach (abfd));
+ bfd_set_format (newbfd, bfd_get_format (abfd));
+ bfd_copy_private_bfd_data (abfd, newbfd);
+ bfd_copy_private_header_data (abfd, newbfd);
+
+ /* Create an output section. */
+ osection = bfd_make_section_with_flags (newbfd, sect->name, sect->flags);
+ if (!osection)
+ {
+ /* Already got one - multiple comdats, ignore dups. */
+ return;
+ }
+ bfd_copy_private_section_data (abfd, sect, newbfd, osection);
+ bfd_set_section_size (newbfd, osection, size);
+ osection->output_section = osection;
+ osection->output_offset = 0;
+
+ /* Roughly what objcopy -j does. */
+ relsize = bfd_get_reloc_upper_bound (abfd, sect);
+ if (relsize < 0)
+ {
+ /* Do not complain if the target does not support relocations. */
+ if (relsize == -1 && bfd_get_error () == bfd_error_invalid_operation)
+ relsize = 0;
+ else
+ {
+ //status = 1;
+ //bfd_nonfatal_message (NULL, abfd, sect, NULL);
+ printf ("bad relocs\n");
+ return;
+ }
+ }
+
+ relcount = 0;
+ relpp = NULL;
+ if (relsize == 0)
+ bfd_set_reloc (newbfd, osection, NULL, 0);
+ else
+ {
+ relpp = xmalloc (relsize);
+ relcount = bfd_canonicalize_reloc (abfd, sect, relpp, args->symbols);
+ if (relcount < 0)
+ {
+ printf ("bad relocs2\n");
+ //status = 1;
+ //bfd_nonfatal_message (NULL, abfd, sect,
+ //_("relocation count is negative"));
+ return;
+ }
+#if 1
+ /* Copy relocs to new array. */
+ arelent *newrels = xmalloc (relcount * sizeof *newrels);
+ for (i = 0; i < relcount; i++)
+ {
+ newrels[i] = (*relpp[i]);
+ relpp[i] = &newrels[i];
+ }
+#endif
+ }
+
+ if (bfd_get_section_flags (abfd, sect) & SEC_HAS_CONTENTS)
+ {
+ void *memhunk = xmalloc (size);
+ if (!bfd_get_section_contents (abfd, sect, memhunk, 0, size))
+ {
+ //status = 1;
+ //bfd_nonfatal_message (NULL, abfd, sect, NULL);
+ bfd_perror ("bad get sec\n");
+ return;
+ }
+
+ if (!bfd_set_section_contents (newbfd, osection, memhunk, 0, size))
+ {
+ //status = 1;
+ bfd_perror ("bad set sec \n");
+ //bfd_nonfatal_message (NULL, newbfd, osection, NULL);
+ return;
+ }
+ free (memhunk);
+ }
+
+ /* Finally symtab Oversize it by as much as we could possibly need. */
+ asymbol **symtab = xmalloc ((relcount +
args->secnsyms[sect->index]) * sizeof *symtab);
+ asymbol **ptr = symtab;
+ sychptr = args->secsymheads[sect->index];
+ bfd_hash_table_init (&args->symhash, symhash_new, 0);
+ while (~sychptr)
+ {
+ /* Must copy the sym, . Must handle also all syms ref'd by
+ relocs, make it undef if not in sect. */
+ asymbol *copysym = bfd_make_empty_symbol (newbfd);
+ copysym->name = xstrdup (args->symbols[sychptr]->name);
+ copysym->value = args->symbols[sychptr]->value;
+ copysym->flags = args->symbols[sychptr]->flags;
+ copysym->section = osection;
+ bfd_copy_private_symbol_data (abfd, args->symbols[sychptr],
newbfd, copysym);
+ struct symhash_entry *newent = symhash_lookup (&args->symhash,
copysym->name, TRUE, TRUE);
+ newent->oldsym = args->symbols[sychptr];
+ newent->newsym = ptr;
+ *ptr++ = copysym;
+ sychptr = args->symchains[sychptr];
+ }
+
+ if (relsize != 0)
+ {
+ arelent **rel;
+ for (i = 0, rel = relpp; i < relcount; i++, rel++)
+ {
+ /* Look up sym_sym_ptr in our new sym tab and switch it
+ over. Create a new undef'd sym if it's not in our section. */
+ asymbol *symbol = *(*rel)->sym_ptr_ptr;
+ struct symhash_entry *ent = symhash_lookup (&args->symhash,
symbol->name, FALSE, FALSE);
+ int our_sect = (symbol->section == sect);
+ printf ("rel #%ld: sym name '%s', sym sect '%s'\n", i,
symbol->name, symbol->section->name);
+ printf ("our_sect? %d ent: %p\n", our_sect, ent);
+ if (!our_sect)
+ {
+ if (!ent)
+ {
+ struct symhash_entry *newent = symhash_lookup (&args->symhash,
symbol->name, TRUE, TRUE);
+ asymbol *copysym = bfd_make_empty_symbol (newbfd);
+ copysym->name = xstrdup (symbol->name);
+ copysym->flags = symbol->flags;
+ copysym->section = bfd_und_section_ptr; // osection; // no,
undefined section.
+ bfd_copy_private_symbol_data (abfd, symbol, newbfd, copysym);
+ newent->newsym = ptr;
+ newent->oldsym = symbol;
+ *ptr++ = copysym;
+ (*rel)->sym_ptr_ptr = newent->newsym;
+ }
+ else
+ {
+ if (symbol != ent->oldsym)
+ printf ("WTF!? %p != %p\n", symbol, ent->oldsym);
+ (*rel)->sym_ptr_ptr = ent->newsym;
+ }
+ }
+ else
+ {
+ /* In our section, so must already have been copied on symchain */
+ (*rel)->sym_ptr_ptr = ent->newsym;
+ }
+ }
+
+ /* Rebase all reloc sym ptrs to copied syms. */
+#if 1-1
+ bfd_set_reloc (newbfd, osection, NULL, 0);
+#else
+ bfd_set_reloc (newbfd, osection, relcount == 0 ? NULL : relpp, relcount);
+ if (relcount == 0)
+ free (relpp);
+#endif
+ }
+
+ bfd_set_symtab (newbfd, symtab, (ptr - symtab));
+
+ /* To really be ideal we'd also copy debug types. */
+ printf ("insterting newbfd '%s'\n", newbfd->filename);
+ /* Don't need the hash table any more. */
+ bfd_hash_table_free (&args->symhash);
+#if 1
+ /* Add it to archive! */
+ newbfd->archive_next = args->head;
+ args->head = newbfd;
+ /* Write it out! */
+ if (!bfd_make_readable (newbfd))
+ {
+ printf ("ERR MAKE READABLE %d/%d\n", bfd_get_error(), errno);
+ }
+#else
+ bfd_close (newbfd);
+#endif
+}
void
-pe_dll_generate_implib (def_file *def, const char *impfilename)
+pe_dll_generate_implib (def_file *def, const char *impfilename,
struct bfd_link_info *info)
{
- int i;
+ int i, j;
bfd *ar_head;
bfd *ar_tail;
- bfd *outarch;
+ bfd *outarch;
+ bfd *b;
bfd *head = 0;
dll_filename = (def->name) ? def->name : dll_name;
@@ -2464,7 +2751,108 @@ pe_dll_generate_implib (def_file *def, c
head = n;
def->exports[i].internal_name = internal;
}
-
+
+ /* C++ typeinfo needs special handling. We must place the
+ comdat sections wholesale into the import library. Iterate
+ over all input bfds. */
+ for (b = info->input_bfds; b; b = b->link_next)
+ {
+ asymbol **symbols;
+ int nsyms;
+ struct copy_typeinfo_sec_args args;
+ char *secmarks;
+ unsigned int *secsymheads;
+ unsigned int *symchains;
+ unsigned int *secnsyms;
+ int nsecs = 0;
+
+ /* Count all sections. */
+ bfd_map_over_sections (b, sec_counter, &nsecs);
+
+ /* Make an array of flags for them. */
+ secmarks = xmalloc (nsecs * sizeof *secmarks);
+ memset (secmarks, 0, nsecs);
+
+ /* Read in symbols. */
+ if (!bfd_generic_link_read_symbols (b))
+ {
+ einfo (_("%B%F: could not read symbols: %E\n"), b);
+ break;
+ }
+
+ /* Iterate over all symbols, marking typeinfo sections for copy.
+ We also build link chains of symbols per section to speed up
+ finding them again when copying sections. */
+ symbols = bfd_get_outsymbols (b);
+ nsyms = bfd_get_symcount (b);
+
+ /* Allocate sym tracking stuff. */
+ secsymheads = xmalloc (nsecs * sizeof *secsymheads);
+ memset (secsymheads, ~0u, nsecs * sizeof *secsymheads);
+ secnsyms = xmalloc (nsecs * sizeof *secnsyms);
+ memset (secnsyms, 0, nsecs * sizeof *secnsyms);
+ symchains = xmalloc (nsyms * sizeof *symchains);
+ memset (symchains, ~0u, nsyms * sizeof *symchains);
+
+ for (j = 0; j < nsyms; j++)
+ {
+ /* Add sym to chain for its section. */
+ ASSERT(symbols[j]->section->index < nsecs);
+ ASSERT(symbols[j]->section->index >= 0);
+ if (!~secsymheads[symbols[j]->section->index])
+ {
+ secsymheads[symbols[j]->section->index] = j;
+ symchains[j] = ~0u;
+ ASSERT(!secnsyms[symbols[j]->section->index]);
+ secnsyms[symbols[j]->section->index] = 1;
+ }
+ else
+ {
+ symchains[j] = secsymheads[symbols[j]->section->index];
+ secsymheads[symbols[j]->section->index] = j;
+ secnsyms[symbols[j]->section->index]++;
+ }
+
+ /* Check if exportable typeinfo. */
+ if (symbols[j]->section != &bfd_und_section
+ && ((symbols[j]->flags & BSF_GLOBAL)
+ || (symbols[j]->flags == BFD_FORT_COMM_DEFAULT_VALUE)))
+ {
+ const char *sn = symbols[j]->name;
+ if (pe_details->underscored && *sn == '_')
+ sn++;
+ if (is_typeinfo (sn))
+ {
+ secmarks[symbols[j]->section->index] = 1;
+ ASSERT(symbols[j]->section->flags & SEC_LINK_ONCE);
+ }
+ }
+ }
+
+ /* Now we know which sections we want, copy each to a new bfd along
+ with its relocs and any symbols referring to it, and link them into
+ the archive. */
+ printf ("bfd: %s, secmarks:\n", b->filename);
+ for (j = 0; j < nsecs; j++)
+ putc (secmarks[j] ? '+' : '.', stdout);
+ putc ('\n', stdout);
+
+ args.secmarks = secmarks;
+ args.head = head;
+ args.secsymheads = secsymheads;
+ args.secnsyms = secnsyms;
+ args.symchains = symchains;
+ args.symbols = symbols;
+ args.nsyms = nsyms;
+ bfd_map_over_sections (b, sec_copier, &args);
+ head = args.head;
+
+ /* Finally we can free the section marks and co. */
+ free (secmarks);
+ free (secsymheads);
+ free (symchains);
+ }
+
ar_tail = make_tail (outarch);
if (ar_head == NULL || ar_tail == NULL)
Index: ld/pe-dll.h
===================================================================
RCS file: /cvs/src/src/ld/pe-dll.h,v
retrieving revision 1.16
diff -p -u -r1.16 pe-dll.h
--- ld/pe-dll.h 6 Jul 2007 14:09:42 -0000 1.16
+++ ld/pe-dll.h 26 Nov 2008 15:23:13 -0000
@@ -43,7 +43,7 @@ extern void pe_dll_add_excludes
extern void pe_dll_generate_def_file
(const char *);
extern void pe_dll_generate_implib
- (def_file *, const char *);
+ (def_file *, const char *, struct bfd_link_info *);
extern void pe_process_import_defs
(bfd *, struct bfd_link_info *);
extern bfd_boolean pe_implied_import_dll
Index: ld/emultempl/pe.em
===================================================================
RCS file: /cvs/src/src/ld/emultempl/pe.em,v
retrieving revision 1.136
diff -p -u -r1.136 pe.em
--- ld/emultempl/pe.em 4 Oct 2008 06:08:59 -0000 1.136
+++ ld/emultempl/pe.em 26 Nov 2008 15:23:15 -0000
@@ -1572,7 +1572,7 @@ gld_${EMULATION_NAME}_finish (void)
{
pe_dll_fill_sections (link_info.output_bfd, &link_info);
if (pe_implib_filename)
- pe_dll_generate_implib (pe_def_file, pe_implib_filename);
+ pe_dll_generate_implib (pe_def_file, pe_implib_filename, &link_info);
}
#if defined(TARGET_IS_shpe) || defined(TARGET_IS_mipspe)
/* ARM doesn't need relocs. */