dwarf-reader, ir: Add member fns to exported corpus fns after c14n

Message ID 87edct7tky.fsf@redhat.com
State New
Headers
Series dwarf-reader, ir: Add member fns to exported corpus fns after c14n |

Commit Message

Dodji Seketeli March 1, 2024, 5:25 p.m. UTC
  Hello,

There are cases where a member function C::f has a defined & exported
ELF symbol in a translation unit TU-2, whereas it has no associated
symbol in another translation unit TU-1.

The class C from TU-1 and the class C from TU-2 have the same
canonical type because they are equivalent.  Now, suppose it's C from
TU-2 that is the canonical type; that means the canonical type of C
from TU-1 is C from TU-2.

Today, the only types that are emitted in an ABIXML file are canonical
types.  So if we want the C::f which has a publicly defined & exported
elf symbol to be emitted in the ABIXML file, then the C::f that is
defined & exported in the ABI corpus should be the C::f from the
canonical class type C i.e, the C::f from TU-2.

In other words, the exported C::f should be the member function of the
canonical type class C.

That means that determining which C::f to export in the ABI corpus
should be done *after* type canonicalization.

Today however, determining which C::f to export is done during the
construction of the IR, in the DWARF reader.  That leads to
cases where the exported C::f is the one that has no defined &
exported ELF symbol.  As virtual member functions (in particular) are
involved in class type comparison, that could lead to spurious type
changes left and right.  Oops.

This patch implements the export of member functions after type
canonicalization in the DWARF reader.  Note that CTF and BTF readers
are not impacted as they only support the C language which doesn't
have member functions.

	* src/abg-dwarf-reader.cc
	(reader::fixup_functions_with_no_symbols): Do not export the
	virtual member function here.
	(build_ir_node_from_die): For DW_TAG_subprogram DIEs, likewise.
	* src/abg-ir.cc (maybe_adjust_canonical_type): Walk the member
	functions of the canonical type and export them in the ABI corpus
	if they have a defined and exported elf symbol.
	* tests/data/test-read-dwarf/test9-pr18818-clang.so.abi: Adjust.

Signed-off-by: Dodji Seketeli <dodji@redhat.com>
Applying to the master branch
---
 src/abg-dwarf-reader.cc                       | 15 ++++---
 src/abg-ir.cc                                 | 44 +++++++++++++++----
 .../test9-pr18818-clang.so.abi                |  2 +-
 3 files changed, 45 insertions(+), 16 deletions(-)
  

Patch

diff --git a/src/abg-dwarf-reader.cc b/src/abg-dwarf-reader.cc
index 6d64c0c7..46f08a9e 100644
--- a/src/abg-dwarf-reader.cc
+++ b/src/abg-dwarf-reader.cc
@@ -4552,12 +4552,7 @@  public:
 	  ABG_ASSERT(is_member_function(i->second));
 	  ABG_ASSERT(get_member_function_is_virtual(i->second));
 	  i->second->set_symbol(sym);
-	  // The function_decl now has an associated (public) ELF symbol so
-	  // it ought to be advertised as being public.
-	  i->second->set_is_in_public_symbol_table(true);
-	  // Add the function to the set of exported decls of the
-	  // current corpus.
-	  maybe_add_fn_to_exported_decls(i->second.get());
+
 	  if (do_log())
 	    cerr << "fixed up '"
 		 << i->second->get_pretty_representation()
@@ -16016,7 +16011,13 @@  build_ir_node_from_die(reader&	rdr,
 
 	if (fn)
 	  {
-	    rdr.maybe_add_fn_to_exported_decls(fn.get());
+	    if (!is_member_function(fn)
+		|| !get_member_function_is_virtual(fn))
+	      // Virtual member functions are added to the set of
+	      // functions exported by the current ABI corpus *after*
+	      // the canonicalization of their parent type.  So let's
+	      // not do it here.
+	      rdr.maybe_add_fn_to_exported_decls(fn.get());
 	    rdr.associate_die_to_decl(die, fn, where_offset,
 				       /*associate_by_repr=*/false);
 	    maybe_canonicalize_type(fn->get_type(), rdr);
diff --git a/src/abg-ir.cc b/src/abg-ir.cc
index d3fbafb4..d0d92055 100644
--- a/src/abg-ir.cc
+++ b/src/abg-ir.cc
@@ -15649,18 +15649,15 @@  static void
 maybe_adjust_canonical_type(const type_base_sptr& canonical,
 			    const type_base_sptr& type)
 {
-  if (!canonical
-      // If 'type' is *NOT* a newly canonicalized type ...
-      || type->get_naked_canonical_type()
-      // ... or if 'type' is it's own canonical type, then get out.
-      || type.get() == canonical.get())
+  if (type->get_naked_canonical_type())
     return;
 
+  class_decl_sptr canonical_class = is_class_type(canonical);
+
   if (class_decl_sptr cl = is_class_type(type))
     {
-      class_decl_sptr canonical_class = is_class_type(canonical);
-
-      if (canonical_class)
+      if (canonical_class
+	  && canonical_class.get() != cl.get())
 	{
 	  // Set symbols of member functions that might be missing
 	  // theirs.
@@ -15695,6 +15692,37 @@  maybe_adjust_canonical_type(const type_base_sptr& canonical,
 	}
     }
 
+  // Make sure the virtual member functions with exported symbols are
+  // all added to the set of exported functions of the corpus.
+
+  // If we are looking at a non-canonicalized class (for instance, a
+  // decl-only class that has virtual member functoins), let's pretend
+  // it does have a canonical class so that we can perform the
+  // necessary virtual  member function adjustments
+  if (class_decl_sptr cl = is_class_type(type))
+    if (is_non_canonicalized_type(cl))
+      {
+	ABG_ASSERT(!canonical_class);
+	canonical_class = cl;
+      }
+
+  if (canonical_class)
+    {
+      if (auto abi_corpus = canonical_class->get_corpus())
+	{
+	  for (auto& fn : canonical_class->get_member_functions())
+	    {
+	      if (elf_symbol_sptr sym = fn->get_symbol())
+		if (sym->is_defined() && sym->is_public())
+		  {
+		    fn->set_is_in_public_symbol_table(true);
+		    auto b = abi_corpus->get_exported_decls_builder();
+		    b->maybe_add_fn_to_exported_fns(fn.get());
+		  }
+	    }
+	}
+    }
+
   // If an artificial function type equals a non-artfificial one in
   // the system, then the canonical type of both should be deemed
   // non-artificial.  This is important because only non-artificial
diff --git a/tests/data/test-read-dwarf/test9-pr18818-clang.so.abi b/tests/data/test-read-dwarf/test9-pr18818-clang.so.abi
index a7ee1ef3..8c598216 100644
--- a/tests/data/test-read-dwarf/test9-pr18818-clang.so.abi
+++ b/tests/data/test-read-dwarf/test9-pr18818-clang.so.abi
@@ -4810,7 +4810,7 @@ 
             </function-decl>
           </member-function>
           <member-function access='public' destructor='yes' vtable-offset='0'>
-            <function-decl name='~system_error' mangled-name='_ZN5boost6system12system_errorD2Ev' filepath='src/third_party/boost-1.56.0/boost/system/system_error.hpp' line='47' column='1' visibility='default' binding='global' size-in-bits='64'>
+            <function-decl name='~system_error' mangled-name='_ZN5boost6system12system_errorD2Ev' filepath='src/third_party/boost-1.56.0/boost/system/system_error.hpp' line='47' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='_ZN5boost6system12system_errorD2Ev'>
               <parameter type-id='type-id-206' is-artificial='yes'/>
               <return type-id='type-id-118'/>
             </function-decl>