ctf-reader: Assert on ir::hash_as_canonical_type_or_constant

Message ID 20211210025252.2309157-1-guillermo.e.martinez@oracle.com
State New
Headers
Series ctf-reader: Assert on ir::hash_as_canonical_type_or_constant |

Commit Message

Guillermo E. Martinez Dec. 10, 2021, 2:52 a.m. UTC
  Hello libabigail team,

This patch fix assertions in ctf reader back-end.

Dependencies/limitations:

* It works on the top of the following patch:
  https://sourceware.org/pipermail/libabigail/2021q4/003918.html

I'll appreciate your comments.

Kind Regards,
Guillermo

In some scenarios where we declare same data types `recursively' such
as: like linked list, functions that accept the same pointer to function
as arguments, forward types declarations to build structures with member
fields with mutual dependencies, etc., an assertion is trigger:

    abidw: ../../../libabigail-upstream/src/abg-ir.cc:25251: size_t
    abigail::ir::hash_as_canonical_type_or_constant(const
    abigail::ir::type_base*): Assertion `__abg_cond__' failed.

It is happening because the recursively behavior of `process_ctf_type'
and `process_ctf_*' used to register ctf types doesn't verify when a
ctf_type was processed and registered before by their subsequence
_recursive_ calls, so `type_base' object is built more than once and the
second time when it is inserted in `types_maps', it refuses in a silent
way, being that the key was already inserted, however
`add_decl_to_scope', `bind_function_type_life_time' successfully
registered the ctf type object. In this patch `process_ctf_type'
delegates register types task to `process_ctf_*' functions guaranteeing
a single ctf type registration, also it improves the performance looking
for the type before start to build it again.

	* src/abg-ctf-reader.cc (process_ctf_base_type): Add new
	`translation_unit_sptr' parameter. Add condition to validate
	success 'base_type' construction and register type object.
	(process_ctf_typedef): Add `lookup_type' to get a `type_base'
	object when this was previously created, if this is not the
	case, register ctf type. Add condition to validate success
	'base_type' construction and register type object.
	(process_ctf_function_type): Likewise.
	(process_ctf_array_type): Likewise.
	(process_ctf_qualified_type): Likewise.
	(process_ctf_pointer_type): Likewise.
	(process_ctf_struct_type): Add `add_decl_to_scope'.
	(process_ctf_union_type): Likewise.
	(process_ctf_type): Add `lookup_type' to get a `type_base'
	object when this was previously created. Delegate register
	type object to `process_ctf_*'.
	* tests/data/test-read-ctf/test-array-of-pointers.c: New
	testcase.
	* tests/data/test-read-ctf/test-list-struct.c: Likewise.
	* tests/data/test-read-ctf/test-callback.c: Likewise.
	* tests/data/test-read-ctf/test-callback2.c: Likewise.
	* tests/data/test-read-ctf/test-functions-declaration.c: Likewise.
	* tests/data/test-read-ctf/test-forward-type-decl.c: Likewise.
	* tests/data/test-read-ctf/test-array-of-pointers.abi:
	Expected test output.
	* tests/data/test-read-ctf/test-callback.abi: Likewise.
	* tests/data/test-read-ctf/test-callback2.abi: Likewise.
	* tests/data/test-read-ctf/test-forward-type-decl.abi: Likewise.
	* tests/data/test-read-ctf/test-functions-declaration.abi: Likewise.
	* tests/data/test-read-ctf/test-list-struct.abi: Likewise.
	* tests/data/test-read-ctf/test6.c: Likewise.
	* tests/data/test-read-ctf/test6.o.abi: Likewise.
	* tests/test-read-ctf.cc: Add testcases to CTF test harness.

Signed-off-by: Guillermo E. Martinez <guillermo.e.martinez@oracle.com>
---
 src/abg-ctf-reader.cc                         | 151 ++++++++++--------
 .../test-read-ctf/test-array-of-pointers.abi  |  36 +++++
 .../test-read-ctf/test-array-of-pointers.c    |  20 +++
 tests/data/test-read-ctf/test-callback.abi    |  31 ++++
 tests/data/test-read-ctf/test-callback.c      |  16 ++
 tests/data/test-read-ctf/test-callback2.abi   |  20 +++
 tests/data/test-read-ctf/test-callback2.c     |  11 ++
 .../test-read-ctf/test-forward-type-decl.abi  |  29 ++++
 .../test-read-ctf/test-forward-type-decl.c    |  23 +++
 .../test-functions-declaration.abi            |  25 +++
 .../test-functions-declaration.c              |  16 ++
 tests/data/test-read-ctf/test-list-struct.abi |  20 +++
 tests/data/test-read-ctf/test-list-struct.c   |  11 ++
 tests/test-read-ctf.cc                        |  48 ++++++
 14 files changed, 389 insertions(+), 68 deletions(-)
 create mode 100644 tests/data/test-read-ctf/test-array-of-pointers.abi
 create mode 100644 tests/data/test-read-ctf/test-array-of-pointers.c
 create mode 100644 tests/data/test-read-ctf/test-callback.abi
 create mode 100644 tests/data/test-read-ctf/test-callback.c
 create mode 100644 tests/data/test-read-ctf/test-callback2.abi
 create mode 100644 tests/data/test-read-ctf/test-callback2.c
 create mode 100644 tests/data/test-read-ctf/test-forward-type-decl.abi
 create mode 100644 tests/data/test-read-ctf/test-forward-type-decl.c
 create mode 100644 tests/data/test-read-ctf/test-functions-declaration.abi
 create mode 100644 tests/data/test-read-ctf/test-functions-declaration.c
 create mode 100644 tests/data/test-read-ctf/test-list-struct.abi
 create mode 100644 tests/data/test-read-ctf/test-list-struct.c
  

Comments

Dodji Seketeli Dec. 21, 2021, 4:22 p.m. UTC | #1
Hello Guillermo,

"Guillermo E. Martinez via Libabigail" <libabigail@sourceware.org> a
écrit:

[...]

Thanks for the patch!

I have tested this patch and I've found some issues.  Please find my
comments below:

> diff --git a/tests/test-read-ctf.cc b/tests/test-read-ctf.cc
> index b448ddc5..7f5381f6 100644
> --- a/tests/test-read-ctf.cc
> +++ b/tests/test-read-ctf.cc
> @@ -220,6 +220,54 @@ static InOutSpec in_out_specs[] =
>      "data/test-read-ctf/PR27700/test-PR27700.abi",
>      "output/test-read-ctf/PR27700/test-PR27700.abi",
>    },
> +  {
> +    "data/test-read-ctf/test-callback.o",

This *.o is not present in the patch.  I think the remaining *.o
below are not present in the patch either.  So test/runtestreadctf
fails.

Also, you need to add these files to the tests/data/Makefile.am file.

As I've said elsewhere in the past, "make distcheck-fast" can help you
check if the files are really present in the tarball.

[...]

Cheers,
  
Guillermo E. Martinez Dec. 21, 2021, 5:28 p.m. UTC | #2
On Tuesday, December 21, 2021 10:22:33 AM CST Dodji Seketeli wrote:
> Hello Guillermo,
> 
> "Guillermo E. Martinez via Libabigail" <libabigail@sourceware.org> a
> écrit:
> 
> [...]
> 
> Thanks for the patch!
> 
> I have tested this patch and I've found some issues.  Please find my
> comments below:
> 
> > diff --git a/tests/test-read-ctf.cc b/tests/test-read-ctf.cc
> > index b448ddc5..7f5381f6 100644
> > --- a/tests/test-read-ctf.cc
> > +++ b/tests/test-read-ctf.cc
> > @@ -220,6 +220,54 @@ static InOutSpec in_out_specs[] =
> >      "data/test-read-ctf/PR27700/test-PR27700.abi",
> >      "output/test-read-ctf/PR27700/test-PR27700.abi",
> >    },
> > +  {
> > +    "data/test-read-ctf/test-callback.o",
> 
> This *.o is not present in the patch.  I think the remaining *.o
> below are not present in the patch either.  So test/runtestreadctf
> fails.
> 
> Also, you need to add these files to the tests/data/Makefile.am file.
> 
> As I've said elsewhere in the past, "make distcheck-fast" can help you
> check if the files are really present in the tarball.
Ohh, apologises, I'll do that. Dodji thanks for your comments!

> [...]
> 
> Cheers,
  

Patch

diff --git a/src/abg-ctf-reader.cc b/src/abg-ctf-reader.cc
index fbf6baef..bc49f49c 100644
--- a/src/abg-ctf-reader.cc
+++ b/src/abg-ctf-reader.cc
@@ -15,6 +15,7 @@ 
 
 #include <fcntl.h> /* For open(3) */
 #include <iostream>
+#include <memory>
 
 #include "ctf-api.h"
 
@@ -39,6 +40,7 @@  namespace abigail
 {
 namespace ctf_reader
 {
+using std::dynamic_pointer_cast;
 
 class read_context
 {
@@ -157,6 +159,10 @@  process_ctf_typedef(read_context *ctxt,
   if (!utype)
     return result;
 
+  result = dynamic_pointer_cast<typedef_decl>(ctxt->lookup_type(ctf_type));
+  if (result)
+    return result;
+
   result.reset(new typedef_decl(typedef_name, utype, location(),
                                 typedef_name /* mangled_name */));
 
@@ -171,6 +177,12 @@  process_ctf_typedef(read_context *ctxt,
       decl->set_naming_typedef(result);
     }
 
+  if (result)
+    {
+      add_decl_to_scope(result, tunit->get_global_scope());
+      ctxt->add_type(ctf_type, result);
+    }
+
   return result;
 }
 
@@ -187,6 +199,7 @@  process_ctf_typedef(read_context *ctxt,
 static type_decl_sptr
 process_ctf_base_type(read_context *ctxt,
                       corpus_sptr corp,
+                      translation_unit_sptr tunit,
                       ctf_dict_t *ctf_dictionary,
                       ctf_id_t ctf_type)
 {
@@ -226,6 +239,12 @@  process_ctf_base_type(read_context *ctxt,
 
     }
 
+  if (result)
+    {
+      add_decl_to_scope(result, tunit->get_global_scope());
+      ctxt->add_type(ctf_type, result);
+    }
+
   return result;
 }
 
@@ -284,6 +303,9 @@  process_ctf_function_type(read_context *ctxt,
       function_parms.push_back(parm);
     }
 
+  result = dynamic_pointer_cast<function_type>(ctxt->lookup_type(ctf_type));
+  if (result)
+    return result;
 
   /* Ok now the function type itself.  */
   result.reset(new function_type(ret_type,
@@ -291,8 +313,15 @@  process_ctf_function_type(read_context *ctxt,
                                  tunit->get_address_size(),
                                  ctf_type_align(ctf_dictionary, ctf_type)));
 
-  tunit->bind_function_type_life_time(result);
-  result->set_is_artificial(true);
+  if (result)
+    {
+      tunit->bind_function_type_life_time(result);
+      result->set_is_artificial(true);
+      decl_base_sptr function_type_decl = get_type_declaration(result);
+      add_decl_to_scope(function_type_decl, tunit->get_global_scope());
+      ctxt->add_type(ctf_type, result);
+    }
+
   return result;
 }
 
@@ -394,6 +423,7 @@  process_ctf_struct_type(read_context *ctxt,
      refer to this struct, we have to make it available in the cache
      at this point even if the members haven't been added to the IR
      node yet.  */
+  add_decl_to_scope(result, tunit->get_global_scope());
   ctxt->add_type(ctf_type, result);
 
   /* Now add the struct members as specified in the CTF type description.
@@ -442,6 +472,7 @@  process_ctf_union_type(read_context *ctxt,
      refer to this union, we have to make it available in the cache
      at this point even if the members haven't been added to the IR
      node yet.  */
+  add_decl_to_scope(result, tunit->get_global_scope());
   ctxt->add_type(ctf_type, result);
 
   /* Now add the union members as specified in the CTF type description.
@@ -498,6 +529,10 @@  process_ctf_array_type(read_context *ctxt,
   if (!index_type)
     return result;
 
+  result = dynamic_pointer_cast<array_type_def>(ctxt->lookup_type(ctf_type));
+  if (result)
+    return result;
+
   /* The number of elements of the array determines the IR subranges
      type to build.  */
   array_type_def::subranges_type subranges;
@@ -529,6 +564,13 @@  process_ctf_array_type(read_context *ctxt,
 
   /* Finally build the IR for the array type and return it.  */
   result.reset(new array_type_def(element_type, subranges, location()));
+  if (result)
+    {
+      decl_base_sptr array_type_decl = get_type_declaration(result);
+      add_decl_to_scope(array_type_decl, tunit->get_global_scope());
+      ctxt->add_type(ctf_type, result);
+    }
+
   return result;
 }
 
@@ -566,6 +608,14 @@  process_ctf_qualified_type(read_context *ctxt,
     ABG_ASSERT_NOT_REACHED;
 
   result.reset(new qualified_type_def(utype, qualifiers, location()));
+
+  if (result)
+    {
+      decl_base_sptr qualified_type_decl = get_type_declaration(result);
+      add_decl_to_scope(qualified_type_decl, tunit->get_global_scope());
+      ctxt->add_type(ctf_type, result);
+    }
+
   return result;
 }
 
@@ -597,10 +647,20 @@  process_ctf_pointer_type(read_context *ctxt,
   if (!target_type)
     return result;
 
+  result = dynamic_pointer_cast<pointer_type_def>(ctxt->lookup_type(ctf_type));
+  if (result)
+    return result;
+
   result.reset(new pointer_type_def(target_type,
                                       ctf_type_size(ctf_dictionary, ctf_type) * 8,
                                       ctf_type_align(ctf_dictionary, ctf_type) * 8,
                                       location()));
+  if (result)
+    {
+      add_decl_to_scope(result, tunit->get_global_scope());
+      ctxt->add_type(ctf_type, result);
+    }
+
   return result;
 }
 
@@ -657,6 +717,12 @@  process_ctf_enum_type(read_context *ctxt,
   const char *enum_name = ctf_type_name_raw(ctf_dictionary, ctf_type);
   result.reset(new enum_type_decl(enum_name, location(),
                                   utype, enms, enum_name));
+  if (result)
+    {
+      add_decl_to_scope(result, tunit->get_global_scope());
+      ctxt->add_type(ctf_type, result);
+    }
+
   return result;
 }
 
@@ -683,43 +749,31 @@  process_ctf_type(read_context *ctxt,
   int type_kind = ctf_type_kind(ctf_dictionary, ctf_type);
   type_base_sptr result;
 
+  if ((result = ctxt->lookup_type(ctf_type)))
+    return result;
+
   switch (type_kind)
     {
     case CTF_K_INTEGER:
     case CTF_K_FLOAT:
       {
         type_decl_sptr type_decl
-          = process_ctf_base_type(ctxt, corp, ctf_dictionary, ctf_type);
-
-        if (type_decl)
-          {
-            add_decl_to_scope(type_decl, tunit->get_global_scope());
-            result = is_type(type_decl);
-          }
+          = process_ctf_base_type(ctxt, corp, tunit, ctf_dictionary, ctf_type);
+        result = is_type(type_decl);
         break;
       }
     case CTF_K_TYPEDEF:
       {
         typedef_decl_sptr typedef_decl
           = process_ctf_typedef(ctxt, corp, tunit, ctf_dictionary, ctf_type);
-
-        if (typedef_decl)
-          {
-            add_decl_to_scope(typedef_decl, tunit->get_global_scope());
-            result = is_type(typedef_decl);
-          }
+        result = is_type(typedef_decl);
         break;
       }
     case CTF_K_POINTER:
       {
         pointer_type_def_sptr pointer_type
           = process_ctf_pointer_type(ctxt, corp, tunit, ctf_dictionary, ctf_type);
-
-        if (pointer_type)
-          {
-            add_decl_to_scope(pointer_type, tunit->get_global_scope());
-            result = pointer_type;
-          }
+        result = pointer_type;
         break;
       }
     case CTF_K_CONST:
@@ -728,79 +782,42 @@  process_ctf_type(read_context *ctxt,
       {
         type_base_sptr qualified_type
           = process_ctf_qualified_type(ctxt, corp, tunit, ctf_dictionary, ctf_type);
-
-        if (qualified_type)
-          {
-            decl_base_sptr qualified_type_decl = get_type_declaration(qualified_type);
-
-            add_decl_to_scope(qualified_type_decl, tunit->get_global_scope());
-            result = qualified_type;
-          }
+        result = qualified_type;
         break;
       }
     case CTF_K_ARRAY:
       {
         array_type_def_sptr array_type
           = process_ctf_array_type(ctxt, corp, tunit, ctf_dictionary, ctf_type);
-
-        if (array_type)
-          {
-            decl_base_sptr array_type_decl = get_type_declaration(array_type);
-
-            add_decl_to_scope(array_type_decl, tunit->get_global_scope());
-            result = array_type;
-          }
+        result = array_type;
         break;
       }
     case CTF_K_ENUM:
       {
         enum_type_decl_sptr enum_type
           = process_ctf_enum_type(ctxt, tunit, ctf_dictionary, ctf_type);
-
-        if (enum_type)
-          {
-            add_decl_to_scope(enum_type, tunit->get_global_scope());
-            result = enum_type;
-          }
-
+        result = enum_type;
         break;
       }
     case CTF_K_FUNCTION:
       {
         function_type_sptr function_type
           = process_ctf_function_type(ctxt, corp, tunit, ctf_dictionary, ctf_type);
-
-        if (function_type)
-          {
-            decl_base_sptr function_type_decl = get_type_declaration(function_type);
-
-            add_decl_to_scope(function_type_decl, tunit->get_global_scope());
-            result = function_type;
-          }
+        result = function_type;
         break;
       }
     case CTF_K_STRUCT:
       {
         class_decl_sptr struct_decl
           = process_ctf_struct_type(ctxt, corp, tunit, ctf_dictionary, ctf_type);
-
-        if (struct_decl)
-          {
-            add_decl_to_scope(struct_decl, tunit->get_global_scope());
-            result = is_type(struct_decl);
-          }
+        result = is_type(struct_decl);
         break;
       }
     case CTF_K_UNION:
       {
         union_decl_sptr union_decl
           = process_ctf_union_type(ctxt, corp, tunit, ctf_dictionary, ctf_type);
-
-        if (union_decl)
-          {
-            add_decl_to_scope(union_decl, tunit->get_global_scope());
-            result = is_type(union_decl);
-          }
+        result = is_type(union_decl);
         break;
       }
     case CTF_K_UNKNOWN:
@@ -809,10 +826,8 @@  process_ctf_type(read_context *ctxt,
       break;
     }
 
-  if (result)
-    ctxt->add_type(ctf_type, result);
-  else
-    fprintf(stderr, "NOT PROCESSED TYPE %lu\n", ctf_type);
+  // if (!result)
+  //   fprintf(stderr, "NOT PROCESSED TYPE %lu\n", ctf_type);
 
   return result;
 }
diff --git a/tests/data/test-read-ctf/test-array-of-pointers.abi b/tests/data/test-read-ctf/test-array-of-pointers.abi
new file mode 100644
index 00000000..920da28b
--- /dev/null
+++ b/tests/data/test-read-ctf/test-array-of-pointers.abi
@@ -0,0 +1,36 @@ 
+<abi-corpus version='2.1' path='data/test-read-ctf/test-array-of-pointers.o'>
+  <elf-variable-symbols>
+    <elf-symbol name='t' size='8' type='object-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+  </elf-variable-symbols>
+  <abi-instr address-size='64' language='LANG_C'>
+    <class-decl name='cgroup' size-in-bits='256' alignment-in-bits='64' is-struct='yes' visibility='default' id='type-id-1'>
+      <data-member access='public' layout-offset-in-bits='0'>
+        <var-decl name='s1' type-id='type-id-2' visibility='default'/>
+      </data-member>
+    </class-decl>
+    <class-decl name='css_set' size-in-bits='256' alignment-in-bits='64' is-struct='yes' visibility='default' id='type-id-3'>
+      <data-member access='public' layout-offset-in-bits='0'>
+        <var-decl name='s0' type-id='type-id-2' visibility='default'/>
+      </data-member>
+    </class-decl>
+    <class-decl name='state' size-in-bits='64' alignment-in-bits='64' is-struct='yes' visibility='default' id='type-id-4'>
+      <data-member access='public' layout-offset-in-bits='0'>
+        <var-decl name='cg' type-id='type-id-5' visibility='default'/>
+      </data-member>
+    </class-decl>
+    <class-decl name='task' size-in-bits='64' alignment-in-bits='64' is-struct='yes' visibility='default' id='type-id-6'>
+      <data-member access='public' layout-offset-in-bits='0'>
+        <var-decl name='s' type-id='type-id-7' visibility='default'/>
+      </data-member>
+    </class-decl>
+    <array-type-def dimensions='1' type-id='type-id-8' size-in-bits='256' alignment-in-bits='64' id='type-id-2'>
+      <subrange length='4' type-id='type-id-9' id='type-id-10'/>
+    </array-type-def>
+    <type-decl name='unsigned long int' size-in-bits='64' alignment-in-bits='64' id='type-id-9'/>
+    <pointer-type-def type-id='type-id-1' size-in-bits='64' alignment-in-bits='64' id='type-id-5'/>
+    <pointer-type-def type-id='type-id-3' size-in-bits='64' alignment-in-bits='64' id='type-id-7'/>
+    <pointer-type-def type-id='type-id-4' size-in-bits='64' alignment-in-bits='64' id='type-id-8'/>
+    <pointer-type-def type-id='type-id-6' size-in-bits='64' alignment-in-bits='64' id='type-id-11'/>
+    <var-decl name='t' type-id='type-id-11' mangled-name='t' visibility='default'/>
+  </abi-instr>
+</abi-corpus>
diff --git a/tests/data/test-read-ctf/test-array-of-pointers.c b/tests/data/test-read-ctf/test-array-of-pointers.c
new file mode 100644
index 00000000..560ce354
--- /dev/null
+++ b/tests/data/test-read-ctf/test-array-of-pointers.c
@@ -0,0 +1,20 @@ 
+/* Test a array of pointer definition as struct member
+ * gcc -gctf -c test-array-of-pointers.c -o test-array-of-pointer.o
+ */
+struct task {
+ struct css_set *s;
+};
+
+struct state {
+ struct cgroup *cg;
+};
+
+struct css_set {
+ struct state *s0[4];
+};
+
+struct cgroup {
+ struct state *s1[4];
+};
+
+struct task *t;
diff --git a/tests/data/test-read-ctf/test-callback.abi b/tests/data/test-read-ctf/test-callback.abi
new file mode 100644
index 00000000..704c4971
--- /dev/null
+++ b/tests/data/test-read-ctf/test-callback.abi
@@ -0,0 +1,31 @@ 
+<abi-corpus version='2.1' path='data/test-read-ctf/test-callback.o'>
+  <elf-function-symbols>
+    <elf-symbol name='assign' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+    <elf-symbol name='f2' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+  </elf-function-symbols>
+  <abi-instr address-size='64' language='LANG_C'>
+    <class-decl name='test' size-in-bits='64' alignment-in-bits='64' is-struct='yes' visibility='default' id='type-id-1'>
+      <data-member access='public' layout-offset-in-bits='0'>
+        <var-decl name='fn1' type-id='type-id-2' visibility='default'/>
+      </data-member>
+    </class-decl>
+    <type-decl name='int' size-in-bits='32' alignment-in-bits='32' id='type-id-3'/>
+    <type-decl name='long int' size-in-bits='64' alignment-in-bits='64' id='type-id-4'/>
+    <pointer-type-def type-id='type-id-1' size-in-bits='64' alignment-in-bits='64' id='type-id-5'/>
+    <pointer-type-def type-id='type-id-6' size-in-bits='64' alignment-in-bits='64' id='type-id-2'/>
+    <function-decl name='assign' visibility='default' binding='global' size-in-bits='64' alignment-in-bits='8'>
+      <return type-id='type-id-7'/>
+    </function-decl>
+    <function-decl name='f2' visibility='default' binding='global' size-in-bits='64' alignment-in-bits='8'>
+      <parameter type-id='type-id-3'/>
+      <parameter type-id='type-id-4'/>
+      <return type-id='type-id-7'/>
+    </function-decl>
+    <function-type size-in-bits='64' alignment-in-bits='8' id='type-id-6'>
+      <parameter type-id='type-id-3'/>
+      <parameter type-id='type-id-4'/>
+      <return type-id='type-id-7'/>
+    </function-type>
+    <type-decl name='void' id='type-id-7'/>
+  </abi-instr>
+</abi-corpus>
diff --git a/tests/data/test-read-ctf/test-callback.c b/tests/data/test-read-ctf/test-callback.c
new file mode 100644
index 00000000..a5fc7df2
--- /dev/null
+++ b/tests/data/test-read-ctf/test-callback.c
@@ -0,0 +1,16 @@ 
+/* Test a simple callback as a struct member
+ * gcc -gctf -c test-callback.c -o test-callback.o
+ */
+struct test {
+   void (*fn1)(int, long);
+};
+
+void f2(int a, long b)
+{
+}
+
+void assign()
+{
+   struct test *tt;
+   tt->fn1 = f2;
+}
diff --git a/tests/data/test-read-ctf/test-callback2.abi b/tests/data/test-read-ctf/test-callback2.abi
new file mode 100644
index 00000000..bdd4ad33
--- /dev/null
+++ b/tests/data/test-read-ctf/test-callback2.abi
@@ -0,0 +1,20 @@ 
+<abi-corpus version='2.1' path='data/test-read-ctf/test-callback2.o'>
+  <elf-variable-symbols>
+    <elf-symbol name='s0' size='8' type='object-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+  </elf-variable-symbols>
+  <abi-instr address-size='64' language='LANG_C'>
+    <class-decl name='s0' size-in-bits='64' alignment-in-bits='64' is-struct='yes' visibility='default' id='type-id-1'>
+      <data-member access='public' layout-offset-in-bits='0'>
+        <var-decl name='mem_fun' type-id='type-id-2' visibility='default'/>
+      </data-member>
+    </class-decl>
+    <type-decl name='int' size-in-bits='32' alignment-in-bits='32' id='type-id-3'/>
+    <pointer-type-def type-id='type-id-4' size-in-bits='64' alignment-in-bits='64' id='type-id-2'/>
+    <pointer-type-def type-id='type-id-1' size-in-bits='64' alignment-in-bits='64' id='type-id-5'/>
+    <var-decl name='s0' type-id='type-id-5' mangled-name='s0' visibility='default'/>
+    <function-type size-in-bits='64' alignment-in-bits='8' id='type-id-4'>
+      <parameter type-id='type-id-5'/>
+      <return type-id='type-id-3'/>
+    </function-type>
+  </abi-instr>
+</abi-corpus>
diff --git a/tests/data/test-read-ctf/test-callback2.c b/tests/data/test-read-ctf/test-callback2.c
new file mode 100644
index 00000000..6d33780f
--- /dev/null
+++ b/tests/data/test-read-ctf/test-callback2.c
@@ -0,0 +1,11 @@ 
+/* Test a simple callback as a struct member
+ * that takes a pointer to a struct parent as
+ * argument
+ * gcc -gctf -c test-callback2.c -o test-callback2.o
+ */
+struct s0
+{
+  int (*mem_fun)(struct s0 *);
+};
+
+struct s0 *s0;
diff --git a/tests/data/test-read-ctf/test-forward-type-decl.abi b/tests/data/test-read-ctf/test-forward-type-decl.abi
new file mode 100644
index 00000000..21bb45c8
--- /dev/null
+++ b/tests/data/test-read-ctf/test-forward-type-decl.abi
@@ -0,0 +1,29 @@ 
+<abi-corpus version='2.1' path='data/test-read-ctf/test-forward-type-decl.o'>
+  <elf-variable-symbols>
+    <elf-symbol name='addr' size='8' type='object-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+  </elf-variable-symbols>
+  <abi-instr address-size='64' language='LANG_C'>
+    <class-decl name='address_space' size-in-bits='128' alignment-in-bits='64' is-struct='yes' visibility='default' id='type-id-1'>
+      <data-member access='public' layout-offset-in-bits='0'>
+        <var-decl name='rb_root' type-id='type-id-2' visibility='default'/>
+      </data-member>
+      <data-member access='public' layout-offset-in-bits='64'>
+        <var-decl name='page' type-id='type-id-3' visibility='default'/>
+      </data-member>
+    </class-decl>
+    <class-decl name='page' size-in-bits='64' alignment-in-bits='64' is-struct='yes' visibility='default' id='type-id-4'>
+      <data-member access='public' layout-offset-in-bits='0'>
+        <var-decl name='mapping' type-id='type-id-5' visibility='default'/>
+      </data-member>
+    </class-decl>
+    <class-decl name='rb_node' size-in-bits='64' alignment-in-bits='64' is-struct='yes' visibility='default' id='type-id-6'>
+      <data-member access='public' layout-offset-in-bits='0'>
+        <var-decl name='rb_left' type-id='type-id-2' visibility='default'/>
+      </data-member>
+    </class-decl>
+    <pointer-type-def type-id='type-id-1' size-in-bits='64' alignment-in-bits='64' id='type-id-5'/>
+    <pointer-type-def type-id='type-id-4' size-in-bits='64' alignment-in-bits='64' id='type-id-3'/>
+    <pointer-type-def type-id='type-id-6' size-in-bits='64' alignment-in-bits='64' id='type-id-2'/>
+    <var-decl name='addr' type-id='type-id-5' mangled-name='addr' visibility='default'/>
+  </abi-instr>
+</abi-corpus>
diff --git a/tests/data/test-read-ctf/test-forward-type-decl.c b/tests/data/test-read-ctf/test-forward-type-decl.c
new file mode 100644
index 00000000..742b24ad
--- /dev/null
+++ b/tests/data/test-read-ctf/test-forward-type-decl.c
@@ -0,0 +1,23 @@ 
+/* Test a forward type declaration as a struct member
+ * to exercise circular dependencies.
+ * gcc -gctf -c test-forward-type-decl.c -o \
+ *    test-forward-type-decl.o
+ */
+typedef struct page *page_t;
+
+struct rb_node {
+  struct rb_node *rb_left;
+};
+
+struct address_space;
+
+struct page {
+  struct address_space *mapping;
+};
+
+struct address_space {
+  struct rb_node *rb_root;
+  struct page *page;
+};
+
+struct address_space *addr;
diff --git a/tests/data/test-read-ctf/test-functions-declaration.abi b/tests/data/test-read-ctf/test-functions-declaration.abi
new file mode 100644
index 00000000..9ef05e44
--- /dev/null
+++ b/tests/data/test-read-ctf/test-functions-declaration.abi
@@ -0,0 +1,25 @@ 
+<abi-corpus version='2.1' path='data/test-read-ctf/test-functions-declaration.o'>
+  <elf-function-symbols>
+    <elf-symbol name='attribute_container_add_device' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+    <elf-symbol name='attribute_container_device_trigger' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+  </elf-function-symbols>
+  <abi-instr address-size='64' language='LANG_C'>
+    <type-decl name='int' size-in-bits='32' alignment-in-bits='32' id='type-id-1'/>
+    <type-decl name='long int' size-in-bits='64' alignment-in-bits='64' id='type-id-2'/>
+    <pointer-type-def type-id='type-id-3' size-in-bits='64' alignment-in-bits='64' id='type-id-4'/>
+    <function-decl name='attribute_container_add_device' visibility='default' binding='global' size-in-bits='64' alignment-in-bits='8'>
+      <parameter type-id='type-id-4'/>
+      <return type-id='type-id-5'/>
+    </function-decl>
+    <function-decl name='attribute_container_device_trigger' visibility='default' binding='global' size-in-bits='64' alignment-in-bits='8'>
+      <parameter type-id='type-id-4'/>
+      <return type-id='type-id-5'/>
+    </function-decl>
+    <function-type size-in-bits='64' alignment-in-bits='8' id='type-id-3'>
+      <parameter type-id='type-id-1'/>
+      <parameter type-id='type-id-2'/>
+      <return type-id='type-id-5'/>
+    </function-type>
+    <type-decl name='void' id='type-id-5'/>
+  </abi-instr>
+</abi-corpus>
diff --git a/tests/data/test-read-ctf/test-functions-declaration.c b/tests/data/test-read-ctf/test-functions-declaration.c
new file mode 100644
index 00000000..36776bbb
--- /dev/null
+++ b/tests/data/test-read-ctf/test-functions-declaration.c
@@ -0,0 +1,16 @@ 
+/* Test declaring twice a function pinter a struct member
+ * gcc -gctf -c test-functions-declaration.c -o \
+ *    test-functions-declaration.o
+ */
+void
+attribute_container_add_device(
+          void (*fn1)(int, long))
+{
+}
+
+void
+attribute_container_device_trigger(
+       void (*fn2)(int , long))
+{
+
+}
diff --git a/tests/data/test-read-ctf/test-list-struct.abi b/tests/data/test-read-ctf/test-list-struct.abi
new file mode 100644
index 00000000..196a79a1
--- /dev/null
+++ b/tests/data/test-read-ctf/test-list-struct.abi
@@ -0,0 +1,20 @@ 
+<abi-corpus version='2.1' path='data/test-read-ctf/test-list-struct.o'>
+  <elf-variable-symbols>
+    <elf-symbol name='n1' size='16' type='object-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+    <elf-symbol name='n2' size='16' type='object-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+  </elf-variable-symbols>
+  <abi-instr address-size='64' language='LANG_C'>
+    <class-decl name='rb_node_b' size-in-bits='128' alignment-in-bits='64' is-struct='yes' visibility='default' id='type-id-1'>
+      <data-member access='public' layout-offset-in-bits='0'>
+        <var-decl name='this' type-id='type-id-2' visibility='default'/>
+      </data-member>
+      <data-member access='public' layout-offset-in-bits='64'>
+        <var-decl name='a' type-id='type-id-3' visibility='default'/>
+      </data-member>
+    </class-decl>
+    <type-decl name='int' size-in-bits='32' alignment-in-bits='32' id='type-id-3'/>
+    <pointer-type-def type-id='type-id-1' size-in-bits='64' alignment-in-bits='64' id='type-id-2'/>
+    <var-decl name='n1' type-id='type-id-1' mangled-name='n1' visibility='default'/>
+    <var-decl name='n2' type-id='type-id-1' mangled-name='n2' visibility='default'/>
+  </abi-instr>
+</abi-corpus>
diff --git a/tests/data/test-read-ctf/test-list-struct.c b/tests/data/test-read-ctf/test-list-struct.c
new file mode 100644
index 00000000..a1161923
--- /dev/null
+++ b/tests/data/test-read-ctf/test-list-struct.c
@@ -0,0 +1,11 @@ 
+/* Test a ADT where a struct member is a pointer to
+ * itself.
+ * gcc -gctf -c test-callback.c -o test-callback.o
+ */
+
+struct rb_node_b {
+  struct rb_node_b *this;
+  int a;
+};
+
+struct rb_node_b n1, n2;
diff --git a/tests/test-read-ctf.cc b/tests/test-read-ctf.cc
index b448ddc5..7f5381f6 100644
--- a/tests/test-read-ctf.cc
+++ b/tests/test-read-ctf.cc
@@ -220,6 +220,54 @@  static InOutSpec in_out_specs[] =
     "data/test-read-ctf/PR27700/test-PR27700.abi",
     "output/test-read-ctf/PR27700/test-PR27700.abi",
   },
+  {
+    "data/test-read-ctf/test-callback.o",
+    "",
+    "",
+    SEQUENCE_TYPE_ID_STYLE,
+    "data/test-read-ctf/test-callback.abi",
+    "output/test-read-ctf/test-callback.abi",
+  },
+  {
+    "data/test-read-ctf/test-array-of-pointers.o",
+    "",
+    "",
+    SEQUENCE_TYPE_ID_STYLE,
+    "data/test-read-ctf/test-array-of-pointers.abi",
+    "output/test-read-ctf/test-array-of-pointers.abi",
+  },
+  {
+    "data/test-read-ctf/test-functions-declaration.o",
+    "",
+    "",
+    SEQUENCE_TYPE_ID_STYLE,
+    "data/test-read-ctf/test-functions-declaration.abi",
+    "output/test-read-ctf/test-functions-declaration.abi",
+  },
+  {
+    "data/test-read-ctf/test-forward-type-decl.o",
+    "",
+    "",
+    SEQUENCE_TYPE_ID_STYLE,
+    "data/test-read-ctf/test-forward-type-decl.abi",
+    "output/test-read-ctf/test-forward-type-decl.abi",
+  },
+  {
+    "data/test-read-ctf/test-list-struct.o",
+    "",
+    "",
+    SEQUENCE_TYPE_ID_STYLE,
+    "data/test-read-ctf/test-list-struct.abi",
+    "output/test-read-ctf/test-list-struct.abi",
+  },
+  {
+    "data/test-read-ctf/test-callback2.o",
+    "",
+    "",
+    SEQUENCE_TYPE_ID_STYLE,
+    "data/test-read-ctf/test-callback2.abi",
+    "output/test-read-ctf/test-callback2.abi",
+  },
   // This should be the last entry.
   {NULL, NULL, NULL, SEQUENCE_TYPE_ID_STYLE, NULL, NULL}
 };