This is the mail archive of the gcc-patches@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]

C++/v3 PATCH to use strcmp for typeinfo comparison (the plugin problem, 1/2)


For a while now some uses of C++ plugins (shared objects loaded with RTLD_LOCAL) have been broken because of symbol resolution rules: if you have two plugins that link against a common library, when the first one is loaded any vague linkage symbols in both the plugin and the library are resolved to the definition in the plugin, and then when the second plugin is loaded any references there are resolved to its own definitions, so any code that relies on vague linkage symbols being shared between the library and the second plugin breaks. This applies to typeinfo comparison (including in EH), and any vague linkage objects such as template static data members and inline function local statics.

This patch solves this problem for typeinfo comparison by switching to using strcmp by default on all targets, since weak symbols don't quite do the trick. In order to avoid breaking g++.dg/abi/local1.C, I've adjusted the typeinfo name mangling for classes in static functions by pretending that the function is inside an anonymous namespace.

Embedded targets that don't support dlopen could override this default by defining __GXX_MERGED_TYPEINFO_NAMES to 1 in their libstdc++-v3 configuration file, but I doubt that the overhead of doing strcmp rather than a single pointer comparison will be significant.

Tested x86_64-pc-linux-gnu, applied to trunk.
2009-07-22  Jason Merrill  <jason@redhat.com>

	* mangle.c (mangle_type_string_for_rtti): Rename to be clearer.
	(needs_fake_anon): New.
	(write_name): Check it.
	(write_nested_name): Add a fake anonymous namespace scope if true.
	* name-lookup.c (get_anonymous_namespace_name): No longer static.
	* rtti.c, cp-tree.h: Adjust.

	* libsupc++/typeinfo (__GXX_MERGED_TYPEINFO_NAMES): Default to 0.

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 6ad039a..2bc2d62 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -4345,6 +4345,7 @@ extern tree type_promotes_to			(tree);
 extern tree perform_qualification_conversions	(tree, tree);
 
 /* in name-lookup.c */
+extern tree get_anonymous_namespace_name	(void);
 extern tree pushdecl				(tree);
 extern tree pushdecl_maybe_friend		(tree, bool);
 extern void maybe_push_cleanup_level		(tree);
@@ -5095,7 +5096,7 @@ extern tree merge_exception_specifiers		(tree, tree);
 /* in mangle.c */
 extern void init_mangle				(void);
 extern void mangle_decl				(tree);
-extern const char *mangle_type_string		(tree);
+extern const char *mangle_type_string_for_rtti	(tree);
 extern tree mangle_typeinfo_for_type		(tree);
 extern tree mangle_typeinfo_string_for_type	(tree);
 extern tree mangle_vtbl_for_type		(tree);
diff --git a/gcc/cp/mangle.c b/gcc/cp/mangle.c
index 1c79dcc..bb046d2 100644
--- a/gcc/cp/mangle.c
+++ b/gcc/cp/mangle.c
@@ -105,6 +105,10 @@ typedef struct GTY(()) globals {
 
 static GTY (()) globals G;
 
+/* Whether or not to pretend that a static function is in an anonymous
+   namespace.  */
+static bool fake_anon_scope;
+
 /* The obstack on which we build mangled names.  */
 static struct obstack *mangle_obstack;
 
@@ -726,6 +730,20 @@ write_encoding (const tree decl)
     }
 }
 
+/* Since we now use strcmp to compare typeinfos on all targets because of
+   the RTLD_LOCAL problem, we need to munge the typeinfo name used for
+   local classes of static functions to fix g++.dg/abi/local1.C.  We do
+   that by pretending that the function is in an anonymous namespace.  */
+
+static bool
+needs_fake_anon (const_tree decl)
+{
+  /* Pretend there's an anonymous namespace right around a static
+     function if we're mangling for RTTI.  */
+  return (fake_anon_scope && !TREE_PUBLIC (decl)
+	  && TREE_CODE (decl) == FUNCTION_DECL);
+}
+
 /* <name> ::= <unscoped-name>
 	  ::= <unscoped-template-name> <template-args>
 	  ::= <nested-name>
@@ -749,18 +767,23 @@ write_name (tree decl, const int ignore_local_scope)
       /* In case this is a typedef, fish out the corresponding
 	 TYPE_DECL for the main variant.  */
       decl = TYPE_NAME (TYPE_MAIN_VARIANT (TREE_TYPE (decl)));
-      context = TYPE_CONTEXT (TYPE_MAIN_VARIANT (TREE_TYPE (decl)));
+      context = CP_TYPE_CONTEXT (TYPE_MAIN_VARIANT (TREE_TYPE (decl)));
     }
   else
-    context = (DECL_CONTEXT (decl) == NULL) ? NULL : CP_DECL_CONTEXT (decl);
+    context = CP_DECL_CONTEXT (decl);
+
+  gcc_assert (context != NULL_TREE);
+
+  /* If we need a fake anonymous namespace, force the nested name path.  */
+  if (needs_fake_anon (decl) && context == global_namespace)
+    context = error_mark_node;
 
   /* A decl in :: or ::std scope is treated specially.  The former is
      mangled using <unscoped-name> or <unscoped-template-name>, the
      latter with a special substitution.  Also, a name that is
      directly in a local function scope is also mangled with
      <unscoped-name> rather than a full <nested-name>.  */
-  if (context == NULL
-      || context == global_namespace
+  if (context == global_namespace
       || DECL_NAMESPACE_STD_P (context)
       || (ignore_local_scope && TREE_CODE (context) == FUNCTION_DECL))
     {
@@ -778,6 +801,9 @@ write_name (tree decl, const int ignore_local_scope)
     }
   else
     {
+      if (context == error_mark_node)
+	context = global_namespace;
+
       /* Handle local names, unless we asked not to (that is, invoked
 	 under <local-name>, to handle only the part of the name under
 	 the local scope).  */
@@ -790,10 +816,10 @@ write_name (tree decl, const int ignore_local_scope)
 	     directly in that function's scope, either decl or one of
 	     its enclosing scopes.  */
 	  tree local_entity = decl;
-	  while (context != NULL && context != global_namespace)
+	  while (context != global_namespace)
 	    {
 	      /* Make sure we're always dealing with decls.  */
-	      if (context != NULL && TYPE_P (context))
+	      if (TYPE_P (context))
 		context = TYPE_NAME (context);
 	      /* Is this a function?  */
 	      if (TREE_CODE (context) == FUNCTION_DECL)
@@ -837,7 +863,6 @@ write_unscoped_name (const tree decl)
       /* If not, it should be either in the global namespace, or directly
 	 in a local function scope.  */
       gcc_assert (context == global_namespace
-		  || context == NULL
 		  || TREE_CODE (context) == FUNCTION_DECL);
 
       write_unqualified_name (decl);
@@ -909,6 +934,9 @@ write_nested_name (const tree decl)
     {
       /* No, just use <prefix>  */
       write_prefix (DECL_CONTEXT (decl));
+      if (needs_fake_anon (decl))
+	/* Pretend this static function is in an anonymous namespace.  */
+	write_source_name (get_anonymous_namespace_name ());
       write_unqualified_name (decl);
     }
   write_char ('E');
@@ -2817,15 +2845,18 @@ mangle_decl (const tree decl)
   SET_DECL_ASSEMBLER_NAME (decl, id);
 }
 
-/* Generate the mangled representation of TYPE.  */
+/* Generate the mangled representation of TYPE for the typeinfo name.  */
 
 const char *
-mangle_type_string (const tree type)
+mangle_type_string_for_rtti (const tree type)
 {
   const char *result;
 
   start_mangling (type);
+  /* Mangle in a fake anonymous namespace if necessary.  */
+  fake_anon_scope = true;
   write_type (type);
+  fake_anon_scope = false;
   result = finish_mangling (/*warn=*/false);
   if (DEBUG_MANGLE)
     fprintf (stderr, "mangle_type_string = '%s'\n\n", result);
diff --git a/gcc/cp/name-lookup.c b/gcc/cp/name-lookup.c
index 7a8016f..c2d8779 100644
--- a/gcc/cp/name-lookup.c
+++ b/gcc/cp/name-lookup.c
@@ -62,19 +62,14 @@ static GTY(()) tree anonymous_namespace_name;
 
 /* Initialize anonymous_namespace_name if necessary, and return it.  */
 
-static tree
-get_anonymous_namespace_name(void)
+tree
+get_anonymous_namespace_name (void)
 {
   if (!anonymous_namespace_name)
     {
       /* The anonymous namespace has to have a unique name
 	 if typeinfo objects are being compared by name.  */
-      if (! flag_weak || ! SUPPORTS_ONE_ONLY)
-	anonymous_namespace_name = get_file_function_name ("N");
-      else
-	/* The demangler expects anonymous namespaces to be called
-	   something starting with '_GLOBAL__N_'.  */
-	anonymous_namespace_name = get_identifier ("_GLOBAL__N_1");
+      anonymous_namespace_name = get_file_function_name ("N");
     }
   return anonymous_namespace_name;
 }
diff --git a/gcc/cp/rtti.c b/gcc/cp/rtti.c
index 01bba34..8dde479 100644
--- a/gcc/cp/rtti.c
+++ b/gcc/cp/rtti.c
@@ -356,7 +356,7 @@ tinfo_name (tree type)
   const char *name;
   tree name_string;
 
-  name = mangle_type_string (type);
+  name = mangle_type_string_for_rtti (type);
   name_string = fix_string_type (build_string (strlen (name) + 1, name));
   return name_string;
 }
diff --git a/libstdc++-v3/libsupc++/typeinfo b/libstdc++-v3/libsupc++/typeinfo
index eea38e7..4c47043 100644
--- a/libstdc++-v3/libsupc++/typeinfo
+++ b/libstdc++-v3/libsupc++/typeinfo
@@ -43,32 +43,29 @@ namespace __cxxabiv1
 } // namespace __cxxabiv1
 
 // Determine whether typeinfo names for the same type are merged (in which
-// case comparison can just compare pointers) or not (in which case
-// strings must be compared and g++.dg/abi/local1.C will fail), and
-// whether comparison is to be implemented inline or not.  By default we
-// use inline pointer comparison if weak symbols are available, and
-// out-of-line strcmp if not.  Out-of-line pointer comparison is used
-// where the object files are to be portable to multiple systems, some of
-// which may not be able to use pointer comparison, but the particular
-// system for which libstdc++ is being built can use pointer comparison;
-// in particular for most ARM EABI systems, where the ABI specifies
-// out-of-line comparison.  Inline strcmp is not currently supported.  The
-// compiler's target configuration can override the defaults by defining
-// __GXX_TYPEINFO_EQUALITY_INLINE to 1 or 0 to indicate whether or not
-// comparison is inline, and __GXX_MERGED_TYPEINFO_NAMES to 1 or 0 to
-// indicate whether or not pointer comparison can be used.
+// case comparison can just compare pointers) or not (in which case strings
+// must be compared), and whether comparison is to be implemented inline or
+// not.  We used to do inline pointer comparison by default if weak symbols
+// are available, but even with weak symbols sometimes names are not merged
+// when objects are loaded with RTLD_LOCAL, so now we always use strcmp by
+// default.  For ABI compatibility, we do the strcmp inline if weak symbols
+// are available, and out-of-line if not.  Out-of-line pointer comparison
+// is used where the object files are to be portable to multiple systems,
+// some of which may not be able to use pointer comparison, but the
+// particular system for which libstdc++ is being built can use pointer
+// comparison; in particular for most ARM EABI systems, where the ABI
+// specifies out-of-line comparison.  The compiler's target configuration
+// can override the defaults by defining __GXX_TYPEINFO_EQUALITY_INLINE to
+// 1 or 0 to indicate whether or not comparison is inline, and
+// __GXX_MERGED_TYPEINFO_NAMES to 1 or 0 to indicate whether or not pointer
+// comparison can be used.
 
 #ifndef __GXX_MERGED_TYPEINFO_NAMES
-  #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
+// By default, typeinfo names are not merged.
+#define __GXX_MERGED_TYPEINFO_NAMES 0
 #endif
 
-// By default follow the same rules as for __GXX_MERGED_TYPEINFO_NAMES.
+// By default follow the old inline rules to avoid ABI changes.
 #ifndef __GXX_TYPEINFO_EQUALITY_INLINE
   #if !__GXX_WEAK__
     #define __GXX_TYPEINFO_EQUALITY_INLINE 0
@@ -100,25 +97,35 @@ namespace std
     { return __name; }
 
 #if !__GXX_TYPEINFO_EQUALITY_INLINE
-    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 before(const type_info& __arg) const;
     bool operator==(const type_info& __arg) const;
 #else
   #if !__GXX_MERGED_TYPEINFO_NAMES
-    #error "Inline implementation of type_info comparision requires merging of type_info objects"
-  #endif
     /** 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,
+    // Even with the new abi, on systems that support dlopen
+    // we can run into cases where type_info names aren't merged,
+    // so we still need to do string comparison.
+    bool before(const type_info& __arg) const
+    { return __builtin_strcmp (__name, __arg.__name) < 0; }
+
+    bool operator==(const type_info& __arg) const
+    {
+      return ((__name == __arg.__name)
+	      || __builtin_strcmp (__name, __arg.__name) == 0);
+    }
+  #else
+    // On some targets 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
 #endif
     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]