[LONG] RF{C,D,A,H}: pe-x86 DLLs (shared libstdc++ and others) vs C++ RTTI vs LD

classic Classic list List threaded Threaded
6 messages Options
Reply | Threaded
Open this post in threaded view
|

[LONG] RF{C,D,A,H}: pe-x86 DLLs (shared libstdc++ and others) vs C++ RTTI vs LD

Dave Korn-6
 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.  */
Reply | Threaded
Open this post in threaded view
|

Re: [LONG] RF{C,D,A,H}: pe-x86 DLLs (shared libstdc++ and others) vs C++ RTTI vs LD

Brian Dessent
Dave Korn wrote:

> 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.
> ...
> 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.

But when --e-a-i is specified the special .xa version of the linker
script is used instead of the default one, which casuses .rdata to be
put into .data making it writable.  And regardless of whether --e-a-i
was specified, the presence of an auto-import site in .text
automatically causes the linker to emit .text with write permission.  So
whatever the reason for the crash during process loading, it shouldn't
be due to trying to write to a readonly page -- if it is, then that's
the bug to fix.

Can you find out more detail of the STATUS_ACCESS_EXCEPTION?  In the
past I've used ollydbg for this as it makes it pretty easy to determine
the precise address that NTLDR was trying to modify at the time of the
fault, and from there you can figure out what section that corresponds
to.

> They both fail when trying to throw an object based on a string :-(

In both of these cases, the fault seems to be a NULL pointer dereference
in get_adjusted_ptr, at the line:

  if (catch_type->__do_catch (throw_type, &thrown_ptr, 1))

In both of your backtraces, catch_type is 0x409..., i.e. this is the
typeinfo object from the .text section of the executable that got
statically linked in from the import lib, not the typeinfo object in the
DLL.  The __do_catch method is a virtual method of std::type_info, and
it appears that in this second copy the vtable is never initialized
(rightfully, as the DLL has no idea of its existance), and thus the NULL
dereference.  I really think having multiple copies of the typeinfo is a
bad path to go down.

> 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

That's interesting to know, as I thought that pointer comparisons for
rtti was the only option.   Is __GXX_MERGED_TYPEINFO_NAMES correctly
being set to 0 (and __GXX_TYPEINFO_EQUALITY_INLINE to 0 as well it would
seem) for this target then?

> 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.

I think that auto-import should be the answer here.  The comment in
winnt-cxx.c is correct insomuch as explicit dllimport isn't possible
because then it can't be used as a static initializer, but leaving off
dllimport and relying on auto-import is OK for static initializers (just
so long as they are made writable, which --e-a-i does), so it should be
fine.  Perhaps the comment was written before the feature was added to
the linker ca. 2001-08-02.

> 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:

For the purposes of the dejagnu testsuite, either -Wl,--e-a-i needs to
be added to the flags for the C++ tests, or the harness needs to be
taught to ignore the Info: lines.

But outside the testsuite, if the user gets all this noise when linking
normal C++ code it's a quality issue.  I think this really speaks more
to the general problem of this option's very existance than anything
else.  In my ideal world, the linker should be smart enough to do the
right thing with neither a bunch informational spew nor user
intervention.  That means if it processes an auto-import in .rdata, it
should be smart enough to mark the section writable without the user
needing to specify a flag.  At the same time, you don't want to
pessimise every program by eliminating the optimization of having
readonly data in a sharable segment, so you also don't want to just
unconditionally turn on -Wl,--e-a-i in the g++ driver specs either.

> 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!

I'd really like to know why auto-import is failing before going down
that road.

Brian
Reply | Threaded
Open this post in threaded view
|

Re: [LONG] RF{C,D,A,H}: pe-x86 DLLs (shared libstdc++ and others) vs C++ RTTI vs LD

Danny Smith-5
In reply to this post by Dave Korn-6
On Sun, Nov 30, 2008 at 2:17 PM, Dave Korn
<[hidden email]> wrote:

>  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. ]
>

If I remember correctly,  there were no problems with *user-defined*
types and TI tables (with above  _GLIBCXX_IMPORT additions).  The
problems arose from *language-defined*  types with TI tables generated
in  libsupc++'s tinfo.cc  and tinfo2.cc.  Excluding those 2 objects
from libstdc++.dll and adding them as statically-linked objects to the
libstdc++.a  import lib allowed the comdat linkage (linkonce)
mechanism to work.

Danny
Reply | Threaded
Open this post in threaded view
|

Re: [LONG] RF{C,D,A,H}: pe-x86 DLLs (shared libstdc++ and others) vs C++ RTTI vs LD

Dave Korn-6
In reply to this post by Brian Dessent
Brian Dessent wrote:
> Dave Korn wrote:
>
>> 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.
>> ...

> But when --e-a-i is specified the special .xa version of the linker
> script is used instead of the default one, which casuses .rdata to be
> put into .data making it writable.  And regardless of whether --e-a-i
> was specified, the presence of an auto-import site in .text
> automatically causes the linker to emit .text with write permission.  So
> whatever the reason for the crash during process loading, it shouldn't
> be due to trying to write to a readonly page -- if it is, then that's
> the bug to fix.
>
> Can you find out more detail of the STATUS_ACCESS_EXCEPTION?  In the
> past I've used ollydbg for this as it makes it pretty easy to determine
> the precise address that NTLDR was trying to modify at the time of the
> fault, and from there you can figure out what section that corresponds
> to.

  Mea culpa.  I bodged something up in my testing, it seems; in any case,
after a fresh rebuild I can no longer reproduce the problem in that using
--enable-auto-import now works as expected and prevents the crash from
happening.  Duh me.

  FWIW, I investigated what was causing the access exception (without
-e-a-i) using WinDbg, and found it was a DWORD-sized move (apparently from
__imp___ZTISt11logic_error?  register trace is confusing) to
__fu0___ZTISt11logic_error.  So in this case the warning from ld isn't
useless noise by any means, it's very real and indicates a problem.

0:000> g
(5c8.5ec): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=6c4ddef0 ebx=00000000 ecx=6c4d0498 edx=00409268 esi=6c4dc000 edi=6c4e05a0
eip=77f92b57 esp=0022f858 ebp=0022f888 iopl=0         nv up ei ng nz na po cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010283
ntdll!NtQueryEvent+0x2fa:
*** ERROR: Module load completed but symbols could not be loaded for
image00400000
77f92b57 890a            mov     dword ptr [edx],ecx  ds:0023:00409268=0040b1b4

0:000> !vadump

BaseAddress:       00400000
RegionSize:        00001000
State:             00001000  MEM_COMMIT
Protect:           00000002  PAGE_READONLY
Type:              01000000  MEM_IMAGE

BaseAddress:       00401000
RegionSize:        00006000
State:             00001000  MEM_COMMIT
Protect:           00000080  PAGE_EXECUTE_WRITECOPY
Type:              01000000  MEM_IMAGE

BaseAddress:       00407000
RegionSize:        00001000
State:             00001000  MEM_COMMIT
Protect:           00000008  PAGE_WRITECOPY
Type:              01000000  MEM_IMAGE

BaseAddress:       00408000
RegionSize:        00002000
State:             00001000  MEM_COMMIT
Protect:           00000002  PAGE_READONLY
Type:              01000000  MEM_IMAGE

BaseAddress:       0040a000
RegionSize:        00001000
State:             00001000  MEM_COMMIT
Protect:           00000008  PAGE_WRITECOPY
Type:              01000000  MEM_IMAGE

BaseAddress:       0040b000
RegionSize:        00001000
State:             00001000  MEM_COMMIT
Protect:           00000004  PAGE_READWRITE
Type:              01000000  MEM_IMAGE


Sections:
Idx Name          Size      VMA       LMA       File off  Algn
  0 .text         00005b5c  00401000  00401000  00000400  2**4
                  CONTENTS, ALLOC, LOAD, CODE, DATA
  1 .data         00000830  00407000  00407000  00006000  2**5
                  CONTENTS, ALLOC, LOAD, DATA
  2 .rdata        00001ab4  00408000  00408000  00006a00  2**5
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  3 .bss          00000060  0040a000  0040a000  00000000  2**3
                  ALLOC
  4 .idata        00000688  0040b000  0040b000  00008600  2**2
                  CONTENTS, ALLOC, LOAD, DATA


[1357](sec  3)(fl 0x00)(ty   0)(scl   2) (nx 0) 0x00001268
__fu0___ZTISt11logic_error

[ 94](sec  5)(fl 0x00)(ty   0)(scl   3) (nx 0) 0x000001b4 .idata$5
[1423](sec  5)(fl 0x00)(ty   0)(scl   2) (nx 0) 0x000001b4
__imp___ZTISt11logic_error
[1429](sec  5)(fl 0x00)(ty   0)(scl   2) (nx 0) 0x000001b4
__imp___ZTISt11logic_error


> dereference.  I really think having multiple copies of the typeinfo is a
> bad path to go down.

  I'm not entirely sure.  You saw Danny's reply; I'm testing that now and
putting the typeinfo into libstdc++.dll.a has fixed
20_util/shared_ptr/thread/default_weaktoshared.cc.

>> 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
>
> That's interesting to know, as I thought that pointer comparisons for
> rtti was the only option.   Is __GXX_MERGED_TYPEINFO_NAMES correctly
> being set to 0 (and __GXX_TYPEINFO_EQUALITY_INLINE to 0 as well it would
> seem) for this target then?

  Had to do a rebuild to get that right, but it is now.

> I think that auto-import should be the answer here.

  I now agree fully.

> For the purposes of the dejagnu testsuite, either -Wl,--e-a-i needs to
> be added to the flags for the C++ tests, or the harness needs to be
> taught to ignore the Info: lines.

  I chose the first, as there are genuine failures caused by not using
-e-a-i, so ignoring the spew isn't enough.

> But outside the testsuite, if the user gets all this noise when linking
> normal C++ code it's a quality issue.  I think this really speaks more
> to the general problem of this option's very existance than anything
> else.  In my ideal world, the linker should be smart enough to do the
> right thing with neither a bunch informational spew nor user
> intervention.  That means if it processes an auto-import in .rdata, it
> should be smart enough to mark the section writable without the user
> needing to specify a flag.  At the same time, you don't want to
> pessimise every program by eliminating the optimization of having
> readonly data in a sharable segment, so you also don't want to just
> unconditionally turn on -Wl,--e-a-i in the g++ driver specs either.

  Well, possibly it just needs to be taught what to do with the _fu_
fixups?  Could be as simple as a quick patch to the alternative link script, no?

    cheers,
      DaveK
Reply | Threaded
Open this post in threaded view
|

Re: [LONG] RF{C,D,A,H}: pe-x86 DLLs (shared libstdc++ and others) vs C++ RTTI vs LD

Dave Korn-6
In reply to this post by Danny Smith-5
Danny Smith wrote:

>
> If I remember correctly,  there were no problems with *user-defined*
> types and TI tables (with above  _GLIBCXX_IMPORT additions).  The
> problems arose from *language-defined*  types with TI tables generated
> in  libsupc++'s tinfo.cc  and tinfo2.cc.  Excluding those 2 objects
> from libstdc++.dll and adding them as statically-linked objects to the
> libstdc++.a  import lib allowed the comdat linkage (linkonce)
> mechanism to work.
>
> Danny

  Thanks Danny.  It looks like libsupc++ has been refactored a bit since
you looked at this?  There's next to nothing in tinfo.o and tinfo2.o:

/gnu/gcc/release/gcc4-4.3.2-2/build/i686-pc-cygwin/libstdc++-v3/libsupc++ $
nm tinfo.o | grep " [TR] " | cut -d' ' -f3 | c++filt
std::type_info::__do_catch(std::type_info const*, void**, unsigned int) const
std::type_info::__do_upcast(__cxxabiv1::__class_type_info const*, void**) const
std::type_info::__is_pointer_p() const
std::type_info::__is_function_p() const
std::type_info::operator==(std::type_info const&) const
std::type_info::~type_info()
std::type_info::~type_info()
std::type_info::~type_info()
typeinfo for std::type_info
typeinfo name for std::type_info
vtable for std::type_info

/gnu/gcc/release/gcc4-4.3.2-2/build/i686-pc-cygwin/libstdc++-v3/libsupc++ $
nm tinfo2.o | grep " [TR] " | cut -d' ' -f3 | c++filt
std::type_info::before(std::type_info const&) const

and there are all these other files with names like
"fundamental_type_info.o" with typeinfo for stuff like int scalars and so on.

  I can't exclude just the first two or the DLL won't link, but the group
of tinfo/tinfo2 and *type_info* seems to form a closed dependency group and
if I exclude them all from export and put them into the import library they
don't get pulled into the DLL's link.  Tests are still running but it solved
regression in 20_util/shared_ptr/thread/default_weaktoshared.cc so far.  I'm
sending a patch separately that adds an ld option to make this kind of
partitioning between DLL and import library easier to perform.

    cheers,
      DaveK
Reply | Threaded
Open this post in threaded view
|

Re: [LONG] RF{C,D,A,H}: pe-x86 DLLs (shared libstdc++ and others) vs C++ RTTI vs LD

Dave Korn-6
Dave Korn wrote:

> Danny Smith wrote:
>> If I remember correctly,  there were no problems with *user-defined*
>> types and TI tables (with above  _GLIBCXX_IMPORT additions).  The
>> problems arose from *language-defined*  types with TI tables generated
>> in  libsupc++'s tinfo.cc  and tinfo2.cc.  Excluding those 2 objects
>> from libstdc++.dll and adding them as statically-linked objects to the
>> libstdc++.a  import lib allowed the comdat linkage (linkonce)
>> mechanism to work.
>>
>> Danny
>
>   Thanks Danny.  It looks like libsupc++ has been refactored a bit since
> you looked at this?  There's next to nothing in tinfo.o and tinfo2.o:

> and there are all these other files with names like
> "fundamental_type_info.o" with typeinfo for stuff like int scalars and so on.
>
>   I can't exclude just the first two or the DLL won't link, but the group
> of tinfo/tinfo2 and *type_info* seems to form a closed dependency group and
> if I exclude them all from export and put them into the import library they
> don't get pulled into the DLL's link.  Tests are still running but it solved
> regression in 20_util/shared_ptr/thread/default_weaktoshared.cc so far.

  Nope, you were right in the first place: doing that fixed one case but
caused a couple of regressions in other (related) tests.  It looks like
linking tinfo/tinfo2 into the DLL but not exporting them, and placing them
into the import library does the right thing.

    cheers,
      DaveK