[4/4] Emit & read undefined interfaces to & from ABIXML

Message ID 87cyrw3fzy.fsf@redhat.com
State New
Headers
Series Support undefined interfaces to fix abicompt's weak mode |

Commit Message

Dodji Seketeli March 14, 2024, 5 p.m. UTC
  Hello,

This patch teaches the ABIXML writer to emit information about
undefined interfaces.  It also teaches the ABIXML reader to read
information about undefined interfaces.

It introduces two new ABIXML elements:
'undefined-elf-function-symbols' and 'undefined-elf-variable-symbols'
to represent undefined function and variable symbols.

Then, in the 'abi-instr' element functions and variables that
reference undefined elf symbols now have an 'elf-symbol-id' attribute
referencing the undefined symbol listed in the new
'undefined-elf-variable-symbols' or 'undefined-elf-function-symbols'
element.

The patch introduces tests that perform compatibility checks done on
ABIXML files.

	* include/abg-writer.h (set_write_undefined_symbols): Declare new
	function.
	(set_common_options): Use the new set_write_undefined_symbols in
	this function template.
	* src/abg-dwarf-reader.cc (reader::{get_die_language, die_is_in_c,
	die_is_in_cplus_plus, die_is_in_c_or_cplusplus}): Move these
	member functions into ...
	(get_die_language, die_is_in_c, die_is_in_cplus_plus)
	(die_is_in_c_or_cplusplus): ... these static non-member functions.
	(fn_die_equal_by_linkage_name): Adjust and remove the now useless
	reader parameter.
	(compare_dies, get_scope_die, function_is_suppressed)
	(variable_is_suppressed): Adjust.
	(build_translation_unit_and_add_to_ir): When we are asked to load
	undefined symbol, make sure to also analyze top-level class types
	and if we are in C++, also analyze top-level unions and structs as
	these might also have some undefined interfaces.
	* src/abg-reader.cc (build_elf_symbol_db): Let's not construct and
	return the symbol DB anymore.  Rather, let's let the caller
	construct it, so we can just update it with the input gathered.
	(read_symbol_db_from_input): Support getting undefined function
	and variable symbols from the new undefined-elf-function-symbols
	and undefined-elf-variable-symbols elements.  Note that undefined
	and defined function symbols go into the same symbol DB whereas
	undefined and defined variable symbols go into another symbol DB.
	Now, we suppose that the variable & symbol DBs are allocated by
	the caller.  We pass it down to build_elf_symbol_db that populates
	it.  Maybe we should rename build_elf_symbol_db into
	populate_elf_symbol_db.
	(reader::read_corpus): Allocate the function
	and variable symbol DB and let read_symbol_db_from_input populate
	it.  Sort functions and variables after reading the whole ABIXML.
	* src/abg-writer.cc (write_context::write_context): Define new
	data member.
	(write_context::write_context): Initialize it.
	(write_context::{get,set}::write_undefined_symbols): Define
	accessors.
	(set_write_undefined_symbols): Define a new function.
	(write_context::decl_is_emitted): Add a new overload.
	(write_elf_symbol_reference): Add a writer context and a corpus
	parameter.  If the symbol is not in the corpus or if the symbol is
	undefined and we were not asked to emit undefined symbols then do
	not emit any reference to it.
	(write_translation_unit): Emit the undefined functions and
	variables that belong to the current translation unit, along with
	their reference to the undefined ELF symbol they are associated
	to.
	(write_var_decl, write_function_decl): Let
	write_elf_symbol_reference decide whether it should emit the
	reference to ELF symbol or not, as it now know how to make that
	decision.
	(write_corpus): Write the undefined function & variable ELF symbol
	data bases.  These in the new 'undefined-elf-function-symbols' and
	'undefined-elf-variable-symbols' elements.
	* tools/abidw.cc (options::load_undefined_interfaces): Define new
	data member.
	(options:options): Initialize it.
	(display_usage): Add a help string for the
	--no-load-undefined-interfaces option.
	(parse_command_line): Parse the --no-load-undefined-interfaces
	option.
	(set_generic_options): Set the
	fe_iface::option_type::load_undefined_interfaces option.
	* doc/manuals/abidw.rst: Document the new
	--no-load-undefined-interfaces of abidw.
	* tests/data/test-abicompat/test10/libtest10-with-exported-symbols.so:
	New binary input file.
	* tests/data/test-abicompat/test10/libtest10-with-incompatible-exported-symbols.so:
	New binary input file.
	* tests/data/test-abicompat/test10/libtest10-with-incompatible-exported-symbols.so.abi:
	New abixml input file.
	* tests/data/test-abicompat/test10/test10-app-with-undefined-symbols:
	New binary input file.
	* tests/data/test-abicompat/test10/test10-app-with-undefined-symbols.abi:
	New abixml input file.
	* tests/data/test-abicompat/test10/test10-app-with-undefined-symbols.cc:
	New source file for binary test input
	* tests/data/test-abicompat/test10/test10-fn-changed-report-0.txt:
	New source file for binary test input
	* tests/data/test-abicompat/test10/test10-fn-changed-report-1.txt:
	New source file for binary test input
	* tests/data/test-abicompat/test10/test10-fn-changed-report-2.txt:
	New source file for binary test input
	* tests/data/test-abicompat/test10/test10-fn-changed-report-3.txt:
	New source file for binary test input
	* tests/data/test-abicompat/test10/test10-fn-changed-report-4.txt:
	New source file for binary test input
	* tests/data/test-abicompat/test10/test10-with-exported-symbols.cc:
	New source file for binary test input
	* tests/data/test-abicompat/test10/test10-with-exported-symbols.h:
	New source file for binary test input
	* tests/data/test-abicompat/test10/test10-with-incompatible-exported-symbols.cc:
	New source file for binary test input
	* tests/data/test-abicompat/test10/test10-with-incompatible-exported-symbols.h:
	New source file for binary test input.
	* tests/data/Makefile.am: Add new test input files to source
	distribution.
	* tests/test-abicompat.cc (in_out_specs): Add the new test inputs
	to this test harness.
	* tests/test-annotate.cc (main): Use the new
	--no-load-undefined-interfaces option of abidw to keep the old
	behavior.
	* tests/test-read-common.cc (test_task::serialize_corpus): Do not
	emit undefined symbols.
	* tests/test-read-dwarf.cc (test_task_dwarf::perform): Likewise.

Signed-off-by: Dodji Seketeli <dodji@redhat.com>
---
 doc/manuals/abidw.rst                         |   10 +
 include/abg-writer.h                          |    4 +
 ...-undefined-interfaces-to-from-ABIXML.patch | 1051 +++++++++++++++++
 src/abg-dwarf-reader.cc                       |  189 +--
 src/abg-reader.cc                             |   81 +-
 src/abg-writer.cc                             |  152 ++-
 tests/data/Makefile.am                        |   15 +
 .../test10/libtest10-with-exported-symbols.so |  Bin 0 -> 18288 bytes
 ...st10-with-incompatible-exported-symbols.so |  Bin 0 -> 18336 bytes
 ...-with-incompatible-exported-symbols.so.abi |   65 +
 .../test10/test10-app-with-undefined-symbols  |  Bin 0 -> 26072 bytes
 .../test10-app-with-undefined-symbols.abi     |   55 +
 .../test10-app-with-undefined-symbols.cc      |   13 +
 .../test10/test10-fn-changed-report-0.txt     |    0
 .../test10/test10-fn-changed-report-1.txt     |   13 +
 .../test10/test10-fn-changed-report-2.txt     |   13 +
 .../test10/test10-fn-changed-report-3.txt     |   13 +
 .../test10/test10-fn-changed-report-4.txt     |   13 +
 .../test10/test10-with-exported-symbols.cc    |   20 +
 .../test10/test10-with-exported-symbols.h     |   14 +
 ...st10-with-incompatible-exported-symbols.cc |   20 +
 ...est10-with-incompatible-exported-symbols.h |   14 +
 tests/test-abicompat.cc                       |   54 +
 tests/test-annotate.cc                        |    2 +-
 tests/test-read-common.cc                     |    1 +
 tests/test-read-dwarf.cc                      |    2 +-
 tools/abidw.cc                                |    7 +
 27 files changed, 1698 insertions(+), 123 deletions(-)
 create mode 100644 patches/0001-WIP-Emit-read-undefined-interfaces-to-from-ABIXML.patch
 create mode 100755 tests/data/test-abicompat/test10/libtest10-with-exported-symbols.so
 create mode 100755 tests/data/test-abicompat/test10/libtest10-with-incompatible-exported-symbols.so
 create mode 100644 tests/data/test-abicompat/test10/libtest10-with-incompatible-exported-symbols.so.abi
 create mode 100755 tests/data/test-abicompat/test10/test10-app-with-undefined-symbols
 create mode 100644 tests/data/test-abicompat/test10/test10-app-with-undefined-symbols.abi
 create mode 100644 tests/data/test-abicompat/test10/test10-app-with-undefined-symbols.cc
 create mode 100644 tests/data/test-abicompat/test10/test10-fn-changed-report-0.txt
 create mode 100644 tests/data/test-abicompat/test10/test10-fn-changed-report-1.txt
 create mode 100644 tests/data/test-abicompat/test10/test10-fn-changed-report-2.txt
 create mode 100644 tests/data/test-abicompat/test10/test10-fn-changed-report-3.txt
 create mode 100644 tests/data/test-abicompat/test10/test10-fn-changed-report-4.txt
 create mode 100644 tests/data/test-abicompat/test10/test10-with-exported-symbols.cc
 create mode 100644 tests/data/test-abicompat/test10/test10-with-exported-symbols.h
 create mode 100644 tests/data/test-abicompat/test10/test10-with-incompatible-exported-symbols.cc
 create mode 100644 tests/data/test-abicompat/test10/test10-with-incompatible-exported-symbols.h

new file mode 100755
index 0000000000000000000000000000000000000000..d63e70b4677ab56154d66bb64da943bbc100e15a
GIT binary patch
literal 18336
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
SxbHGXw0W1sP1p=<toRQ+hDclh

literal 0
HcmV?d00001

new file mode 100644
index 00000000..055c1e85
  

Patch

diff --git a/doc/manuals/abidw.rst b/doc/manuals/abidw.rst
index 13db912f..e84047c5 100644
--- a/doc/manuals/abidw.rst
+++ b/doc/manuals/abidw.rst
@@ -329,6 +329,16 @@  Options
     makes ``abidw`` load *all* the types defined in the binaries, even
     those that are not reachable from public declarations.
 
+    * ``--no-load-undefined-interfaces``
+
+    By default, ``libabigail`` (and thus ``abidw``) loads information
+    about undefined function and variable symbols as well as functions
+    and variables that are associated with those undefined symbols.
+    Those are called undefined interfaces.  This option however makes
+    makes ``abidw`` avoid loading information about undefined
+    interfaces.  The resulting XML file thus doesn't contain
+    information about those undefined interfaces.
+
   *  ``--abidiff``
 
     Load the ABI of the ELF binary given in argument, save it in
diff --git a/include/abg-writer.h b/include/abg-writer.h
index 540487e0..45508544 100644
--- a/include/abg-writer.h
+++ b/include/abg-writer.h
@@ -57,6 +57,9 @@  set_write_comp_dir(write_context& ctxt, bool flag);
 void
 set_write_elf_needed(write_context& ctxt, bool flag);
 
+void
+set_write_undefined_symbols(write_context& ctxt, bool flag);
+
 void
 set_write_default_sizes(write_context& ctxt, bool flag);
 
@@ -88,6 +91,7 @@  set_common_options(write_context& ctxt, const OPTS& opts)
   set_write_corpus_path(ctxt, opts.write_corpus_path);
   set_write_comp_dir(ctxt, opts.write_comp_dir);
   set_write_elf_needed(ctxt, opts.write_elf_needed);
+  set_write_undefined_symbols(ctxt, opts.load_undefined_interfaces);
   set_write_parameter_names(ctxt, opts.write_parameter_names);
   set_short_locs(ctxt, opts.short_locs);
   set_write_default_sizes(ctxt, opts.default_sizes);
diff --git a/patches/0001-WIP-Emit-read-undefined-interfaces-to-from-ABIXML.patch b/patches/0001-WIP-Emit-read-undefined-interfaces-to-from-ABIXML.patch
new file mode 100644
index 00000000..218820e1
--- /dev/null
+++ b/patches/0001-WIP-Emit-read-undefined-interfaces-to-from-ABIXML.patch
@@ -0,0 +1,1051 @@ 
+From cab35084da3550474f5349cfc2e65d36a7f3f91b Mon Sep 17 00:00:00 2001
+From: Dodji Seketeli <dodji@redhat.com>
+Date: Wed, 6 Mar 2024 13:47:14 +0100
+Subject: [PATCH] WIP: Emit & read undefined interfaces to & from ABIXML
+
+This work-in-progress patch teaches the ABIXML writer to emit
+information about undefined interfaces.  It also teaches the ABIXML
+reader to read information about undefined interfaces.
+
+It introduces two new ABIXML elements:
+'undefined-elf-function-symbols' and 'undefined-elf-variable-symbols'
+to represent undefined function and variable symbols.
+
+Then, in the 'abi-instr' element functions and variables that
+reference undefined elf symbols now have an 'elf-symbol-id' attribute
+referencing the undefined symbol listed in the new
+'undefined-elf-variable-symbols' or 'undefined-elf-function-symbols'
+element.
+
+This work-in-progress branch comes on top of the users/dodji/abidb
+branch and should be merged into it at some point.
+
+TODO: add some tests.
+
+	* include/abg-writer.h (set_write_undefined_symbols): Declare new
+	function.
+	(set_common_options): Use the new set_write_undefined_symbols in
+	this function template.
+	* src/abg-corpus.cc (corpus::lookup_{function,variable}_symbol):
+	Lookup the symbol also among undefined symbols, not just among
+	defined symbols.
+	* src/abg-dwarf-reader.cc (reader::{get_die_language, die_is_in_c,
+	die_is_in_cplus_plus, die_is_in_c_or_cplusplus}): Move these
+	member functions into ...
+	(get_die_language, die_is_in_c, die_is_in_cplus_plus)
+	(die_is_in_c_or_cplusplus): ... these static non-member functions.
+	(fn_die_equal_by_linkage_name): Adjust and remove the now useless
+	reader parameter.
+	(compare_dies, get_scope_die, function_is_suppressed)
+	(variable_is_suppressed): Adjust.
+	(build_translation_unit_and_add_to_ir): When we are asked to load
+	undefined symbol, make sure to also analyze top-level class types
+	and if we are in C++, also analyze top-level unions and structs as
+	these might also have some undefined interfaces.
+	* src/abg-reader.cc (build_elf_symbol_db): Let's not construct and
+	return the symbol DB anymore.  Rather, let's let the caller
+	construct it, so we can just update it with the input gathered.
+	(read_symbol_db_from_input): Support getting undefined function
+	and variable symbols from the new undefined-elf-function-symbols
+	and undefined-elf-variable-symbols elements.  Note that undefined
+	and defined function symbols go into the same symbol DB whereas
+	undefined and defined variable symbols go into another symbol DB.
+	Now, we suppose that the variable & symbol DBs are allocated by
+	the caller.  We pass it down to build_elf_symbol_db that populates
+	it.  Maybe we should rename build_elf_symbol_db into
+	populate_elf_symbol_db.
+	(reader::read_corpus): Allocate the function
+	and variable symbol DB and let read_symbol_db_from_input populate
+	it.  Sort functions and variables after reading the whole ABIXML.
+	* src/abg-writer.cc (write_context::write_context): Define new
+	data member.
+	(write_context::write_context): Initialize it.
+	(write_context::{get,set}::write_undefined_symbols): Define
+	accessors.
+	(set_write_undefined_symbols): Define a new function.
+	(write_context::decl_is_emitted): Add a new overload.
+	(write_elf_symbol_reference): Add a writer context and a corpus
+	parameter.  If the symbol is not in the corpus or if the symbol is
+	undefined and we were not asked to emit undefined symbols then do
+	not emit any reference to it.
+	(write_translation_unit): Emit the undefined functions and
+	variables that belong to the current translation unit, along with
+	their reference to the undefined ELF symbol they are associated
+	to.
+	(write_var_decl, write_function_decl): Let
+	write_elf_symbol_reference decide whether it should emit the
+	reference to ELF symbol or not, as it now know how to make that
+	decision.
+	(write_corpus): Write the undefined function & variable ELF symbol
+	data bases.  These in the new 'undefined-elf-function-symbols' and
+	'undefined-elf-variable-symbols' elements.
+	* tools/abidw.cc (options::load_undefined_interfaces): Define new
+	data member.
+	(options:options): Initialize it.
+	(display_usage): Add a help string for the
+	--no-load-undefined-interfaces option.
+	(parse_command_line): Parse the --no-load-undefined-interfaces
+	option.
+	(set_generic_options): Set the
+	fe_iface::option_type::load_undefined_interfaces option.
+	* doc/manuals/abidw.rst: Document the new
+	--no-load-undefined-interfaces of abidw.
+
+Signed-off-by: Dodji Seketeli <dodji@redhat.com>
+---
+ doc/manuals/abidw.rst    |  10 +++
+ include/abg-writer.h     |   4 +
+ src/abg-corpus.cc        |  38 +++++---
+ src/abg-dwarf-reader.cc  | 189 +++++++++++++++++++++------------------
+ src/abg-reader.cc        |  81 +++++++++++------
+ src/abg-writer.cc        | 152 +++++++++++++++++++++++++++++--
+ tests/test-annotate.cc   |   2 +-
+ tests/test-read-dwarf.cc |   2 +-
+ tools/abidw.cc           |   7 ++
+ 9 files changed, 350 insertions(+), 135 deletions(-)
+
+diff --git a/doc/manuals/abidw.rst b/doc/manuals/abidw.rst
+index 1e308e66..6c399675 100644
+--- a/doc/manuals/abidw.rst
++++ b/doc/manuals/abidw.rst
+@@ -329,6 +329,16 @@ Options
+     makes ``abidw`` load *all* the types defined in the binaries, even
+     those that are not reachable from public declarations.
+ 
++    * ``--no-load-undefined-interfaces``
++
++    By default, ``libabigail`` (and thus ``abidw``) loads information
++    about undefined function and variable symbols as well as functions
++    and variables that are associated with those undefined symbols.
++    Those are called undefined interfaces.  This option however makes
++    makes ``abidw`` avoid loading information about undefined
++    interfaces.  The resulting XML file thus doesn't contain
++    information about those undefined interfaces.
++
+   *  ``--abidiff``
+ 
+     Load the ABI of the ELF binary given in argument, save it in
+diff --git a/include/abg-writer.h b/include/abg-writer.h
+index 540487e0..45508544 100644
+--- a/include/abg-writer.h
++++ b/include/abg-writer.h
+@@ -57,6 +57,9 @@ set_write_comp_dir(write_context& ctxt, bool flag);
+ void
+ set_write_elf_needed(write_context& ctxt, bool flag);
+ 
++void
++set_write_undefined_symbols(write_context& ctxt, bool flag);
++
+ void
+ set_write_default_sizes(write_context& ctxt, bool flag);
+ 
+@@ -88,6 +91,7 @@ set_common_options(write_context& ctxt, const OPTS& opts)
+   set_write_corpus_path(ctxt, opts.write_corpus_path);
+   set_write_comp_dir(ctxt, opts.write_comp_dir);
+   set_write_elf_needed(ctxt, opts.write_elf_needed);
++  set_write_undefined_symbols(ctxt, opts.load_undefined_interfaces);
+   set_write_parameter_names(ctxt, opts.write_parameter_names);
+   set_short_locs(ctxt, opts.short_locs);
+   set_write_default_sizes(ctxt, opts.default_sizes);
+diff --git a/src/abg-corpus.cc b/src/abg-corpus.cc
+index e99b23fe..76c6bbb1 100644
+--- a/src/abg-corpus.cc
++++ b/src/abg-corpus.cc
+@@ -1208,13 +1208,16 @@ corpus::get_undefined_var_symbol_map() const
+ const elf_symbol_sptr
+ corpus::lookup_function_symbol(const string& n) const
+ {
+-  if (get_fun_symbol_map().empty())
++  if (get_fun_symbol_map().empty() && get_undefined_fun_symbol_map().empty())
+     return elf_symbol_sptr();
+ 
+-  string_elf_symbols_map_type::const_iterator it =
+-    get_fun_symbol_map().find(n);
++  string_elf_symbols_map_type::const_iterator it = get_fun_symbol_map().find(n);
+   if ( it == get_fun_symbol_map().end())
+-    return elf_symbol_sptr();
++    {
++      it = get_undefined_fun_symbol_map().find(n);
++      if (it == get_undefined_fun_symbol_map().end())
++	return elf_symbol_sptr();
++    }
+   return it->second[0];
+ }
+ 
+@@ -1275,13 +1278,17 @@ const elf_symbol_sptr
+ corpus::lookup_function_symbol(const string& symbol_name,
+ 			       const elf_symbol::version& version) const
+ {
+-  if (get_fun_symbol_map().empty())
++  if (get_fun_symbol_map().empty() && get_undefined_fun_symbol_map().empty())
+     return elf_symbol_sptr();
+ 
+   string_elf_symbols_map_type::const_iterator it =
+     get_fun_symbol_map().find(symbol_name);
+   if ( it == get_fun_symbol_map().end())
+-    return elf_symbol_sptr();
++    {
++      it = get_undefined_fun_symbol_map().find(symbol_name);
++      if (it == get_undefined_fun_symbol_map().end())
++	return elf_symbol_sptr();
++    }
+ 
+   return find_symbol_by_version(version, it->second);
+ }
+@@ -1304,13 +1311,16 @@ corpus::lookup_function_symbol(const elf_symbol& symbol) const
+ const elf_symbol_sptr
+ corpus::lookup_variable_symbol(const string& n) const
+ {
+-  if (get_var_symbol_map().empty())
++  if (get_var_symbol_map().empty() && get_undefined_var_symbol_map().empty())
+     return elf_symbol_sptr();
+ 
+-  string_elf_symbols_map_type::const_iterator it =
+-    get_var_symbol_map().find(n);
++  string_elf_symbols_map_type::const_iterator it = get_var_symbol_map().find(n);
+   if ( it == get_var_symbol_map().end())
+-    return elf_symbol_sptr();
++    {
++      it = get_undefined_var_symbol_map().find(n);
++      if (it == get_undefined_var_symbol_map().end())
++	return elf_symbol_sptr();
++    }
+   return it->second[0];
+ }
+ 
+@@ -1326,13 +1336,17 @@ const elf_symbol_sptr
+ corpus::lookup_variable_symbol(const string& symbol_name,
+ 			       const elf_symbol::version& version) const
+ {
+-  if (get_var_symbol_map().empty())
++  if (get_var_symbol_map().empty() && get_undefined_var_symbol_map().empty())
+     return elf_symbol_sptr();
+ 
+   string_elf_symbols_map_type::const_iterator it =
+     get_var_symbol_map().find(symbol_name);
+   if ( it == get_var_symbol_map().end())
+-    return elf_symbol_sptr();
++    {
++      it = get_undefined_var_symbol_map().find(symbol_name);
++      if (it == get_undefined_var_symbol_map().end())
++	return elf_symbol_sptr();
++    }
+ 
+   return find_symbol_by_version(version, it->second);
+ }
+diff --git a/src/abg-dwarf-reader.cc b/src/abg-dwarf-reader.cc
+index d0aa8c29..89c7be3b 100644
+--- a/src/abg-dwarf-reader.cc
++++ b/src/abg-dwarf-reader.cc
+@@ -379,6 +379,18 @@ get_scope_die(const reader&	rdr,
+ 	      size_t			where_offset,
+ 	      Dwarf_Die&		scope_die);
+ 
++static bool
++get_die_language(const Dwarf_Die *die, translation_unit::language &lang) ;
++
++static bool
++die_is_in_c(const Dwarf_Die *die);
++
++static bool
++die_is_in_cplus_plus(const Dwarf_Die *die);
++
++static bool
++die_is_in_c_or_cplusplus(const Dwarf_Die *die);
++
+ static bool
+ die_is_anonymous(const Dwarf_Die* die);
+ 
+@@ -654,6 +666,75 @@ compare_dies_during_canonicalization(reader& rdr,
+ static bool
+ get_member_child_die(const Dwarf_Die *die, Dwarf_Die *child);
+ 
++/// Get the language used to generate a given DIE.
++///
++/// @param die the DIE to consider.
++///
++/// @param lang the resulting language.
++///
++/// @return true iff the language of the DIE was found.
++static bool
++get_die_language(const Dwarf_Die *die, translation_unit::language &lang) 
++{
++  Dwarf_Die cu_die;
++  ABG_ASSERT(dwarf_diecu(const_cast<Dwarf_Die*>(die), &cu_die, 0, 0));
++
++  uint64_t l = 0;
++  if (!die_unsigned_constant_attribute(&cu_die, DW_AT_language, l))
++    return false;
++
++  lang = dwarf_language_to_tu_language(l);
++  return true;
++}
++
++/// Test if a given DIE originates from a program written in the C
++/// language.
++///
++/// @param die the DIE to consider.
++///
++/// @return true iff @p die originates from a program in the C
++/// language.
++static bool
++die_is_in_c(const Dwarf_Die *die)
++{
++  translation_unit::language l = translation_unit::LANG_UNKNOWN;
++  if (!get_die_language(die, l))
++    return false;
++  return is_c_language(l);
++}
++
++/// Test if a given DIE originates from a program written in the C++
++/// language.
++///
++/// @param die the DIE to consider.
++///
++/// @return true iff @p die originates from a program in the C++
++/// language.
++static bool
++die_is_in_cplus_plus(const Dwarf_Die *die)
++{
++  translation_unit::language l = translation_unit::LANG_UNKNOWN;
++  if (!get_die_language(die, l))
++    return false;
++  return is_cplus_plus_language(l);
++}
++
++/// Test if a given DIE originates from a program written either in
++/// C or C++.
++///
++/// @param die the DIE to consider.
++///
++/// @return true iff @p die originates from a program written either in
++/// C or C++.
++static bool
++die_is_in_c_or_cplusplus(const Dwarf_Die *die)
++{
++  translation_unit::language l = translation_unit::LANG_UNKNOWN;
++  if (!get_die_language(die, l))
++    return false;
++  return (is_cplus_plus_language(l) || is_c_language(l));
++}
++
+ /// Compare a symbol name against another name, possibly demangling
+ /// the symbol_name before performing the comparison.
+ ///
+@@ -3320,75 +3401,6 @@ public:
+     return i->second;
+   }
+ 
+-  /// Get the language used to generate a given DIE.
+-  ///
+-  /// @param die the DIE to consider.
+-  ///
+-  /// @param lang the resulting language.
+-  ///
+-  /// @return true iff the language of the DIE was found.
+-  bool
+-  get_die_language(const Dwarf_Die *die, translation_unit::language &lang) const
+-  {
+-    Dwarf_Die cu_die;
+-    ABG_ASSERT(dwarf_diecu(const_cast<Dwarf_Die*>(die), &cu_die, 0, 0));
+-
+-    uint64_t l = 0;
+-    if (!die_unsigned_constant_attribute(&cu_die, DW_AT_language, l))
+-      return false;
+-
+-    lang = dwarf_language_to_tu_language(l);
+-    return true;
+-  }
+-
+-  /// Test if a given DIE originates from a program written in the C
+-  /// language.
+-  ///
+-  /// @param die the DIE to consider.
+-  ///
+-  /// @return true iff @p die originates from a program in the C
+-  /// language.
+-  bool
+-  die_is_in_c(const Dwarf_Die *die) const
+-  {
+-    translation_unit::language l = translation_unit::LANG_UNKNOWN;
+-    if (!get_die_language(die, l))
+-      return false;
+-    return is_c_language(l);
+-  }
+-
+-  /// Test if a given DIE originates from a program written in the C++
+-  /// language.
+-  ///
+-  /// @param die the DIE to consider.
+-  ///
+-  /// @return true iff @p die originates from a program in the C++
+-  /// language.
+-  bool
+-  die_is_in_cplus_plus(const Dwarf_Die *die) const
+-  {
+-    translation_unit::language l = translation_unit::LANG_UNKNOWN;
+-    if (!get_die_language(die, l))
+-      return false;
+-    return is_cplus_plus_language(l);
+-  }
+-
+-  /// Test if a given DIE originates from a program written either in
+-  /// C or C++.
+-  ///
+-  /// @param die the DIE to consider.
+-  ///
+-  /// @return true iff @p die originates from a program written either in
+-  /// C or C++.
+-  bool
+-  die_is_in_c_or_cplusplus(const Dwarf_Die *die) const
+-  {
+-    translation_unit::language l = translation_unit::LANG_UNKNOWN;
+-    if (!get_die_language(die, l))
+-      return false;
+-    return (is_cplus_plus_language(l) || is_c_language(l));
+-  }
+-
+   /// Check if we can assume the One Definition Rule[1] to be relevant
+   /// for the current translation unit.
+   ///
+@@ -9818,7 +9830,7 @@ die_function_signature(const reader& rdr,
+ 
+   translation_unit::language lang;
+   bool has_lang = false;
+-  if ((has_lang = rdr.get_die_language(fn_die, lang)))
++  if ((has_lang = get_die_language(fn_die, lang)))
+     {
+       // In a binary originating from the C language, it's OK to use
+       // the linkage name of the function as a key for the map which
+@@ -10324,8 +10336,6 @@ compare_as_decl_and_type_dies(const reader &rdr,
+ /// in C++ for instance, that doesn't imply that the two functions are
+ /// equal.
+ ///
+-/// @param rdr the @ref reader to consider.
+-///
+ /// @param l the first function DIE to consider.
+ ///
+ /// @param r the second function DIE to consider.
+@@ -10333,8 +10343,7 @@ compare_as_decl_and_type_dies(const reader &rdr,
+ /// @return true iff the function represented by @p l have the same
+ /// linkage name as the function represented by @p r.
+ static bool
+-fn_die_equal_by_linkage_name(const reader &rdr,
+-			     const Dwarf_Die *l,
++fn_die_equal_by_linkage_name(const Dwarf_Die *l,
+ 			     const Dwarf_Die *r)
+ {
+   if (!!l != !!r)
+@@ -10352,8 +10361,8 @@ fn_die_equal_by_linkage_name(const reader &rdr,
+   string llinkage_name = die_linkage_name(l),
+     rlinkage_name = die_linkage_name(r);
+ 
+-  if (rdr.die_is_in_c_or_cplusplus(l)
+-      && rdr.die_is_in_c_or_cplusplus(r))
++  if (die_is_in_c_or_cplusplus(l)
++      && die_is_in_c_or_cplusplus(r))
+     {
+       if (!llinkage_name.empty() && !rlinkage_name.empty())
+ 	return llinkage_name == rlinkage_name;
+@@ -11277,19 +11286,18 @@ compare_dies(const reader& rdr,
+ 	rdr.compare_count_++;
+ 
+ 	if (l_tag == DW_TAG_subprogram
+-	    && !fn_die_equal_by_linkage_name(rdr, l, r))
++	    && !fn_die_equal_by_linkage_name(l, r))
+ 	  {
+ 	    SET_RESULT_TO_FALSE(result, l, r);
+ 	    break;
+ 	  }
+ 	else if (l_tag == DW_TAG_subprogram
+-		 && rdr.die_is_in_c(l) && rdr.die_is_in_c(r)
+-		 /*&& fn_die_equal_by_linkage_name(rdr, l, r)*/)
++		 && die_is_in_c(l) && die_is_in_c(r))
+ 	  {
+ 	    result = COMPARISON_RESULT_EQUAL;
+ 	    break;
+ 	  }
+-	else if (!rdr.die_is_in_c(l) && !rdr.die_is_in_c(r))
++	else if (!die_is_in_c(l) && !die_is_in_c(r))
+ 	  {
+ 	    // In C, we cannot have two different functions with the
+ 	    // same linkage name in a given binary.  But here we are
+@@ -11914,7 +11922,7 @@ get_scope_die(const reader&	rdr,
+     memcpy(&origin_die_mem, dye, sizeof(origin_die_mem));
+ 
+   translation_unit::language die_lang = translation_unit::LANG_UNKNOWN;
+-  rdr.get_die_language(die, die_lang);
++  get_die_language(die, die_lang);
+   if (is_c_language(die_lang)
+       || rdr.die_parent_map(rdr.get_die_source(die)).empty())
+     {
+@@ -11974,7 +11982,7 @@ get_scope_for_die(reader&	rdr,
+   const die_source source_of_die = rdr.get_die_source(die);
+ 
+   translation_unit::language die_lang = translation_unit::LANG_UNKNOWN;
+-  rdr.get_die_language(die, die_lang);
++  get_die_language(die, die_lang);
+   if (is_c_language(die_lang)
+       || rdr.die_parent_map(source_of_die).empty())
+     {
+@@ -12342,10 +12350,17 @@ build_translation_unit_and_add_to_ir(reader&	rdr,
+     return result;
+ 
+   result->set_is_constructed(false);
+-
++  int tag = dwarf_tag(&child);
+   do
+     if (rdr.load_undefined_interfaces()
+-	&& rdr.is_decl_die_with_undefined_symbol(&child))
++	&& (rdr.is_decl_die_with_undefined_symbol(&child)
++	    || tag == DW_TAG_class_type // Top-level classes might
++					// have undefined interfaces
++					// that need to be
++					// represented, so let's
++					// analyze them as well.
++	    || ((tag == DW_TAG_union_type || tag == DW_TAG_structure_type)
++		&& die_is_in_cplus_plus(&child))))
+       {
+ 	// Analyze undefined functions & variables for the purpose of
+ 	// analyzing compatibility matters.
+@@ -15013,7 +15028,7 @@ function_is_suppressed(const reader& rdr,
+ 
+   string fname = die_string_attribute(function_die, DW_AT_name);
+   string flinkage_name = die_linkage_name(function_die);
+-  if (flinkage_name.empty() && rdr.die_is_in_c(function_die))
++  if (flinkage_name.empty() && die_is_in_c(function_die))
+     flinkage_name = fname;
+   string qualified_name = build_qualified_name(scope, fname);
+ 
+@@ -15190,7 +15205,7 @@ variable_is_suppressed(const reader&		rdr,
+ 
+   string name = die_string_attribute(variable_die, DW_AT_name);
+   string linkage_name = die_linkage_name(variable_die);
+-  if (linkage_name.empty() && rdr.die_is_in_c(variable_die))
++  if (linkage_name.empty() && die_is_in_c(variable_die))
+     linkage_name = name;
+   string qualified_name = build_qualified_name(scope, name);
+ 
+@@ -16049,7 +16064,7 @@ build_ir_node_from_die(reader&	rdr,
+ 	bool var_is_cloned = false;
+ 
+ 	if (tag == DW_TAG_member)
+-	  ABG_ASSERT(!rdr.die_is_in_c(die));
++	  ABG_ASSERT(!die_is_in_c(die));
+ 
+ 	if (die_die_attribute(die, DW_AT_specification, spec_die, false)
+ 	    || (var_is_cloned = die_die_attribute(die, DW_AT_abstract_origin,
+diff --git a/src/abg-reader.cc b/src/abg-reader.cc
+index b525a137..a91cfaf4 100644
+--- a/src/abg-reader.cc
++++ b/src/abg-reader.cc
+@@ -1270,7 +1270,8 @@ public:
+     if (!needed.empty())
+       corp.set_needed(needed);
+ 
+-    string_elf_symbols_map_sptr fn_sym_db, var_sym_db;
++    string_elf_symbols_map_sptr fn_sym_db(new string_elf_symbols_map_type),
++      var_sym_db(new string_elf_symbols_map_type);
+ 
+     // Read the symbol databases.
+     read_symbol_db_from_input(*this, fn_sym_db, var_sym_db);
+@@ -1338,6 +1339,9 @@ public:
+ 	set_corpus_node(node);
+       }
+ 
++    corpus()->sort_functions();
++    corpus()->sort_variables();
++
+     status = STATUS_OK;
+     return corpus();
+   }
+@@ -1389,8 +1393,9 @@ build_elf_symbol(reader&, const xmlNodePtr, bool);
+ static elf_symbol_sptr
+ build_elf_symbol_from_reference(reader&, const xmlNodePtr);
+ 
+-static string_elf_symbols_map_sptr
+-build_elf_symbol_db(reader&, const xmlNodePtr, bool);
++static bool
++build_elf_symbol_db(reader&, const xmlNodePtr, bool,
++		    string_elf_symbols_map_sptr&);
+ 
+ static function_decl::parameter_sptr
+ build_function_parameter (reader&, const xmlNodePtr);
+@@ -1898,9 +1903,9 @@ read_translation_unit_from_input(fe_iface& iface)
+ ///
+ /// @return true upon successful parsing, false otherwise.
+ static bool
+-read_symbol_db_from_input(reader&		 rdr,
+-			  string_elf_symbols_map_sptr& fn_symdb,
+-			  string_elf_symbols_map_sptr& var_symdb)
++read_symbol_db_from_input(reader&			rdr,
++			  string_elf_symbols_map_sptr&	fn_symdb,
++			  string_elf_symbols_map_sptr&	var_symdb)
+ {
+   xml::reader_sptr reader = rdr.get_libxml_reader();
+   if (!reader)
+@@ -1917,13 +1922,20 @@ read_symbol_db_from_input(reader&		 rdr,
+ 	if (status != 1)
+ 	  return false;
+ 
+-	bool has_fn_syms = false, has_var_syms = false;
++	bool has_fn_syms = false, has_undefined_fn_syms = false,
++	  has_var_syms = false, has_undefined_var_syms = false;
+ 	if (xmlStrEqual (XML_READER_GET_NODE_NAME(reader).get(),
+ 			 BAD_CAST("elf-function-symbols")))
+ 	  has_fn_syms = true;
+ 	else if (xmlStrEqual (XML_READER_GET_NODE_NAME(reader).get(),
+ 			      BAD_CAST("elf-variable-symbols")))
+ 	  has_var_syms = true;
++	else if (xmlStrEqual (XML_READER_GET_NODE_NAME(reader).get(),
++			      BAD_CAST("undefined-elf-function-symbols")))
++	  has_undefined_fn_syms = true;
++	else if (xmlStrEqual (XML_READER_GET_NODE_NAME(reader).get(),
++			      BAD_CAST("undefined-elf-variable-symbols")))
++	  has_undefined_var_syms = true;
+ 	else
+ 	  break;
+ 
+@@ -1932,20 +1944,30 @@ read_symbol_db_from_input(reader&		 rdr,
+ 	  return false;
+ 
+ 	if (has_fn_syms)
+-	  fn_symdb = build_elf_symbol_db(rdr, node, true);
++	  build_elf_symbol_db(rdr, node, /*function_sym=*/true, fn_symdb);
++	else if (has_undefined_fn_syms)
++	  build_elf_symbol_db(rdr, node, /*function_sym=*/true, fn_symdb);
+ 	else if (has_var_syms)
+-	  var_symdb = build_elf_symbol_db(rdr, node, false);
++	  build_elf_symbol_db(rdr, node, /*function_sym=*/false, var_symdb);
++	else if (has_undefined_var_syms)
++	  build_elf_symbol_db(rdr, node, /*function_sym=*/false, var_symdb);
+ 
+ 	xmlTextReaderNext(reader.get());
+       }
+   else
+     for (xmlNodePtr n = rdr.get_corpus_node(); n; n = xmlNextElementSibling(n))
+       {
+-	bool has_fn_syms = false, has_var_syms = false;
++	bool has_fn_syms = false, has_undefined_fn_syms = false,
++	  has_var_syms = false, has_undefined_var_syms = false;
+ 	if (xmlStrEqual(n->name, BAD_CAST("elf-function-symbols")))
+ 	  has_fn_syms = true;
++	else if (xmlStrEqual(n->name, BAD_CAST("undefined-elf-function-symbols")))
++	  has_undefined_fn_syms = true;
+ 	else if (xmlStrEqual(n->name, BAD_CAST("elf-variable-symbols")))
+ 	  has_var_syms = true;
++	else if (xmlStrEqual(n->name,
++			     BAD_CAST("undefined-elf-variable-symbols")))
++	  has_undefined_var_syms = true;
+ 	else
+ 	  {
+ 	    rdr.set_corpus_node(n);
+@@ -1953,9 +1975,13 @@ read_symbol_db_from_input(reader&		 rdr,
+ 	  }
+ 
+ 	if (has_fn_syms)
+-	  fn_symdb = build_elf_symbol_db(rdr, n, true);
++	  build_elf_symbol_db(rdr, n, /*function_sym=*/true, fn_symdb);
++	else if (has_undefined_fn_syms)
++	  build_elf_symbol_db(rdr, n, /*function_sym=*/true, fn_symdb);
+ 	else if (has_var_syms)
+-	  var_symdb = build_elf_symbol_db(rdr, n, false);
++	  build_elf_symbol_db(rdr, n, /*function_sym=*/false, var_symdb);
++	else if (has_undefined_var_syms)
++	  build_elf_symbol_db(rdr, n, /*function_sym=*/false, var_symdb);
+ 	else
+ 	  break;
+       }
+@@ -3300,24 +3326,30 @@ build_elf_symbol_from_reference(reader& rdr, const xmlNodePtr node)
+ /// @param function_syms true if we should look for a function symbols
+ /// data base, false if we should look for a variable symbols data
+ /// base.
+-static string_elf_symbols_map_sptr
+-build_elf_symbol_db(reader& rdr,
+-		    const xmlNodePtr node,
+-		    bool function_syms)
++///
++/// @param map a pointer to the map to fill with the symbol database.
++///
++/// @return true if some elf symbols were found.
++static bool
++build_elf_symbol_db(reader&				rdr,
++		    const xmlNodePtr			node,
++		    bool				function_syms,
++		    string_elf_symbols_map_sptr&	map)
+ {
+-  string_elf_symbols_map_sptr map, nil;
+   string_elf_symbol_sptr_map_type id_sym_map;
+ 
+   if (!node)
+-    return nil;
++    return false;
+ 
+   if (function_syms
+-      && !xmlStrEqual(node->name, BAD_CAST("elf-function-symbols")))
+-    return nil;
++      && !xmlStrEqual(node->name, BAD_CAST("elf-function-symbols"))
++      && !xmlStrEqual(node->name, BAD_CAST("undefined-elf-function-symbols")))
++    return false;
+ 
+   if (!function_syms
+-      && !xmlStrEqual(node->name, BAD_CAST("elf-variable-symbols")))
+-    return nil;
++      && !xmlStrEqual(node->name, BAD_CAST("elf-variable-symbols"))
++      && !xmlStrEqual(node->name, BAD_CAST("undefined-elf-variable-symbols")))
++    return false;
+ 
+   rdr.set_corpus_node(node);
+ 
+@@ -3336,9 +3368,8 @@ build_elf_symbol_db(reader& rdr,
+       }
+ 
+   if (id_sym_map.empty())
+-    return nil;
++    return false;
+ 
+-  map.reset(new string_elf_symbols_map_type);
+   string_elf_symbols_map_type::iterator it;
+   for (string_elf_symbol_sptr_map_type::const_iterator i = id_sym_map.begin();
+        i != id_sym_map.end();
+@@ -3374,7 +3405,7 @@ build_elf_symbol_db(reader& rdr,
+ 	}
+     }
+ 
+-  return map;
++  return true;
+ }
+ 
+ /// Build a function parameter from a 'parameter' xml element node.
+diff --git a/src/abg-writer.cc b/src/abg-writer.cc
+index 408623ab..dafe706c 100644
+--- a/src/abg-writer.cc
++++ b/src/abg-writer.cc
+@@ -214,6 +214,7 @@ class write_context
+   bool					m_write_corpus_path;
+   bool					m_write_comp_dir;
+   bool					m_write_elf_needed;
++  bool					m_write_undefined_symbols;
+   bool					m_write_parameter_names;
+   bool					m_short_locs;
+   bool					m_write_default_sizes;
+@@ -252,6 +253,7 @@ public:
+       m_write_corpus_path(true),
+       m_write_comp_dir(true),
+       m_write_elf_needed(true),
++      m_write_undefined_symbols(true),
+       m_write_parameter_names(true),
+       m_short_locs(false),
+       m_write_default_sizes(true),
+@@ -325,6 +327,20 @@ public:
+   set_write_elf_needed(bool f)
+   {m_write_elf_needed = f;}
+ 
++  /// Getter of the "undefined-symbols" option.
++  ///
++  /// @return true iff undefined symbols shall be emitted.
++  bool
++  get_write_undefined_symbols() const
++  {return m_write_undefined_symbols;}
++
++  /// Setter of the "undefined-symbols" option.
++  ///
++  /// @param f true iff undefined symbols shall be emitted.
++  void
++  set_write_undefined_symbols(bool f)
++  {m_write_undefined_symbols = f;}
++
+   /// Getter of the default-sizes option.
+   ///
+   /// @return true iff default size-in-bits needs to be emitted
+@@ -793,6 +809,20 @@ public:
+   type_is_emitted(const type_base_sptr& t) const
+   {return type_is_emitted(t.get());}
+ 
++  /// Test if a given decl has been written out to the XML output.
++  ///
++  /// @param the decl to consider.
++  ///
++  /// @return true if the decl has already been emitted, false
++  /// otherwise.
++  bool
++  decl_is_emitted(const decl_base& decl) const
++  {
++    string repr = decl.get_pretty_representation(true);
++    interned_string irepr = decl.get_environment().intern(repr);
++    return m_emitted_decls_set.find(irepr) != m_emitted_decls_set.end();
++  }
++
+   /// Test if a given decl has been written out to the XML output.
+   ///
+   /// @param the decl to consider.
+@@ -901,8 +931,14 @@ static void write_voffset(function_decl_sptr, ostream&);
+ static void write_elf_symbol_type(elf_symbol::type, ostream&);
+ static void write_elf_symbol_binding(elf_symbol::binding, ostream&);
+ static bool write_elf_symbol_aliases(const elf_symbol&, ostream&);
+-static bool write_elf_symbol_reference(const elf_symbol&, ostream&);
+-static bool write_elf_symbol_reference(const elf_symbol_sptr, ostream&);
++static bool write_elf_symbol_reference(write_context&,
++				       const elf_symbol&,
++				       const corpus& abi,
++				       ostream&);
++static bool write_elf_symbol_reference(write_context&,
++				       const elf_symbol_sptr,
++				       const corpus& abi,
++				       ostream&);
+ static void write_is_declaration_only(const decl_base_sptr&, ostream&);
+ static void write_is_struct(const class_decl_sptr&, ostream&);
+ static void write_is_anonymous(const decl_base_sptr&, ostream&);
+@@ -1766,14 +1802,36 @@ write_elf_symbol_aliases(const elf_symbol& sym, ostream& out)
+ /// Write an XML attribute for the reference to a symbol for the
+ /// current decl.
+ ///
++///
++/// @param ctxt the current write context to consider.
++///
+ /// @param sym the symbol to consider.
+ ///
++/// @param abi the ABI corpus the symbol @p sym is supposed to belong
++/// to.  If the symbol doesn't belong to that corpus, then the
++/// reference is not be emitted.
++///
+ /// @param o the output stream to write the attribute to.
+ ///
+ /// @return true upon successful completion.
+ static bool
+-write_elf_symbol_reference(const elf_symbol& sym, ostream& o)
++write_elf_symbol_reference(write_context& ctxt,
++			   const elf_symbol& sym,
++			   const corpus& abi,
++			   ostream& o)
+ {
++  elf_symbol_sptr s = abi.lookup_function_symbol(sym);
++  if (!s)
++    s = abi.lookup_variable_symbol(sym);
++
++  if (// If that symbol wasn't found in the current corpus ...
++      !s
++      // ... or we were NOT asked to represent undefined symbols and
++      // yet that symbol is undefined ...
++      || (!ctxt.get_write_undefined_symbols() && !s->is_defined()))
++    // Then do not emit this symbol reference.
++    return false;
++
+   const elf_symbol* main = sym.get_main_symbol().get();
+   const elf_symbol* alias = &sym;
+   bool found = !alias->is_suppressed();
+@@ -1804,18 +1862,27 @@ write_elf_symbol_reference(const elf_symbol& sym, ostream& o)
+ /// Write an XML attribute for the reference to a symbol for the
+ /// current decl.
+ ///
++/// @param ctxt the write context to consider.
++/// 
+ /// @param sym the symbol to consider.
+ ///
++/// @param abi the ABI corpus the symbol @p sym is supposed to belong
++/// to.  If the symbol doesn't belong to that corpus, then the
++/// reference is not be emitted.
++///
+ /// @param o the output stream to write the attribute to.
+ ///
+ /// @return true upon successful completion.
+ static bool
+-write_elf_symbol_reference(const elf_symbol_sptr sym, ostream& o)
++write_elf_symbol_reference(write_context& ctxt,
++			   const elf_symbol_sptr sym,
++			   const corpus& abi,
++			   ostream& o)
+ {
+   if (!sym)
+     return false;
+ 
+-  return write_elf_symbol_reference(*sym, o);
++  return write_elf_symbol_reference(ctxt, *sym, abi, o);
+ }
+ 
+ /// Serialize the attributes "constructor", "destructor" or "static"
+@@ -2255,6 +2322,18 @@ void
+ set_write_elf_needed(write_context& ctxt, bool flag)
+ {ctxt.set_write_elf_needed(flag);}
+ 
++/// Set the 'undefined-symbols' flag.
++///
++/// When this flag is set then the XML writer will emit corpus
++/// information about the undefined function and variable symbols.
++///
++/// @param ctxt the context to set this flag on to.
++///
++/// @param flag the new value of the 'undefined-symbols' flag.
++void
++set_write_undefined_symbols(write_context& ctxt, bool flag)
++{ctxt.set_write_undefined_symbols(flag);}
++
+ /// Set the 'default-sizes' flag.
+ ///
+ /// When this flag is set then the XML writer will emit default
+@@ -2549,6 +2628,32 @@ write_translation_unit(write_context&		ctxt,
+ 	}
+     }
+ 
++  // Write the undefined functions that belong to this translation
++  // unit
++  if (const abigail::ir::corpus* abi = tu.get_corpus())
++    for (auto undefined_function : abi->get_sorted_undefined_functions())
++      {
++	function_decl_sptr f(const_cast<function_decl*>(undefined_function),
++			     noop_deleter());
++	if (f->get_translation_unit() != &tu || ctxt.decl_is_emitted(f))
++	  continue;
++
++	write_decl(f, ctxt, indent + c.get_xml_element_indent());
++      }
++
++  // Write the undefined variables that belong to this translation
++  // unit
++  if (const abigail::ir::corpus* abi = tu.get_corpus())
++    for (auto undefined_var : abi->get_sorted_undefined_variables())
++      {
++	var_decl_sptr v(const_cast<var_decl*>(undefined_var),
++			noop_deleter());
++	if (v->get_translation_unit() != &tu || ctxt.decl_is_emitted(v))
++	  continue;
++
++	write_decl(v, ctxt, indent + c.get_xml_element_indent());
++      }
++
+   write_referenced_types(ctxt, tu, indent, is_last);
+ 
+   // Now handle all function types that were not only referenced by
+@@ -3468,8 +3573,8 @@ write_var_decl(const var_decl_sptr& decl, write_context& ctxt,
+   write_location(decl, ctxt);
+ 
+   if (elf_symbol_sptr sym = decl->get_symbol())
+-    if (sym->is_defined())
+-      write_elf_symbol_reference(decl->get_symbol(), o);
++    if (corpus* abi = decl->get_corpus())
++      write_elf_symbol_reference(ctxt, decl->get_symbol(), *abi, o);
+ 
+   o << "/>\n";
+ 
+@@ -3526,8 +3631,8 @@ write_function_decl(const function_decl_sptr& decl, write_context& ctxt,
+ 			    : decl->get_translation_unit()->get_address_size()),
+ 			   0);
+   if (elf_symbol_sptr sym = decl->get_symbol())
+-    if (sym->is_defined())
+-      write_elf_symbol_reference(decl->get_symbol(), o);
++    if (corpus* abi = decl->get_corpus())
++      write_elf_symbol_reference(ctxt, decl->get_symbol(), *abi, o);
+ 
+   o << ">\n";
+ 
+@@ -4734,6 +4839,35 @@ write_corpus(write_context&	ctxt,
+       out << "</elf-variable-symbols>\n";
+     }
+ 
++  // Write the undefined function symbols database.
++  if (ctxt.get_write_undefined_symbols()
++      && !corpus->get_sorted_undefined_fun_symbols().empty())
++    {
++      do_indent_to_level(ctxt, indent, 1);
++      out << "<undefined-elf-function-symbols>\n";
++
++      write_elf_symbols_table(corpus->get_sorted_undefined_fun_symbols(), ctxt,
++			      get_indent_to_level(ctxt, indent, 2));
++
++      do_indent_to_level(ctxt, indent, 1);
++      out << "</undefined-elf-function-symbols>\n";
++    }
++
++
++  // Write the undefined variable symbols database.
++    if (ctxt.get_write_undefined_symbols()
++	&& !corpus->get_sorted_undefined_var_symbols().empty())
++    {
++      do_indent_to_level(ctxt, indent, 1);
++      out << "<undefined-elf-variable-symbols>\n";
++
++      write_elf_symbols_table(corpus->get_sorted_undefined_var_symbols(), ctxt,
++			      get_indent_to_level(ctxt, indent, 2));
++
++      do_indent_to_level(ctxt, indent, 1);
++      out << "</undefined-elf-variable-symbols>\n";
++    }
++
+   // Now write the translation units.
+   unsigned nb_tus = corpus->get_translation_units().size(), n = 0;
+   for (translation_units::const_iterator i =
+diff --git a/tests/test-annotate.cc b/tests/test-annotate.cc
+index cb9c8af6..8ddd93fb 100644
+--- a/tests/test-annotate.cc
++++ b/tests/test-annotate.cc
+@@ -158,7 +158,7 @@ main()
+   string abidw;
+ 
+   abidw = string(get_build_dir()) + "/tools/abidw "
+-    "--annotate --no-corpus-path --no-architecture";
++    "--annotate --no-corpus-path --no-architecture --no-load-undefined-interfaces";
+   for (InOutSpec* s = in_out_specs; s->in_elf_path; ++s)
+     {
+       bool is_ok = true;
+diff --git a/tests/test-read-dwarf.cc b/tests/test-read-dwarf.cc
+index 8570d774..a391c0f1 100644
+--- a/tests/test-read-dwarf.cc
++++ b/tests/test-read-dwarf.cc
+@@ -651,7 +651,7 @@ test_task_dwarf::perform()
+   if (spec.type_id_style == HASH_TYPE_ID_STYLE)
+     type_id_style = "hash";
+ 
+-  string cmd = abidw + " --no-architecture "
++  string cmd = abidw + " --no-architecture --no-load-undefined-interfaces"
+     + " --type-id-style " + type_id_style
+     + " --no-corpus-path "
+     + drop_private_types + " " + in_elf_path
+diff --git a/tools/abidw.cc b/tools/abidw.cc
+index c8a44dda..7ea12908 100644
+--- a/tools/abidw.cc
++++ b/tools/abidw.cc
+@@ -109,6 +109,7 @@ struct options
+   bool			short_locs;
+   bool			default_sizes;
+   bool			load_all_types;
++  bool			load_undefined_interfaces;
+   bool			linux_kernel_mode;
+   bool			corpus_group_for_linux;
+   bool			show_stats;
+@@ -155,6 +156,7 @@ struct options
+       short_locs(false),
+       default_sizes(true),
+       load_all_types(),
++      load_undefined_interfaces(true),
+       linux_kernel_mode(true),
+       corpus_group_for_linux(false),
+       show_stats(),
+@@ -233,6 +235,8 @@ display_usage(const string& prog_name, ostream& out)
+     "debug info of <elf-path>, and show its base name\n"
+     << "  --load-all-types  read all types including those not reachable from "
+     "exported declarations\n"
++    << "  --no-load-undefined-interfaces  do not consider undefined "
++    "interfaces from the binary"
+     << "  --no-linux-kernel-mode  don't consider the input binary as "
+        "a Linux Kernel binary\n"
+     << "  --kmi-whitelist|-w  path to a linux kernel "
+@@ -445,6 +449,8 @@ parse_command_line(int argc, char* argv[], options& opts)
+ 	}
+       else if (!strcmp(argv[i], "--load-all-types"))
+ 	opts.load_all_types = true;
++      else if (!strcmp(argv[i], "--no-load-undefined-interfaces"))
++	opts.load_undefined_interfaces = false;
+       else if (!strcmp(argv[i], "--drop-private-types"))
+ 	opts.drop_private_types = true;
+       else if (!strcmp(argv[i], "--drop-undefined-syms"))
+@@ -614,6 +620,7 @@ set_generic_options(abigail::elf_based_reader& rdr, options& opts)
+     opts.leverage_dwarf_factorization;
+   rdr.options().assume_odr_for_cplusplus =
+     opts.assume_odr_for_cplusplus;
++  rdr.options().load_undefined_interfaces = opts.load_undefined_interfaces;
+ }
+ 
+ /// Load an ABI @ref corpus (the internal representation of the ABI of
+-- 
+2.39.3
+
diff --git a/src/abg-dwarf-reader.cc b/src/abg-dwarf-reader.cc
index c1ae31ba..5d5353ea 100644
--- a/src/abg-dwarf-reader.cc
+++ b/src/abg-dwarf-reader.cc
@@ -379,6 +379,18 @@  get_scope_die(const reader&	rdr,
 	      size_t			where_offset,
 	      Dwarf_Die&		scope_die);
 
+static bool
+get_die_language(const Dwarf_Die *die, translation_unit::language &lang) ;
+
+static bool
+die_is_in_c(const Dwarf_Die *die);
+
+static bool
+die_is_in_cplus_plus(const Dwarf_Die *die);
+
+static bool
+die_is_in_c_or_cplusplus(const Dwarf_Die *die);
+
 static bool
 die_is_anonymous(const Dwarf_Die* die);
 
@@ -654,6 +666,75 @@  compare_dies_during_canonicalization(reader& rdr,
 static bool
 get_member_child_die(const Dwarf_Die *die, Dwarf_Die *child);
 
+/// Get the language used to generate a given DIE.
+///
+/// @param die the DIE to consider.
+///
+/// @param lang the resulting language.
+///
+/// @return true iff the language of the DIE was found.
+static bool
+get_die_language(const Dwarf_Die *die, translation_unit::language &lang) 
+{
+  Dwarf_Die cu_die;
+  ABG_ASSERT(dwarf_diecu(const_cast<Dwarf_Die*>(die), &cu_die, 0, 0));
+
+  uint64_t l = 0;
+  if (!die_unsigned_constant_attribute(&cu_die, DW_AT_language, l))
+    return false;
+
+  lang = dwarf_language_to_tu_language(l);
+  return true;
+}
+
+/// Test if a given DIE originates from a program written in the C
+/// language.
+///
+/// @param die the DIE to consider.
+///
+/// @return true iff @p die originates from a program in the C
+/// language.
+static bool
+die_is_in_c(const Dwarf_Die *die)
+{
+  translation_unit::language l = translation_unit::LANG_UNKNOWN;
+  if (!get_die_language(die, l))
+    return false;
+  return is_c_language(l);
+}
+
+/// Test if a given DIE originates from a program written in the C++
+/// language.
+///
+/// @param die the DIE to consider.
+///
+/// @return true iff @p die originates from a program in the C++
+/// language.
+static bool
+die_is_in_cplus_plus(const Dwarf_Die *die)
+{
+  translation_unit::language l = translation_unit::LANG_UNKNOWN;
+  if (!get_die_language(die, l))
+    return false;
+  return is_cplus_plus_language(l);
+}
+
+/// Test if a given DIE originates from a program written either in
+/// C or C++.
+///
+/// @param die the DIE to consider.
+///
+/// @return true iff @p die originates from a program written either in
+/// C or C++.
+static bool
+die_is_in_c_or_cplusplus(const Dwarf_Die *die)
+{
+  translation_unit::language l = translation_unit::LANG_UNKNOWN;
+  if (!get_die_language(die, l))
+    return false;
+  return (is_cplus_plus_language(l) || is_c_language(l));
+}
+
 /// Compare a symbol name against another name, possibly demangling
 /// the symbol_name before performing the comparison.
 ///
@@ -3320,75 +3401,6 @@  public:
     return i->second;
   }
 
-  /// Get the language used to generate a given DIE.
-  ///
-  /// @param die the DIE to consider.
-  ///
-  /// @param lang the resulting language.
-  ///
-  /// @return true iff the language of the DIE was found.
-  bool
-  get_die_language(const Dwarf_Die *die, translation_unit::language &lang) const
-  {
-    Dwarf_Die cu_die;
-    ABG_ASSERT(dwarf_diecu(const_cast<Dwarf_Die*>(die), &cu_die, 0, 0));
-
-    uint64_t l = 0;
-    if (!die_unsigned_constant_attribute(&cu_die, DW_AT_language, l))
-      return false;
-
-    lang = dwarf_language_to_tu_language(l);
-    return true;
-  }
-
-  /// Test if a given DIE originates from a program written in the C
-  /// language.
-  ///
-  /// @param die the DIE to consider.
-  ///
-  /// @return true iff @p die originates from a program in the C
-  /// language.
-  bool
-  die_is_in_c(const Dwarf_Die *die) const
-  {
-    translation_unit::language l = translation_unit::LANG_UNKNOWN;
-    if (!get_die_language(die, l))
-      return false;
-    return is_c_language(l);
-  }
-
-  /// Test if a given DIE originates from a program written in the C++
-  /// language.
-  ///
-  /// @param die the DIE to consider.
-  ///
-  /// @return true iff @p die originates from a program in the C++
-  /// language.
-  bool
-  die_is_in_cplus_plus(const Dwarf_Die *die) const
-  {
-    translation_unit::language l = translation_unit::LANG_UNKNOWN;
-    if (!get_die_language(die, l))
-      return false;
-    return is_cplus_plus_language(l);
-  }
-
-  /// Test if a given DIE originates from a program written either in
-  /// C or C++.
-  ///
-  /// @param die the DIE to consider.
-  ///
-  /// @return true iff @p die originates from a program written either in
-  /// C or C++.
-  bool
-  die_is_in_c_or_cplusplus(const Dwarf_Die *die) const
-  {
-    translation_unit::language l = translation_unit::LANG_UNKNOWN;
-    if (!get_die_language(die, l))
-      return false;
-    return (is_cplus_plus_language(l) || is_c_language(l));
-  }
-
   /// Check if we can assume the One Definition Rule[1] to be relevant
   /// for the current translation unit.
   ///
@@ -9818,7 +9830,7 @@  die_function_signature(const reader& rdr,
 
   translation_unit::language lang;
   bool has_lang = false;
-  if ((has_lang = rdr.get_die_language(fn_die, lang)))
+  if ((has_lang = get_die_language(fn_die, lang)))
     {
       // In a binary originating from the C language, it's OK to use
       // the linkage name of the function as a key for the map which
@@ -10324,8 +10336,6 @@  compare_as_decl_and_type_dies(const reader &rdr,
 /// in C++ for instance, that doesn't imply that the two functions are
 /// equal.
 ///
-/// @param rdr the @ref reader to consider.
-///
 /// @param l the first function DIE to consider.
 ///
 /// @param r the second function DIE to consider.
@@ -10333,8 +10343,7 @@  compare_as_decl_and_type_dies(const reader &rdr,
 /// @return true iff the function represented by @p l have the same
 /// linkage name as the function represented by @p r.
 static bool
-fn_die_equal_by_linkage_name(const reader &rdr,
-			     const Dwarf_Die *l,
+fn_die_equal_by_linkage_name(const Dwarf_Die *l,
 			     const Dwarf_Die *r)
 {
   if (!!l != !!r)
@@ -10352,8 +10361,8 @@  fn_die_equal_by_linkage_name(const reader &rdr,
   string llinkage_name = die_linkage_name(l),
     rlinkage_name = die_linkage_name(r);
 
-  if (rdr.die_is_in_c_or_cplusplus(l)
-      && rdr.die_is_in_c_or_cplusplus(r))
+  if (die_is_in_c_or_cplusplus(l)
+      && die_is_in_c_or_cplusplus(r))
     {
       if (!llinkage_name.empty() && !rlinkage_name.empty())
 	return llinkage_name == rlinkage_name;
@@ -11277,19 +11286,18 @@  compare_dies(const reader& rdr,
 	rdr.compare_count_++;
 
 	if (l_tag == DW_TAG_subprogram
-	    && !fn_die_equal_by_linkage_name(rdr, l, r))
+	    && !fn_die_equal_by_linkage_name(l, r))
 	  {
 	    SET_RESULT_TO_FALSE(result, l, r);
 	    break;
 	  }
 	else if (l_tag == DW_TAG_subprogram
-		 && rdr.die_is_in_c(l) && rdr.die_is_in_c(r)
-		 /*&& fn_die_equal_by_linkage_name(rdr, l, r)*/)
+		 && die_is_in_c(l) && die_is_in_c(r))
 	  {
 	    result = COMPARISON_RESULT_EQUAL;
 	    break;
 	  }
-	else if (!rdr.die_is_in_c(l) && !rdr.die_is_in_c(r))
+	else if (!die_is_in_c(l) && !die_is_in_c(r))
 	  {
 	    // In C, we cannot have two different functions with the
 	    // same linkage name in a given binary.  But here we are
@@ -11914,7 +11922,7 @@  get_scope_die(const reader&	rdr,
     memcpy(&origin_die_mem, dye, sizeof(origin_die_mem));
 
   translation_unit::language die_lang = translation_unit::LANG_UNKNOWN;
-  rdr.get_die_language(die, die_lang);
+  get_die_language(die, die_lang);
   if (is_c_language(die_lang)
       || rdr.die_parent_map(rdr.get_die_source(die)).empty())
     {
@@ -11974,7 +11982,7 @@  get_scope_for_die(reader&	rdr,
   const die_source source_of_die = rdr.get_die_source(die);
 
   translation_unit::language die_lang = translation_unit::LANG_UNKNOWN;
-  rdr.get_die_language(die, die_lang);
+  get_die_language(die, die_lang);
   if (is_c_language(die_lang)
       || rdr.die_parent_map(source_of_die).empty())
     {
@@ -12342,10 +12350,17 @@  build_translation_unit_and_add_to_ir(reader&	rdr,
     return result;
 
   result->set_is_constructed(false);
-
+  int tag = dwarf_tag(&child);
   do
     if (rdr.load_undefined_interfaces()
-	&& rdr.is_decl_die_with_undefined_symbol(&child))
+	&& (rdr.is_decl_die_with_undefined_symbol(&child)
+	    || tag == DW_TAG_class_type // Top-level classes might
+					// have undefined interfaces
+					// that need to be
+					// represented, so let's
+					// analyze them as well.
+	    || ((tag == DW_TAG_union_type || tag == DW_TAG_structure_type)
+		&& die_is_in_cplus_plus(&child))))
       {
 	// Analyze undefined functions & variables for the purpose of
 	// analyzing compatibility matters.
@@ -15013,7 +15028,7 @@  function_is_suppressed(const reader& rdr,
 
   string fname = die_string_attribute(function_die, DW_AT_name);
   string flinkage_name = die_linkage_name(function_die);
-  if (flinkage_name.empty() && rdr.die_is_in_c(function_die))
+  if (flinkage_name.empty() && die_is_in_c(function_die))
     flinkage_name = fname;
   string qualified_name = build_qualified_name(scope, fname);
 
@@ -15190,7 +15205,7 @@  variable_is_suppressed(const reader&		rdr,
 
   string name = die_string_attribute(variable_die, DW_AT_name);
   string linkage_name = die_linkage_name(variable_die);
-  if (linkage_name.empty() && rdr.die_is_in_c(variable_die))
+  if (linkage_name.empty() && die_is_in_c(variable_die))
     linkage_name = name;
   string qualified_name = build_qualified_name(scope, name);
 
@@ -16050,7 +16065,7 @@  build_ir_node_from_die(reader&	rdr,
 	bool var_is_cloned = false;
 
 	if (tag == DW_TAG_member)
-	  ABG_ASSERT(!rdr.die_is_in_c(die));
+	  ABG_ASSERT(!die_is_in_c(die));
 
 	if (die_die_attribute(die, DW_AT_specification, spec_die, false)
 	    || (var_is_cloned = die_die_attribute(die, DW_AT_abstract_origin,
diff --git a/src/abg-reader.cc b/src/abg-reader.cc
index b525a137..a91cfaf4 100644
--- a/src/abg-reader.cc
+++ b/src/abg-reader.cc
@@ -1270,7 +1270,8 @@  public:
     if (!needed.empty())
       corp.set_needed(needed);
 
-    string_elf_symbols_map_sptr fn_sym_db, var_sym_db;
+    string_elf_symbols_map_sptr fn_sym_db(new string_elf_symbols_map_type),
+      var_sym_db(new string_elf_symbols_map_type);
 
     // Read the symbol databases.
     read_symbol_db_from_input(*this, fn_sym_db, var_sym_db);
@@ -1338,6 +1339,9 @@  public:
 	set_corpus_node(node);
       }
 
+    corpus()->sort_functions();
+    corpus()->sort_variables();
+
     status = STATUS_OK;
     return corpus();
   }
@@ -1389,8 +1393,9 @@  build_elf_symbol(reader&, const xmlNodePtr, bool);
 static elf_symbol_sptr
 build_elf_symbol_from_reference(reader&, const xmlNodePtr);
 
-static string_elf_symbols_map_sptr
-build_elf_symbol_db(reader&, const xmlNodePtr, bool);
+static bool
+build_elf_symbol_db(reader&, const xmlNodePtr, bool,
+		    string_elf_symbols_map_sptr&);
 
 static function_decl::parameter_sptr
 build_function_parameter (reader&, const xmlNodePtr);
@@ -1898,9 +1903,9 @@  read_translation_unit_from_input(fe_iface& iface)
 ///
 /// @return true upon successful parsing, false otherwise.
 static bool
-read_symbol_db_from_input(reader&		 rdr,
-			  string_elf_symbols_map_sptr& fn_symdb,
-			  string_elf_symbols_map_sptr& var_symdb)
+read_symbol_db_from_input(reader&			rdr,
+			  string_elf_symbols_map_sptr&	fn_symdb,
+			  string_elf_symbols_map_sptr&	var_symdb)
 {
   xml::reader_sptr reader = rdr.get_libxml_reader();
   if (!reader)
@@ -1917,13 +1922,20 @@  read_symbol_db_from_input(reader&		 rdr,
 	if (status != 1)
 	  return false;
 
-	bool has_fn_syms = false, has_var_syms = false;
+	bool has_fn_syms = false, has_undefined_fn_syms = false,
+	  has_var_syms = false, has_undefined_var_syms = false;
 	if (xmlStrEqual (XML_READER_GET_NODE_NAME(reader).get(),
 			 BAD_CAST("elf-function-symbols")))
 	  has_fn_syms = true;
 	else if (xmlStrEqual (XML_READER_GET_NODE_NAME(reader).get(),
 			      BAD_CAST("elf-variable-symbols")))
 	  has_var_syms = true;
+	else if (xmlStrEqual (XML_READER_GET_NODE_NAME(reader).get(),
+			      BAD_CAST("undefined-elf-function-symbols")))
+	  has_undefined_fn_syms = true;
+	else if (xmlStrEqual (XML_READER_GET_NODE_NAME(reader).get(),
+			      BAD_CAST("undefined-elf-variable-symbols")))
+	  has_undefined_var_syms = true;
 	else
 	  break;
 
@@ -1932,20 +1944,30 @@  read_symbol_db_from_input(reader&		 rdr,
 	  return false;
 
 	if (has_fn_syms)
-	  fn_symdb = build_elf_symbol_db(rdr, node, true);
+	  build_elf_symbol_db(rdr, node, /*function_sym=*/true, fn_symdb);
+	else if (has_undefined_fn_syms)
+	  build_elf_symbol_db(rdr, node, /*function_sym=*/true, fn_symdb);
 	else if (has_var_syms)
-	  var_symdb = build_elf_symbol_db(rdr, node, false);
+	  build_elf_symbol_db(rdr, node, /*function_sym=*/false, var_symdb);
+	else if (has_undefined_var_syms)
+	  build_elf_symbol_db(rdr, node, /*function_sym=*/false, var_symdb);
 
 	xmlTextReaderNext(reader.get());
       }
   else
     for (xmlNodePtr n = rdr.get_corpus_node(); n; n = xmlNextElementSibling(n))
       {
-	bool has_fn_syms = false, has_var_syms = false;
+	bool has_fn_syms = false, has_undefined_fn_syms = false,
+	  has_var_syms = false, has_undefined_var_syms = false;
 	if (xmlStrEqual(n->name, BAD_CAST("elf-function-symbols")))
 	  has_fn_syms = true;
+	else if (xmlStrEqual(n->name, BAD_CAST("undefined-elf-function-symbols")))
+	  has_undefined_fn_syms = true;
 	else if (xmlStrEqual(n->name, BAD_CAST("elf-variable-symbols")))
 	  has_var_syms = true;
+	else if (xmlStrEqual(n->name,
+			     BAD_CAST("undefined-elf-variable-symbols")))
+	  has_undefined_var_syms = true;
 	else
 	  {
 	    rdr.set_corpus_node(n);
@@ -1953,9 +1975,13 @@  read_symbol_db_from_input(reader&		 rdr,
 	  }
 
 	if (has_fn_syms)
-	  fn_symdb = build_elf_symbol_db(rdr, n, true);
+	  build_elf_symbol_db(rdr, n, /*function_sym=*/true, fn_symdb);
+	else if (has_undefined_fn_syms)
+	  build_elf_symbol_db(rdr, n, /*function_sym=*/true, fn_symdb);
 	else if (has_var_syms)
-	  var_symdb = build_elf_symbol_db(rdr, n, false);
+	  build_elf_symbol_db(rdr, n, /*function_sym=*/false, var_symdb);
+	else if (has_undefined_var_syms)
+	  build_elf_symbol_db(rdr, n, /*function_sym=*/false, var_symdb);
 	else
 	  break;
       }
@@ -3300,24 +3326,30 @@  build_elf_symbol_from_reference(reader& rdr, const xmlNodePtr node)
 /// @param function_syms true if we should look for a function symbols
 /// data base, false if we should look for a variable symbols data
 /// base.
-static string_elf_symbols_map_sptr
-build_elf_symbol_db(reader& rdr,
-		    const xmlNodePtr node,
-		    bool function_syms)
+///
+/// @param map a pointer to the map to fill with the symbol database.
+///
+/// @return true if some elf symbols were found.
+static bool
+build_elf_symbol_db(reader&				rdr,
+		    const xmlNodePtr			node,
+		    bool				function_syms,
+		    string_elf_symbols_map_sptr&	map)
 {
-  string_elf_symbols_map_sptr map, nil;
   string_elf_symbol_sptr_map_type id_sym_map;
 
   if (!node)
-    return nil;
+    return false;
 
   if (function_syms
-      && !xmlStrEqual(node->name, BAD_CAST("elf-function-symbols")))
-    return nil;
+      && !xmlStrEqual(node->name, BAD_CAST("elf-function-symbols"))
+      && !xmlStrEqual(node->name, BAD_CAST("undefined-elf-function-symbols")))
+    return false;
 
   if (!function_syms
-      && !xmlStrEqual(node->name, BAD_CAST("elf-variable-symbols")))
-    return nil;
+      && !xmlStrEqual(node->name, BAD_CAST("elf-variable-symbols"))
+      && !xmlStrEqual(node->name, BAD_CAST("undefined-elf-variable-symbols")))
+    return false;
 
   rdr.set_corpus_node(node);
 
@@ -3336,9 +3368,8 @@  build_elf_symbol_db(reader& rdr,
       }
 
   if (id_sym_map.empty())
-    return nil;
+    return false;
 
-  map.reset(new string_elf_symbols_map_type);
   string_elf_symbols_map_type::iterator it;
   for (string_elf_symbol_sptr_map_type::const_iterator i = id_sym_map.begin();
        i != id_sym_map.end();
@@ -3374,7 +3405,7 @@  build_elf_symbol_db(reader& rdr,
 	}
     }
 
-  return map;
+  return true;
 }
 
 /// Build a function parameter from a 'parameter' xml element node.
diff --git a/src/abg-writer.cc b/src/abg-writer.cc
index 408623ab..dafe706c 100644
--- a/src/abg-writer.cc
+++ b/src/abg-writer.cc
@@ -214,6 +214,7 @@  class write_context
   bool					m_write_corpus_path;
   bool					m_write_comp_dir;
   bool					m_write_elf_needed;
+  bool					m_write_undefined_symbols;
   bool					m_write_parameter_names;
   bool					m_short_locs;
   bool					m_write_default_sizes;
@@ -252,6 +253,7 @@  public:
       m_write_corpus_path(true),
       m_write_comp_dir(true),
       m_write_elf_needed(true),
+      m_write_undefined_symbols(true),
       m_write_parameter_names(true),
       m_short_locs(false),
       m_write_default_sizes(true),
@@ -325,6 +327,20 @@  public:
   set_write_elf_needed(bool f)
   {m_write_elf_needed = f;}
 
+  /// Getter of the "undefined-symbols" option.
+  ///
+  /// @return true iff undefined symbols shall be emitted.
+  bool
+  get_write_undefined_symbols() const
+  {return m_write_undefined_symbols;}
+
+  /// Setter of the "undefined-symbols" option.
+  ///
+  /// @param f true iff undefined symbols shall be emitted.
+  void
+  set_write_undefined_symbols(bool f)
+  {m_write_undefined_symbols = f;}
+
   /// Getter of the default-sizes option.
   ///
   /// @return true iff default size-in-bits needs to be emitted
@@ -793,6 +809,20 @@  public:
   type_is_emitted(const type_base_sptr& t) const
   {return type_is_emitted(t.get());}
 
+  /// Test if a given decl has been written out to the XML output.
+  ///
+  /// @param the decl to consider.
+  ///
+  /// @return true if the decl has already been emitted, false
+  /// otherwise.
+  bool
+  decl_is_emitted(const decl_base& decl) const
+  {
+    string repr = decl.get_pretty_representation(true);
+    interned_string irepr = decl.get_environment().intern(repr);
+    return m_emitted_decls_set.find(irepr) != m_emitted_decls_set.end();
+  }
+
   /// Test if a given decl has been written out to the XML output.
   ///
   /// @param the decl to consider.
@@ -901,8 +931,14 @@  static void write_voffset(function_decl_sptr, ostream&);
 static void write_elf_symbol_type(elf_symbol::type, ostream&);
 static void write_elf_symbol_binding(elf_symbol::binding, ostream&);
 static bool write_elf_symbol_aliases(const elf_symbol&, ostream&);
-static bool write_elf_symbol_reference(const elf_symbol&, ostream&);
-static bool write_elf_symbol_reference(const elf_symbol_sptr, ostream&);
+static bool write_elf_symbol_reference(write_context&,
+				       const elf_symbol&,
+				       const corpus& abi,
+				       ostream&);
+static bool write_elf_symbol_reference(write_context&,
+				       const elf_symbol_sptr,
+				       const corpus& abi,
+				       ostream&);
 static void write_is_declaration_only(const decl_base_sptr&, ostream&);
 static void write_is_struct(const class_decl_sptr&, ostream&);
 static void write_is_anonymous(const decl_base_sptr&, ostream&);
@@ -1766,14 +1802,36 @@  write_elf_symbol_aliases(const elf_symbol& sym, ostream& out)
 /// Write an XML attribute for the reference to a symbol for the
 /// current decl.
 ///
+///
+/// @param ctxt the current write context to consider.
+///
 /// @param sym the symbol to consider.
 ///
+/// @param abi the ABI corpus the symbol @p sym is supposed to belong
+/// to.  If the symbol doesn't belong to that corpus, then the
+/// reference is not be emitted.
+///
 /// @param o the output stream to write the attribute to.
 ///
 /// @return true upon successful completion.
 static bool
-write_elf_symbol_reference(const elf_symbol& sym, ostream& o)
+write_elf_symbol_reference(write_context& ctxt,
+			   const elf_symbol& sym,
+			   const corpus& abi,
+			   ostream& o)
 {
+  elf_symbol_sptr s = abi.lookup_function_symbol(sym);
+  if (!s)
+    s = abi.lookup_variable_symbol(sym);
+
+  if (// If that symbol wasn't found in the current corpus ...
+      !s
+      // ... or we were NOT asked to represent undefined symbols and
+      // yet that symbol is undefined ...
+      || (!ctxt.get_write_undefined_symbols() && !s->is_defined()))
+    // Then do not emit this symbol reference.
+    return false;
+
   const elf_symbol* main = sym.get_main_symbol().get();
   const elf_symbol* alias = &sym;
   bool found = !alias->is_suppressed();
@@ -1804,18 +1862,27 @@  write_elf_symbol_reference(const elf_symbol& sym, ostream& o)
 /// Write an XML attribute for the reference to a symbol for the
 /// current decl.
 ///
+/// @param ctxt the write context to consider.
+/// 
 /// @param sym the symbol to consider.
 ///
+/// @param abi the ABI corpus the symbol @p sym is supposed to belong
+/// to.  If the symbol doesn't belong to that corpus, then the
+/// reference is not be emitted.
+///
 /// @param o the output stream to write the attribute to.
 ///
 /// @return true upon successful completion.
 static bool
-write_elf_symbol_reference(const elf_symbol_sptr sym, ostream& o)
+write_elf_symbol_reference(write_context& ctxt,
+			   const elf_symbol_sptr sym,
+			   const corpus& abi,
+			   ostream& o)
 {
   if (!sym)
     return false;
 
-  return write_elf_symbol_reference(*sym, o);
+  return write_elf_symbol_reference(ctxt, *sym, abi, o);
 }
 
 /// Serialize the attributes "constructor", "destructor" or "static"
@@ -2255,6 +2322,18 @@  void
 set_write_elf_needed(write_context& ctxt, bool flag)
 {ctxt.set_write_elf_needed(flag);}
 
+/// Set the 'undefined-symbols' flag.
+///
+/// When this flag is set then the XML writer will emit corpus
+/// information about the undefined function and variable symbols.
+///
+/// @param ctxt the context to set this flag on to.
+///
+/// @param flag the new value of the 'undefined-symbols' flag.
+void
+set_write_undefined_symbols(write_context& ctxt, bool flag)
+{ctxt.set_write_undefined_symbols(flag);}
+
 /// Set the 'default-sizes' flag.
 ///
 /// When this flag is set then the XML writer will emit default
@@ -2549,6 +2628,32 @@  write_translation_unit(write_context&		ctxt,
 	}
     }
 
+  // Write the undefined functions that belong to this translation
+  // unit
+  if (const abigail::ir::corpus* abi = tu.get_corpus())
+    for (auto undefined_function : abi->get_sorted_undefined_functions())
+      {
+	function_decl_sptr f(const_cast<function_decl*>(undefined_function),
+			     noop_deleter());
+	if (f->get_translation_unit() != &tu || ctxt.decl_is_emitted(f))
+	  continue;
+
+	write_decl(f, ctxt, indent + c.get_xml_element_indent());
+      }
+
+  // Write the undefined variables that belong to this translation
+  // unit
+  if (const abigail::ir::corpus* abi = tu.get_corpus())
+    for (auto undefined_var : abi->get_sorted_undefined_variables())
+      {
+	var_decl_sptr v(const_cast<var_decl*>(undefined_var),
+			noop_deleter());
+	if (v->get_translation_unit() != &tu || ctxt.decl_is_emitted(v))
+	  continue;
+
+	write_decl(v, ctxt, indent + c.get_xml_element_indent());
+      }
+
   write_referenced_types(ctxt, tu, indent, is_last);
 
   // Now handle all function types that were not only referenced by
@@ -3468,8 +3573,8 @@  write_var_decl(const var_decl_sptr& decl, write_context& ctxt,
   write_location(decl, ctxt);
 
   if (elf_symbol_sptr sym = decl->get_symbol())
-    if (sym->is_defined())
-      write_elf_symbol_reference(decl->get_symbol(), o);
+    if (corpus* abi = decl->get_corpus())
+      write_elf_symbol_reference(ctxt, decl->get_symbol(), *abi, o);
 
   o << "/>\n";
 
@@ -3526,8 +3631,8 @@  write_function_decl(const function_decl_sptr& decl, write_context& ctxt,
 			    : decl->get_translation_unit()->get_address_size()),
 			   0);
   if (elf_symbol_sptr sym = decl->get_symbol())
-    if (sym->is_defined())
-      write_elf_symbol_reference(decl->get_symbol(), o);
+    if (corpus* abi = decl->get_corpus())
+      write_elf_symbol_reference(ctxt, decl->get_symbol(), *abi, o);
 
   o << ">\n";
 
@@ -4734,6 +4839,35 @@  write_corpus(write_context&	ctxt,
       out << "</elf-variable-symbols>\n";
     }
 
+  // Write the undefined function symbols database.
+  if (ctxt.get_write_undefined_symbols()
+      && !corpus->get_sorted_undefined_fun_symbols().empty())
+    {
+      do_indent_to_level(ctxt, indent, 1);
+      out << "<undefined-elf-function-symbols>\n";
+
+      write_elf_symbols_table(corpus->get_sorted_undefined_fun_symbols(), ctxt,
+			      get_indent_to_level(ctxt, indent, 2));
+
+      do_indent_to_level(ctxt, indent, 1);
+      out << "</undefined-elf-function-symbols>\n";
+    }
+
+
+  // Write the undefined variable symbols database.
+    if (ctxt.get_write_undefined_symbols()
+	&& !corpus->get_sorted_undefined_var_symbols().empty())
+    {
+      do_indent_to_level(ctxt, indent, 1);
+      out << "<undefined-elf-variable-symbols>\n";
+
+      write_elf_symbols_table(corpus->get_sorted_undefined_var_symbols(), ctxt,
+			      get_indent_to_level(ctxt, indent, 2));
+
+      do_indent_to_level(ctxt, indent, 1);
+      out << "</undefined-elf-variable-symbols>\n";
+    }
+
   // Now write the translation units.
   unsigned nb_tus = corpus->get_translation_units().size(), n = 0;
   for (translation_units::const_iterator i =
diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am
index 5bf01528..da37f56c 100644
--- a/tests/data/Makefile.am
+++ b/tests/data/Makefile.am
@@ -2130,6 +2130,21 @@  test-abicompat/test9-fn-changed-v1.cc \
 test-abicompat/test9-fn-changed-v1.h \
 test-abicompat/test9-fn-changed-app \
 test-abicompat/test9-fn-changed-report-0.txt \
+test-abicompat/test10/libtest10-with-exported-symbols.so \
+test-abicompat/test10/libtest10-with-incompatible-exported-symbols.so \
+test-abicompat/test10/libtest10-with-incompatible-exported-symbols.so.abi \
+test-abicompat/test10/test10-app-with-undefined-symbols \
+test-abicompat/test10/test10-app-with-undefined-symbols.abi \
+test-abicompat/test10/test10-app-with-undefined-symbols.cc \
+test-abicompat/test10/test10-with-exported-symbols.cc \
+test-abicompat/test10/test10-with-exported-symbols.h \
+test-abicompat/test10/test10-with-incompatible-exported-symbols.cc \
+test-abicompat/test10/test10-with-incompatible-exported-symbols.h \
+test-abicompat/test10/test10-fn-changed-report-0.txt \
+test-abicompat/test10/test10-fn-changed-report-1.txt \
+test-abicompat/test10/test10-fn-changed-report-2.txt \
+test-abicompat/test10/test10-fn-changed-report-3.txt \
+test-abicompat/test10/test10-fn-changed-report-4.txt \
 \
 test-diff-pkg/dbus-glib-0.104-3.fc23.x86_64.rpm \
 test-diff-pkg/dbus-glib-0.80-3.fc12.x86_64.rpm \
diff --git a/tests/data/test-abicompat/test10/libtest10-with-exported-symbols.so b/tests/data/test-abicompat/test10/libtest10-with-exported-symbols.so
new file mode 100755
index 0000000000000000000000000000000000000000..8eb1fa58d0c85b1813eadaa56367fdc9d7762ec3
GIT binary patch
literal 18288
zcmeHPYit}>6~43Mwb$Nw*NM|Kj~1_ki&BEuk2p@6;ARuY_6CyF7#pQX(R3eU?-uW_
zwL4CnNR-eP)bxQ;Bt$EvC@TC2q5_0Og%F5DsY)dzD37A>5Ex3O6iVx=1>90)`Ockt
z*0bYvQt_imXs$JL?)lE+-Z^u3=FZGL{>;$9VV|Z6F1ol~5EpB)k(d&6?@<+ynAj;A
zv2Pa<sav-sT2?#dw;`rN`K30bNb!s_J*v9IlyQP(dlB0&x?mSY-%%`6r(Hc{mpg5R
z%LCOk?z8z(McIy6vRhDg3(BrSRX-OL4cKmEAE}=^l|MJ_P<l+|F##KW$x6$5L@Bsv
zpMsgP-N#@@{d~8D!#<|kTPZGERR5W(!<5Ij7LDoHMh;Jicg`85U%t^%|HQ@qXa9cY
zt9N$)Wc)Si3(4s`lb!6}XT>m>)r1jG@oH@PRdZ<1moGiu@W9$l`Fs8bXvH!N$>thT
zI-W=koZ790e`p2#2f){epy*qT3d$y%h{r(*->mR83O}y!CWYUq@Ck*#tpd&Q7KSmE
zE98xml_*+<A&dj#hm5pYG^es9%Pfu`8k{cV&GAHX+O%c0B}Qs4VN7Q8iRtV~D1P$r
zZKXoaG^`UdW>5c=X+c&jSw_yxCC%c{F}Fs^t&w$U>>wW{GgZi^S8}q0oK&l%#B?@U
zveK!oTcf2yw4c~qr8bpH871O+2v6Dg$iV~q294fmZ*)5%rUn}Zi2X<7Miccoi5$P#
zjoC)hYoQa1!oOqw-7P4^dG$MG{M!n@;=-AqSJyWhlghR9f5m}w??|xVz-f$gS#;q1
z+a%fgn61$O;`DE$-rkFV7XdE<UIe@dcoFa-;6=cTz<U#cH^T3GJAU6Qf%rrIKXnNa
z|J()3SAITz-!p+rvIFJqe+FE><uBNW+hV8@pCQ@H^W}0m{>Z>@$nFvUFNoZ>Xx#u;
zU!Xc%HJ0bYZKp{2lA_T6y42tPB#~RM#!oNAFTT1besNKcYtP1ie$`qF2S=5IKzV*L
z+_rzop89)gz=T-LZk>qVH?Wba@zXC^tKfPap3<+}iEdp<WB06o07C63+OD)u_AeiY
z@XQ2^;-^2o7=LJJ@gZ^a67#2L-;~hAo2OgO9KBSf7k)Zc=g?euU`XnQpPqR0fzdi>
zi%L8z5u~H{iWdPd0$v2X2zU|jBH%^9i+~paF9Kc!ya;#^_}@lA+obQNZ^`sMHnLDI
zkAl7pItSYHdb#`<=w{IGf%bu32BkvZeyM0D$AmW5qHS8$5IBPmTOGh|!uH&2<#Gg6
zgj$9}>pv0>9uJ%ndp7R=;I<C=`IdC>ST9bZE%|9~Z>Z(-zQN}DkHQgflBcoV4Y~ZL
z%JQ)#`TdY5A(!7`S$=3q{x!(QA(!89S^f!I4v|;92zU|jBH%^9i+~paF9Kc!ya;#^
z@FMVk5dmHY>0bB9YY+dZj*|ITUHqbpr!|;VR@tPoAhTKVqiX%@2F3HbOkO|B>o7Me
zIj_&8=R7Jb|HsvGf%v=BVoSM39Q=%0WGUAWgMUoPn@AbuCe^>7&5HGkR@P?5Y=GBr
zvYek|X<a52UcZ}B4KseB&X#jO&)Y24!P|xXT`k-FKd1EZrCA;i#~F{Kl7Cpq-%@;~
zpUU5RMR*;%yS;w3UUp<~a93mtR(RhU>FJ5~MSCK>-M!no`@6SCwv3tSNIYRFL1*u+
za6<8sra0h{y&c-NM9)$#f$~{PhZp#+yM_*ru_J1%)$$Wl_HSLo&mGF1X)Ql2V_Hjx
z`?~2GZE;_Zsy1oexjhESBi^+rTUsw(tDHw1{X&z<jX#A(`<POzW!XfFr~^K&;W^N{
zA7bZkNMcVc*&V^A{R}(d?||d_)2Q8zZ5Erv>q%2C?iYaoy;u1ywuiBO5u3972C9v;
z%$eS=e2ejV(i9CBv0EjZ7Y#JTn*)T`iC~>So43TOK$Jl0LP)FMjy~w*xs@_d+)M7Y
zvm1f>?uYyZY`#Y`fn%ce{p8R0Xj<sh>F_S7xYy)sXGdIyLZ>4SP62i=)Ys0Qaw(&g
zU^~TuS_$t)MQhN$0ws>PI%PsT>+BrV{Ng}sAFhBtztBTe_2IIhN41gGUG3WL;BD<%
zx^=a-rXjZe<OZ!3VTo#cHt3;U?LG*Dd6@LJYaiZ_ZY81LDQpkqLk?LzwS;Ilqa9^^
zpmis%eF3x_up0{=Xr<;GS8KO~T7!DqrZ#H21sAv`pQeX`!CgUWm6{56h+y#cpdP@v
z3!_TDn?E3%2q{qrv6crSjg}4r(fJfMKYsXK6e&&ncc_^N;zbMHQE8*9@M&$nuug+e
zjzm$UnqL!*J`U>!f8)LWb(4Or*B^e$-+IQc9VX<)dkJZI&cF64pALV^G85iS>Ov(u
z!mV5@iQ)R`6jml%a(U{nkyEkExp1duQ0`1*aO>8d9q;1u8tKegvw3r1%FLU^Yzl0m
zn92;y?d<RD@9R9)8|j>ibWV;Q7!>Zlh*Ty~6kQqQh;*e3>ASOCQ(3DESs00Ab}Esb
z?lN;(t8+G=Hjz_eraQBfiIiFDvdofI>Pjc9gsgT#J5|WdBrM7HbPH1G>Fzw9wKARN
z+)SZp!MJoHmn=+|qN&ui<rx|Q*D5&MmHa8Eo{lRh&kw3`y)rMT#_KBchicp}_&!{X
z*9*RXSK|%hg1T>4;{n0*g=!qL1HM=|1iEMvk(zk0a<6o#BZq30;Co*+j@ehliP&{r
zgv8F8c({@;<FwMzNuG~Y<EsVd$5i8MM6G#)E?NcW(NybWmKAd%c3l@Yh)7L*-Ew6h
zI%a2E?piL=E3>_7nJ#!X>c%%#1B%lt*<RIRU4D(I#)#9!&C5W#*42sE%WeV9n|;V1
z<M|28ab1=8jB7l!E1c)=tiJ;l%@H?wZ3Q+c@#W`{?(Wp?g3JFgslR+Yd<l51{;zag
z-<5tsVlje>tJLyyN$M;gx4#CCw7_NK?M2|e8sq<Ei7)?sy(axEAD@d7U%PC))#1Q2
z`kEDkf}_yXqgKA(0PxKYJ+2;6`g}9vaGzATdvf!H!rjx69|4cBQwROs3iw}Fz(uNP
zm8{vx$!JO}r574j&PY*$A?{vAx?oIA7m|r-BW)FmB_lCAC+PA%ZQ?2(?XImsNrzb@
zQ7k4-7-rroo)DA81TqiPv$@;}m^e5CnpTzS^==o2F+8^S(2y~7ct52^8n`(cLvaPh
z_m5GRN(Cd6$fqeilDZ;fE5g`+=i$AFaOX1&q)@Vhk%M>a+k4QsV|e)EL*vHy-hBs$
z;I=ARvUZ|lReEL2nWI>3ef?C+npu)AC+@rT5RU3vX`wYTNNZ+^iYRWyRw4;%6>XZ~
zTE1YJ(W(4wG&!4{#tlC$B$r8)G9sEjk%y&Ct)eYCW)@4?LcWSIAS;^F2{KT%nQ2Qz
z>BNhuWzJzQPg2y(sF9IL!-hFq(;gzWCf8}sL9V04g6u{jmrbGR0vuu&m8~R8B@xAd
z&0!qc-IB)=O;}bjo1Dd9gQ=OEor2Ruerk!$<|oOkElMVf<}v1`G5g^<BE?mLk+CbM
ztd8I0e^aIRA@okcr;Jz13uoW%=>|PM{S)sD*#0h6VcMq*`F#SW^i^diI6Nm*+UNHu
zOfM^KY8RK5%6l0+Nyrkzo_urr{9cABzlV{^*pUxsiG!hcB5cp|FQ#jhGWVbLm`*@W
z?^u}Uc^cD*vUm4i!E#Lj$=Lpa3NX|2N|D>=`0?@QAt#f4N{{D#Oj(cY>D`jsehwJL
zgx_OT=7;Kr!SBnUB8%!Cv!aBW)1K#%6<X1>D|{bfdXLMV=OawRt`o*~?Dl?_J<m&-
z^7}y6clZB6WzX>!5v8dpk6(q#SaY4|W7w#RY|rytrq7U)qj3BgdJ+|ybMg7*_muqq
z58V39GyNuP!w!3%H_vwhL}5LfTjCl2J_OW%ZlCAZSFU5v{(g)KjUBe<d3fdjQ>gRD
zap!jVJU$OwXZt)~pQopHl!)9f?bx2_3lKT&MNH{-+cctBCTz#_4-inD`yYwfnh|Bs
zdNyje#dV@DVx#tzIzh6qJzXE2g><PfeFb%Xr%O0LPmFT^S&@V&97mhOxP#oKB1JPj
zwuZZ3Zr#-@*q>AOPq_?Sthjjv`-9u;_W#8lRn%?2bp`uD^}{P%$9{*hcOU0>ls&zV
za~5Wq?pwkBmF>3GqfTSg+0IG%x9?Ov)o*i0)O-6juoO}rPkdd*6GLU}*%tQxGQH<R
asdG8!c5SJ^6H0CS->d&CG3GLGvEn~NT{;o~

literal 0
HcmV?d00001

diff --git a/tests/data/test-abicompat/test10/libtest10-with-incompatible-exported-symbols.so b/tests/data/test-abicompat/test10/libtest10-with-incompatible-exported-symbols.so
diff --git a/tests/data/test-abicompat/test10/libtest10-with-incompatible-exported-symbols.so.abi b/tests/data/test-abicompat/test10/libtest10-with-incompatible-exported-symbols.so.abi
new file mode 100644
index 00000000..82653e42
--- /dev/null
+++ b/tests/data/test-abicompat/test10/libtest10-with-incompatible-exported-symbols.so.abi
@@ -0,0 +1,65 @@ 
+<abi-corpus version='2.3'>
+  <elf-needed>
+    <dependency name='libstdc++.so.6'/>
+    <dependency name='libm.so.6'/>
+    <dependency name='libgcc_s.so.1'/>
+    <dependency name='libc.so.6'/>
+  </elf-needed>
+  <elf-function-symbols>
+    <elf-symbol name='_ZN9some_type16get_first_memberEv' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+    <elf-symbol name='_ZN9some_type16set_first_memberEi' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+    <elf-symbol name='_ZN9some_type17get_second_memberEv' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+    <elf-symbol name='_ZN9some_type17set_second_memberEc' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+  </elf-function-symbols>
+  <undefined-elf-function-symbols>
+    <elf-symbol name='__cxa_finalize' version='GLIBC_2.2.5' is-default-version='yes' type='func-type' binding='weak-binding' visibility='default-visibility' is-defined='no'/>
+  </undefined-elf-function-symbols>
+  <undefined-elf-variable-symbols>
+    <elf-symbol name='_ITM_deregisterTMCloneTable' type='no-type' binding='weak-binding' visibility='default-visibility' is-defined='no'/>
+    <elf-symbol name='_ITM_registerTMCloneTable' type='no-type' binding='weak-binding' visibility='default-visibility' is-defined='no'/>
+    <elf-symbol name='__gmon_start__' type='no-type' binding='weak-binding' visibility='default-visibility' is-defined='no'/>
+  </undefined-elf-variable-symbols>
+  <abi-instr address-size='64' path='test10-with-incompatible-exported-symbols.cc' comp-dir-path='/home/dodji/git/libabigail/emit-undefined-ifaces/tests/data/test-abicompat/test10' language='LANG_C_plus_plus_14'>
+    <type-decl name='char' size-in-bits='8' id='type-id-1'/>
+    <class-decl name='some_type' size-in-bits='64' visibility='default' filepath='/home/dodji/git/libabigail/emit-undefined-ifaces/tests/data/test-abicompat/test10/test10-with-incompatible-exported-symbols.h' line='1' column='1' id='type-id-2'>
+      <data-member access='private' layout-offset-in-bits='0'>
+        <var-decl name='first_member' type-id='type-id-3' visibility='default' filepath='/home/dodji/git/libabigail/emit-undefined-ifaces/tests/data/test-abicompat/test10/test10-with-incompatible-exported-symbols.h' line='12' column='1'/>
+      </data-member>
+      <data-member access='private' layout-offset-in-bits='32'>
+        <var-decl name='second_member' type-id='type-id-1' visibility='default' filepath='/home/dodji/git/libabigail/emit-undefined-ifaces/tests/data/test-abicompat/test10/test10-with-incompatible-exported-symbols.h' line='13' column='1'/>
+      </data-member>
+      <member-function access='public'>
+        <function-decl name='set_second_member' mangled-name='_ZN9some_type17set_second_memberEc' filepath='/home/dodji/git/libabigail/emit-undefined-ifaces/tests/data/test-abicompat/test10/test10-with-incompatible-exported-symbols.cc' line='19' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='_ZN9some_type17set_second_memberEc'>
+          <parameter type-id='type-id-4' name='this' is-artificial='yes'/>
+          <parameter type-id='type-id-1' name='v' filepath='/home/dodji/git/libabigail/emit-undefined-ifaces/tests/data/test-abicompat/test10/test10-with-incompatible-exported-symbols.cc' line='19' column='1'/>
+          <return type-id='type-id-5'/>
+        </function-decl>
+      </member-function>
+      <member-function access='public'>
+        <function-decl name='get_second_member' mangled-name='_ZN9some_type17get_second_memberEv' filepath='/home/dodji/git/libabigail/emit-undefined-ifaces/tests/data/test-abicompat/test10/test10-with-incompatible-exported-symbols.cc' line='15' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='_ZN9some_type17get_second_memberEv'>
+          <parameter type-id='type-id-4' name='this' is-artificial='yes'/>
+          <return type-id='type-id-6'/>
+        </function-decl>
+      </member-function>
+      <member-function access='public'>
+        <function-decl name='set_first_member' mangled-name='_ZN9some_type16set_first_memberEi' filepath='/home/dodji/git/libabigail/emit-undefined-ifaces/tests/data/test-abicompat/test10/test10-with-incompatible-exported-symbols.cc' line='11' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='_ZN9some_type16set_first_memberEi'>
+          <parameter type-id='type-id-4' name='this' is-artificial='yes'/>
+          <parameter type-id='type-id-3' name='v' filepath='/home/dodji/git/libabigail/emit-undefined-ifaces/tests/data/test-abicompat/test10/test10-with-incompatible-exported-symbols.cc' line='11' column='1'/>
+          <return type-id='type-id-5'/>
+        </function-decl>
+      </member-function>
+      <member-function access='public'>
+        <function-decl name='get_first_member' mangled-name='_ZN9some_type16get_first_memberEv' filepath='/home/dodji/git/libabigail/emit-undefined-ifaces/tests/data/test-abicompat/test10/test10-with-incompatible-exported-symbols.cc' line='7' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='_ZN9some_type16get_first_memberEv'>
+          <parameter type-id='type-id-4' name='this' is-artificial='yes'/>
+          <return type-id='type-id-7'/>
+        </function-decl>
+      </member-function>
+    </class-decl>
+    <type-decl name='int' size-in-bits='32' id='type-id-3'/>
+    <pointer-type-def type-id='type-id-1' size-in-bits='64' id='type-id-6'/>
+    <pointer-type-def type-id='type-id-3' size-in-bits='64' id='type-id-7'/>
+    <pointer-type-def type-id='type-id-2' size-in-bits='64' id='type-id-4'/>
+    <qualified-type-def type-id='type-id-4' const='yes' id='type-id-8'/>
+    <type-decl name='void' id='type-id-5'/>
+  </abi-instr>
+</abi-corpus>
diff --git a/tests/data/test-abicompat/test10/test10-app-with-undefined-symbols b/tests/data/test-abicompat/test10/test10-app-with-undefined-symbols
new file mode 100755
index 0000000000000000000000000000000000000000..4d5f6c32c8e72a0d549648f96c477710060e2d7c
GIT binary patch
literal 26072
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
M;N!4F2I;o{2cRGJzW@LL

literal 0
HcmV?d00001

diff --git a/tests/data/test-abicompat/test10/test10-app-with-undefined-symbols.abi b/tests/data/test-abicompat/test10/test10-app-with-undefined-symbols.abi
--- /dev/null
+++ b/tests/data/test-abicompat/test10/test10-app-with-undefined-symbols.abi
@@ -0,0 +1,55 @@ 
+<abi-corpus version='2.3' path='test10-app-with-undefined-symbols' architecture='elf-amd-x86_64'>
+  <elf-needed>
+    <dependency name='libtest10-with-exported-symbols.so'/>
+    <dependency name='libstdc++.so.6'/>
+    <dependency name='libm.so.6'/>
+    <dependency name='libgcc_s.so.1'/>
+    <dependency name='libc.so.6'/>
+  </elf-needed>
+  <elf-function-symbols>
+    <elf-symbol name='_start' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+    <elf-symbol name='main' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+  </elf-function-symbols>
+  <elf-variable-symbols>
+    <elf-symbol name='_IO_stdin_used' size='4' type='object-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+  </elf-variable-symbols>
+  <undefined-elf-function-symbols>
+    <elf-symbol name='_ZN9some_type16get_first_memberEv' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='no'/>
+    <elf-symbol name='_ZN9some_type17get_second_memberEv' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='no'/>
+    <elf-symbol name='__libc_start_main@GLIBC_2.34' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='no'/>
+  </undefined-elf-function-symbols>
+  <undefined-elf-variable-symbols>
+    <elf-symbol name='_ITM_deregisterTMCloneTable' type='no-type' binding='weak-binding' visibility='default-visibility' is-defined='no'/>
+    <elf-symbol name='_ITM_registerTMCloneTable' type='no-type' binding='weak-binding' visibility='default-visibility' is-defined='no'/>
+    <elf-symbol name='__gmon_start__' type='no-type' binding='weak-binding' visibility='default-visibility' is-defined='no'/>
+  </undefined-elf-variable-symbols>
+  <abi-instr address-size='64' path='test10-app-with-undefined-symbols.cc' comp-dir-path='/home/dodji/git/libabigail/emit-undefined-ifaces/tests/data/test-abicompat/test10' language='LANG_C_plus_plus_14'>
+    <type-decl name='char' size-in-bits='8' id='type-id-1'/>
+    <class-decl name='some_type' size-in-bits='64' visibility='default' filepath='/home/dodji/git/libabigail/emit-undefined-ifaces/tests/data/test-abicompat/test10/test10-with-exported-symbols.h' line='1' column='1' id='type-id-2'>
+      <data-member access='private' layout-offset-in-bits='0'>
+        <var-decl name='first_member' type-id='type-id-3' visibility='default' filepath='/home/dodji/git/libabigail/emit-undefined-ifaces/tests/data/test-abicompat/test10/test10-with-exported-symbols.h' line='12' column='1'/>
+      </data-member>
+      <data-member access='private' layout-offset-in-bits='32'>
+        <var-decl name='second_member' type-id='type-id-1' visibility='default' filepath='/home/dodji/git/libabigail/emit-undefined-ifaces/tests/data/test-abicompat/test10/test10-with-exported-symbols.h' line='13' column='1'/>
+      </data-member>
+      <member-function access='public'>
+        <function-decl name='get_first_member' mangled-name='_ZN9some_type16get_first_memberEv' filepath='/home/dodji/git/libabigail/emit-undefined-ifaces/tests/data/test-abicompat/test10/test10-with-exported-symbols.h' line='5' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='_ZN9some_type16get_first_memberEv'>
+          <parameter type-id='type-id-4' is-artificial='yes'/>
+          <return type-id='type-id-3'/>
+        </function-decl>
+      </member-function>
+      <member-function access='public'>
+        <function-decl name='get_second_member' mangled-name='_ZN9some_type17get_second_memberEv' filepath='/home/dodji/git/libabigail/emit-undefined-ifaces/tests/data/test-abicompat/test10/test10-with-exported-symbols.h' line='8' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='_ZN9some_type17get_second_memberEv'>
+          <parameter type-id='type-id-4' is-artificial='yes'/>
+          <return type-id='type-id-1'/>
+        </function-decl>
+      </member-function>
+    </class-decl>
+    <type-decl name='int' size-in-bits='32' id='type-id-3'/>
+    <pointer-type-def type-id='type-id-2' size-in-bits='64' id='type-id-4'/>
+    <function-decl name='main' mangled-name='main' filepath='/home/dodji/git/libabigail/emit-undefined-ifaces/tests/data/test-abicompat/test10/test10-app-with-undefined-symbols.cc' line='9' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='main'>
+      <return type-id='type-id-3'/>
+    </function-decl>
+    <type-decl name='void' id='type-id-5'/>
+  </abi-instr>
+</abi-corpus>
diff --git a/tests/data/test-abicompat/test10/test10-app-with-undefined-symbols.cc b/tests/data/test-abicompat/test10/test10-app-with-undefined-symbols.cc
new file mode 100644
index 00000000..07d223e6
--- /dev/null
+++ b/tests/data/test-abicompat/test10/test10-app-with-undefined-symbols.cc
@@ -0,0 +1,13 @@ 
+// Compile with:
+//
+// g++ -g -L. -ltest10-with-exported-symbols -o  test10-app-with-undefined-symbols test10-app-with-undefined-symbols.cc
+//
+
+#include "test10-with-exported-symbols.h"
+
+int
+main()
+{
+  some_type s;
+  return s.get_first_member() + s.get_second_member();
+}
diff --git a/tests/data/test-abicompat/test10/test10-fn-changed-report-0.txt b/tests/data/test-abicompat/test10/test10-fn-changed-report-0.txt
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/data/test-abicompat/test10/test10-fn-changed-report-1.txt b/tests/data/test-abicompat/test10/test10-fn-changed-report-1.txt
new file mode 100644
index 00000000..3824b9e3
--- /dev/null
+++ b/tests/data/test-abicompat/test10/test10-fn-changed-report-1.txt
@@ -0,0 +1,13 @@ 
+functions defined in library 'libtest10-with-incompatible-exported-symbols.so'
+have sub-types that are different from what application 'test10-app-with-undefined-symbols' expects:
+
+  method int some_type::get_first_member():
+    return type changed:
+      entity changed from 'int' to 'int*'
+      type size changed from 32 to 64 (in bits)
+
+  method char some_type::get_second_member():
+    return type changed:
+      entity changed from 'char' to 'char*'
+      type size changed from 8 to 64 (in bits)
+
diff --git a/tests/data/test-abicompat/test10/test10-fn-changed-report-2.txt b/tests/data/test-abicompat/test10/test10-fn-changed-report-2.txt
new file mode 100644
index 00000000..d5ae6534
--- /dev/null
+++ b/tests/data/test-abicompat/test10/test10-fn-changed-report-2.txt
@@ -0,0 +1,13 @@ 
+functions defined in library 'libtest10-with-incompatible-exported-symbols.so.abi'
+have sub-types that are different from what application 'test10-app-with-undefined-symbols' expects:
+
+  method int some_type::get_first_member():
+    return type changed:
+      entity changed from 'int' to 'int*'
+      type size changed from 32 to 64 (in bits)
+
+  method char some_type::get_second_member():
+    return type changed:
+      entity changed from 'char' to 'char*'
+      type size changed from 8 to 64 (in bits)
+
diff --git a/tests/data/test-abicompat/test10/test10-fn-changed-report-3.txt b/tests/data/test-abicompat/test10/test10-fn-changed-report-3.txt
new file mode 100644
index 00000000..3df28e7c
--- /dev/null
+++ b/tests/data/test-abicompat/test10/test10-fn-changed-report-3.txt
@@ -0,0 +1,13 @@ 
+functions defined in library 'libtest10-with-incompatible-exported-symbols.so'
+have sub-types that are different from what application 'test10-app-with-undefined-symbols.abi' expects:
+
+  method int some_type::get_first_member():
+    return type changed:
+      entity changed from 'int' to 'int*'
+      type size changed from 32 to 64 (in bits)
+
+  method char some_type::get_second_member():
+    return type changed:
+      entity changed from 'char' to 'char*'
+      type size changed from 8 to 64 (in bits)
+
diff --git a/tests/data/test-abicompat/test10/test10-fn-changed-report-4.txt b/tests/data/test-abicompat/test10/test10-fn-changed-report-4.txt
new file mode 100644
index 00000000..8ea5b16c
--- /dev/null
+++ b/tests/data/test-abicompat/test10/test10-fn-changed-report-4.txt
@@ -0,0 +1,13 @@ 
+functions defined in library 'libtest10-with-incompatible-exported-symbols.so.abi'
+have sub-types that are different from what application 'test10-app-with-undefined-symbols.abi' expects:
+
+  method int some_type::get_first_member():
+    return type changed:
+      entity changed from 'int' to 'int*'
+      type size changed from 32 to 64 (in bits)
+
+  method char some_type::get_second_member():
+    return type changed:
+      entity changed from 'char' to 'char*'
+      type size changed from 8 to 64 (in bits)
+
diff --git a/tests/data/test-abicompat/test10/test10-with-exported-symbols.cc b/tests/data/test-abicompat/test10/test10-with-exported-symbols.cc
new file mode 100644
index 00000000..b2be7eac
--- /dev/null
+++ b/tests/data/test-abicompat/test10/test10-with-exported-symbols.cc
@@ -0,0 +1,20 @@ 
+// Build this with:
+// g++ -g -Wall -shared  -o libtest10-with-exported-symbols.so test10-with-exported-symbols.cc
+
+#include "test10-with-exported-symbols.h"
+
+int
+some_type::get_first_member()
+{return first_member;}
+
+void
+some_type::set_first_member(int v)
+{first_member = v;}
+
+char
+some_type::get_second_member()
+{return second_member;}
+
+void
+some_type::set_second_member(char v)
+{second_member = v;}
diff --git a/tests/data/test-abicompat/test10/test10-with-exported-symbols.h b/tests/data/test-abicompat/test10/test10-with-exported-symbols.h
new file mode 100644
index 00000000..ce206570
--- /dev/null
+++ b/tests/data/test-abicompat/test10/test10-with-exported-symbols.h
@@ -0,0 +1,14 @@ 
+class some_type
+{
+ public:
+
+  int get_first_member();
+  void set_first_member(int);
+
+  char get_second_member();
+  void set_second_member(char);
+
+ private:
+  int first_member = 0;
+  char second_member = 0;
+};
diff --git a/tests/data/test-abicompat/test10/test10-with-incompatible-exported-symbols.cc b/tests/data/test-abicompat/test10/test10-with-incompatible-exported-symbols.cc
new file mode 100644
index 00000000..f93a5458
--- /dev/null
+++ b/tests/data/test-abicompat/test10/test10-with-incompatible-exported-symbols.cc
@@ -0,0 +1,20 @@ 
+// Build this with:
+// g++ -g -Wall -shared  -o libtest10-with-incompatible-exported-symbols.so test10-with-incompatible-exported-symbols.cc
+
+#include "test10-with-incompatible-exported-symbols.h"
+
+int*
+some_type::get_first_member()
+{return &first_member;}
+
+void
+some_type::set_first_member(int v)
+{first_member = v;}
+
+char*
+some_type::get_second_member()
+{return &second_member;}
+
+void
+some_type::set_second_member(char v)
+{second_member = v;}
diff --git a/tests/data/test-abicompat/test10/test10-with-incompatible-exported-symbols.h b/tests/data/test-abicompat/test10/test10-with-incompatible-exported-symbols.h
new file mode 100644
index 00000000..bfed108d
--- /dev/null
+++ b/tests/data/test-abicompat/test10/test10-with-incompatible-exported-symbols.h
@@ -0,0 +1,14 @@ 
+class some_type
+{
+ public:
+
+  int* get_first_member();
+  void set_first_member(int);
+
+  char* get_second_member();
+  void set_second_member(char);
+
+ private:
+  int first_member = 0;
+  char second_member = 0;
+};
diff --git a/tests/test-abicompat.cc b/tests/test-abicompat.cc
index 320436c3..a7fcc520 100644
--- a/tests/test-abicompat.cc
+++ b/tests/test-abicompat.cc
@@ -226,6 +226,60 @@  InOutSpec in_out_specs[] =
     "data/test-abicompat/test9-fn-changed-report-0.txt",
     "output/test-abicompat/test9-fn-changed-report-0.txt",
   },
+  {
+    "data/test-abicompat/test10/test10-app-with-undefined-symbols",
+    "data/test-abicompat/test10/libtest10-with-exported-symbols.so",
+    "",
+    "",
+    "--show-base-names",
+    "data/test-abicompat/test10/test10-fn-changed-report-0.txt",
+    "output/test-abicompat/test10/test10-fn-changed-report-0.txt",
+  },
+  {
+    "data/test-abicompat/test10/test10-app-with-undefined-symbols.abi",
+    "data/test-abicompat/test10/libtest10-with-exported-symbols.so",
+    "",
+    "",
+    "--show-base-names",
+    "data/test-abicompat/test10/test10-fn-changed-report-0.txt",
+    "output/test-abicompat/test10/test10-fn-changed-report-0.txt",
+  },
+  {
+    "data/test-abicompat/test10/test10-app-with-undefined-symbols",
+    "data/test-abicompat/test10/libtest10-with-incompatible-exported-symbols.so",
+    "",
+    "",
+    "--show-base-names",
+    "data/test-abicompat/test10/test10-fn-changed-report-1.txt",
+    "output/test-abicompat/test10/test10-fn-changed-report-1.txt",
+  },
+  {
+    "data/test-abicompat/test10/test10-app-with-undefined-symbols",
+    "data/test-abicompat/test10/libtest10-with-incompatible-exported-symbols.so.abi",
+    "",
+    "",
+    "--show-base-names",
+    "data/test-abicompat/test10/test10-fn-changed-report-2.txt",
+    "output/test-abicompat/test10/test10-fn-changed-report-2.txt",
+  },
+  {
+    "data/test-abicompat/test10/test10-app-with-undefined-symbols.abi",
+    "data/test-abicompat/test10/libtest10-with-incompatible-exported-symbols.so",
+    "",
+    "",
+    "--show-base-names",
+    "data/test-abicompat/test10/test10-fn-changed-report-3.txt",
+    "output/test-abicompat/test10/test10-fn-changed-report-3.txt",
+  },
+  {
+    "data/test-abicompat/test10/test10-app-with-undefined-symbols.abi",
+    "data/test-abicompat/test10/libtest10-with-incompatible-exported-symbols.so.abi",
+    "",
+    "",
+    "--show-base-names",
+    "data/test-abicompat/test10/test10-fn-changed-report-4.txt",
+    "output/test-abicompat/test10/test10-fn-changed-report-4.txt",
+  },
   // This entry must be the last one.
   {0, 0, 0, 0, 0, 0, 0}
 };
diff --git a/tests/test-annotate.cc b/tests/test-annotate.cc
index cb9c8af6..8ddd93fb 100644
--- a/tests/test-annotate.cc
+++ b/tests/test-annotate.cc
@@ -158,7 +158,7 @@  main()
   string abidw;
 
   abidw = string(get_build_dir()) + "/tools/abidw "
-    "--annotate --no-corpus-path --no-architecture";
+    "--annotate --no-corpus-path --no-architecture --no-load-undefined-interfaces";
   for (InOutSpec* s = in_out_specs; s->in_elf_path; ++s)
     {
       bool is_ok = true;
diff --git a/tests/test-read-common.cc b/tests/test-read-common.cc
index 1d70b3d0..8c682b1c 100644
--- a/tests/test-read-common.cc
+++ b/tests/test-read-common.cc
@@ -74,6 +74,7 @@  test_task::serialize_corpus(const string& out_abi_path,
   write_context_sptr write_ctxt
       = create_write_context(corp->get_environment(), of);
   set_type_id_style(*write_ctxt, spec.type_id_style);
+  set_write_undefined_symbols(*write_ctxt, false);
   is_ok = write_corpus(*write_ctxt, corp, /*indent=*/0);
   of.close();
 
diff --git a/tests/test-read-dwarf.cc b/tests/test-read-dwarf.cc
index 8570d774..a391c0f1 100644
--- a/tests/test-read-dwarf.cc
+++ b/tests/test-read-dwarf.cc
@@ -651,7 +651,7 @@  test_task_dwarf::perform()
   if (spec.type_id_style == HASH_TYPE_ID_STYLE)
     type_id_style = "hash";
 
-  string cmd = abidw + " --no-architecture "
+  string cmd = abidw + " --no-architecture --no-load-undefined-interfaces"
     + " --type-id-style " + type_id_style
     + " --no-corpus-path "
     + drop_private_types + " " + in_elf_path
diff --git a/tools/abidw.cc b/tools/abidw.cc
index fe977b1d..c6cc0562 100644
--- a/tools/abidw.cc
+++ b/tools/abidw.cc
@@ -109,6 +109,7 @@  struct options
   bool			short_locs;
   bool			default_sizes;
   bool			load_all_types;
+  bool			load_undefined_interfaces;
   bool			linux_kernel_mode;
   bool			corpus_group_for_linux;
   bool			show_stats;
@@ -155,6 +156,7 @@  struct options
       short_locs(false),
       default_sizes(true),
       load_all_types(),
+      load_undefined_interfaces(true),
       linux_kernel_mode(true),
       corpus_group_for_linux(false),
       show_stats(),
@@ -233,6 +235,8 @@  display_usage(const string& prog_name, ostream& out)
     "debug info of <elf-path>, and show its base name\n"
     << "  --load-all-types  read all types including those not reachable from "
     "exported declarations\n"
+    << "  --no-load-undefined-interfaces  do not consider undefined "
+    "interfaces from the binary"
     << "  --no-linux-kernel-mode  don't consider the input binary as "
        "a Linux Kernel binary\n"
     << "  --kmi-whitelist|-w  path to a linux kernel "
@@ -446,6 +450,8 @@  parse_command_line(int argc, char* argv[], options& opts)
 	}
       else if (!strcmp(argv[i], "--load-all-types"))
 	opts.load_all_types = true;
+      else if (!strcmp(argv[i], "--no-load-undefined-interfaces"))
+	opts.load_undefined_interfaces = false;
       else if (!strcmp(argv[i], "--drop-private-types"))
 	opts.drop_private_types = true;
       else if (!strcmp(argv[i], "--drop-undefined-syms"))
@@ -615,6 +621,7 @@  set_generic_options(abigail::elf_based_reader& rdr, options& opts)
     opts.leverage_dwarf_factorization;
   rdr.options().assume_odr_for_cplusplus =
     opts.assume_odr_for_cplusplus;
+  rdr.options().load_undefined_interfaces = opts.load_undefined_interfaces;
 }
 
 /// Load an ABI @ref corpus (the internal representation of the ABI of