This is the mail archive of the gcc@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: Duplicate data objects in shared libraries


Let me try to summarize the discussion:

The semantics of existing SVR4 dynamic linkers are such that:
   IF two shared objects A.so and B.so link against the same shared 
     library C.so, AND
   both override the same symbol S, AND
   A.so and B.so are loaded in that order with RTLD_LOCAL, THEN
   all references to S from A.so and C.so will resolve to the copy from
     A.so, but references from B.so will resolve to the copy from B.so.

This is true because:
   There is only one copy of C.so loaded, and its relocs are only resolved
     once, AND
   the definitions from A.so and C.so are not visible when loading B.so.

This breaks RTTI matching, which relies on all references within a
program resolving to the same copy.  Since references from B.so and C.so
differ, this premise is violated.

It is generally agreed that this is unfortunate.  Yes?

Various solutions present themselves.  Most basically, they break down to:

1) Change the dynamic linker so that B.so and C.so agree, AND/OR
2) Change the runtime so that it doesn't matter if they don't agree.

#1 seems desirable for other symbols, too; it seems broken for a shared
object to have a different idea of what a symbol means from one of its
dependencies.  Anything which uses global data is vulnerable to being
broken by this.  However, it may not be a complete solution, as we may not
be able to implement it for SVR4 platforms other than Linux/GNU; certainly
not in an interesting time frame.

#2 has the advantages of being simple to implement and applicable to all
targets.  On the other hand, as Martin has pointed out, the more
conservative comparison is slower.  I would be interested to see actual
numbers to quantify this.  Volunteer?

#2 is also not a complete solution, as it would only solve the problem for
RTTI nodes.  A template library with, say, an allocator pool referenced
through a static data member would have the same problem unless the library
author is careful to ensure that the pool is only defined in the library,
not in any client .o's.

On the other hand, as David has argued, other affected constructs can be
controlled by the user; type_info nodes are emitted everywhere.  This is
true, but is a bug.  The type_info node should only be emitted with the
vtable if there is one.  As a result, they can be controlled about to the
same degree as any other static data.  However, they are much more common
than static data such as the above, so managing them is much more of a
hassle.

Possible implementations of #1:
3) If a library needed by an RTLD_LOCAL object is already loaded, ignore it
   and map a new copy.  As an optimization, only do this if it refers to
   symbols defined by the RTLD_LOCAL object.
4) If a library needed by an RTLD_LOCAL object is already loaded, force the
   library to RTLD_GLOBAL status so that references from B.so will use the
   already-resolved definition.

I think #3 is philosophically cleaner.

Have I missed any arguments?

I am in favor of doing #1 and neutral to positive on #2.  As a possible
point for further discussion, here is an unofficial patch I whipped up a
week or so ago to do #2 iff -fpic.  YMMV.

Jason

*** ./libsupc++/tinfo.cc.~1~	Sat May 18 16:11:27 2002
--- ./libsupc++/tinfo.cc	Sun May 12 13:57:20 2002
*************** std::type_info::
*** 43,58 ****
  std::bad_cast::~bad_cast() throw() { }
  std::bad_typeid::~bad_typeid() throw() { }
  
- #if !__GXX_MERGED_TYPEINFO_NAMES
- 
- // We can't rely on common symbols being shared between shared objects.
  bool std::type_info::
  operator== (const std::type_info& arg) const
  {
!   return (&arg == this) || (__builtin_strcmp (name (), arg.name ()) == 0);
! }
! 
  #endif
  
  namespace std {
  
--- 43,60 ----
  std::bad_cast::~bad_cast() throw() { }
  std::bad_typeid::~bad_typeid() throw() { }
  
  bool std::type_info::
  operator== (const std::type_info& arg) const
  {
!   bool ret = (name() == arg.name());
! #if !__GXX_MERGED_TYPEINFO_NAMES
!   // In old abi, or when weak symbols are not supported, there can
!   // be multiple instances of a type_info object for one
!   // type. Uniqueness must use the _name value, not object address.
!   ret = (ret || __builtin_strcmp (name (), arg.name ()) == 0);
  #endif
+   return ret;
+ }
  
  namespace std {
  
*** ./libsupc++/tinfo.h.~1~	Sat May 18 16:11:27 2002
--- ./libsupc++/tinfo.h	Sun May 12 13:57:34 2002
***************
*** 8,10 ****
--- 8,20 ----
  // Class declarations shared between the typeinfo implementation files.
  
  #include <cxxabi.h>
+ 
+ #if !__GXX_WEAK__ || defined(__PIC__)
+   // If weak symbols are not supported, typeinfo names are not merged.
+   // Also don't rely on this if building a shared library, as multiple
+   // clients might try to use us.
+   #define __GXX_MERGED_TYPEINFO_NAMES 0
+ #else
+   // On platforms that support weak symbols, typeinfo names are merged.
+   #define __GXX_MERGED_TYPEINFO_NAMES 1
+ #endif
*** ./libsupc++/tinfo2.cc.~1~	Sat May 18 16:11:27 2002
--- ./libsupc++/tinfo2.cc	Sun May 12 13:57:07 2002
*************** extern "C" void abort ();
*** 38,52 ****
  
  using std::type_info;
  
- #if !__GXX_MERGED_TYPEINFO_NAMES
- 
  bool
  type_info::before (const type_info &arg) const
  {
    return __builtin_strcmp (name (), arg.name ()) < 0;
- }
- 
  #endif
  
  #include <cxxabi.h>
  
--- 38,54 ----
  
  using std::type_info;
  
  bool
  type_info::before (const type_info &arg) const
  {
+ #if __GXX_MERGED_TYPEINFO_NAMES
+   // In new abi we can rely on type_info's NTBS being unique,
+   // and therefore address comparisons are sufficient.
+   return name() < arg.name();
+ #else
    return __builtin_strcmp (name (), arg.name ()) < 0;
  #endif
+ }
  
  #include <cxxabi.h>
  
*************** __pointer_catch (const __pbase_type_info
*** 164,167 ****
    return __pbase_type_info::__pointer_catch (thrown_type, thr_obj, outer);
  }
  
! } // namespace std
--- 166,169 ----
    return __pbase_type_info::__pointer_catch (thrown_type, thr_obj, outer);
  }
  
! } // namespace __cxxabiv1
*** ./libsupc++/typeinfo.~1~	Sat May 18 16:11:27 2002
--- ./libsupc++/typeinfo	Sun May 12 13:43:25 2002
*************** namespace __cxxabiv1
*** 44,57 ****
    class __class_type_info;
  } // namespace __cxxabiv1
  
- #if !__GXX_WEAK__
-   // If weak symbols are not supported, typeinfo names are not merged.
-   #define __GXX_MERGED_TYPEINFO_NAMES 0
- #else
-   // On platforms that support weak symbols, typeinfo names are merged.
-   #define __GXX_MERGED_TYPEINFO_NAMES 1
- #endif
- 
  namespace std 
  {
    /** The @c type_info class describes type information generated by
--- 44,49 ----
*************** namespace std 
*** 84,105 ****
      const char* name() const
      { return __name; }
  
- #if !__GXX_MERGED_TYPEINFO_NAMES
-     bool before(const type_info& __arg) const;
-     // In old abi, or when weak symbols are not supported, there can
-     // be multiple instances of a type_info object for one
-     // type. Uniqueness must use the _name value, not object address.
-     bool operator==(const type_info& __arg) const;
- #else
      /** Returns true if @c *this precedes @c __arg in the implementation's
       *  collation order.  */
!     // In new abi we can rely on type_info's NTBS being unique,
!     // and therefore address comparisons are sufficient.
!     bool before(const type_info& __arg) const
!     { return __name < __arg.__name; }
!     bool operator==(const type_info& __arg) const
!     { return __name == __arg.__name; }
! #endif
      bool operator!=(const type_info& __arg) const
      { return !operator==(__arg); }
      
--- 76,85 ----
      const char* name() const
      { return __name; }
  
      /** Returns true if @c *this precedes @c __arg in the implementation's
       *  collation order.  */
!     bool before(const type_info& __arg) const;
!     bool operator==(const type_info& __arg) const;
      bool operator!=(const type_info& __arg) const
      { return !operator==(__arg); }
      

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]