diff --git a/doc/manuals/abicompat.rst b/doc/manuals/abicompat.rst
index e88a05a8..8f19c433 100644
--- a/doc/manuals/abicompat.rst
+++ b/doc/manuals/abicompat.rst
@@ -77,6 +77,11 @@ Options
    Do not show information about where in the *second shared library*
    the respective type was changed.
 
+  * ``--btf``
+
+    When comparing binaries, extract ABI information from BTF debug
+    information, if present.
+
   * ``--ctf``
 
     When comparing binaries, extract ABI information from CTF debug
diff --git a/include/abg-elf-reader.h b/include/abg-elf-reader.h
index ab350607..12f7b026 100644
--- a/include/abg-elf-reader.h
+++ b/include/abg-elf-reader.h
@@ -148,6 +148,12 @@ class reader : public fe_iface
   elf_symbol_sptr
   variable_symbol_is_exported(const string& name) const;
 
+  elf_symbol_sptr
+  function_symbol_is_undefined(const string& name) const;
+
+  elf_symbol_sptr
+  variable_symbol_is_undefined(const string& name) const;
+
   void
   load_dt_soname_and_needed();
 
diff --git a/src/abg-btf-reader.cc b/src/abg-btf-reader.cc
index ce7a7773..25fcb685 100644
--- a/src/abg-btf-reader.cc
+++ b/src/abg-btf-reader.cc
@@ -397,7 +397,8 @@ public:
       }
 
     canonicalize_types();
-
+    corpus()->sort_functions();
+    corpus()->sort_variables();
     return corpus();
   }
 
@@ -504,15 +505,9 @@ public:
     associate_artifact_to_btf_type_id(result, type_id);
 
     if (function_decl_sptr fn = is_function_decl(result))
-      {
-	if (fn->get_is_in_public_symbol_table())
-	  add_fn_to_exported_or_undefined_decls(fn.get());
-      }
+      add_fn_to_exported_or_undefined_decls(fn.get());
     else if (var_decl_sptr var = is_var_decl(result))
-      {
-	if (var->get_is_in_public_symbol_table())
-	  add_var_to_exported_or_undefined_decls(var.get());
-      }
+      add_var_to_exported_or_undefined_decls(var.get());
 
     return result;
   }
@@ -1021,10 +1016,12 @@ public:
 				   location(), /*linkage_name=*/fn_name));
 
     elf_symbol_sptr fn_sym;
-    if ((fn_sym = function_symbol_is_exported(fn_name)))
+    if ((fn_sym = function_symbol_is_exported(fn_name))
+	|| (fn_sym = function_symbol_is_undefined(fn_name)))
       {
 	result->set_symbol(fn_sym);
-	result->set_is_in_public_symbol_table(true);
+	if (fn_sym->is_defined())
+	  result->set_is_in_public_symbol_table(true);
       }
     return result;
   }
@@ -1055,10 +1052,12 @@ public:
 			      /*linkage_name=*/var_name));
 
     elf_symbol_sptr var_sym;
-    if ((var_sym = variable_symbol_is_exported(var_name)))
+    if ((var_sym = variable_symbol_is_exported(var_name))
+	|| (var_sym = variable_symbol_is_undefined(var_name)))
       {
 	result->set_symbol(var_sym);
-	result->set_is_in_public_symbol_table(true);
+	if (var_sym->is_defined())
+	  result->set_is_in_public_symbol_table(true);
       }
     return result;
   }
diff --git a/src/abg-elf-reader.cc b/src/abg-elf-reader.cc
index 14f8ce6a..cbb20dd4 100644
--- a/src/abg-elf-reader.cc
+++ b/src/abg-elf-reader.cc
@@ -890,6 +890,25 @@ reader::variable_symbol_is_exported(const string& name) const
     }
   return elf_symbol_sptr();
 }
+
+/// Test if a name is the name of an undefined function symbol.
+///
+/// @param name the symbol name to consider.
+///
+/// @return the undefined function symbol or nil if none was found.
+elf_symbol_sptr
+reader::function_symbol_is_undefined(const string& name) const
+{return symtab()->function_symbol_is_undefined(name);}
+
+/// Test if a name is the name of an undefined variable symbol.
+///
+/// @param name the symbol name to consider.
+///
+/// @return the undefined variable symbol or nil if none was found.
+elf_symbol_sptr
+reader::variable_symbol_is_undefined(const string& name) const
+{return symtab()->variable_symbol_is_undefined(name);}
+
 /// Load the DT_NEEDED and DT_SONAME elf TAGS.
 void
 reader::load_dt_soname_and_needed()
diff --git a/src/abg-symtab-reader.cc b/src/abg-symtab-reader.cc
index 51177369..fc0cac59 100644
--- a/src/abg-symtab-reader.cc
+++ b/src/abg-symtab-reader.cc
@@ -257,26 +257,40 @@ symtab::variable_symbol_is_exported(const GElf_Addr symbol_address)
 ///
 /// @param sym_name the symbol name to consider.
 ///
-/// @return true iff @p sym_name is the name of a an undefined
-/// function symbol.
-bool
+/// @return the undefined symbol if found, nil otherwise.
+elf_symbol_sptr
 symtab::function_symbol_is_undefined(const string& sym_name)
 {
   collect_undefined_fns_and_vars_linkage_names();
-  return undefined_function_linkage_names_.count(sym_name);
+  if (undefined_function_linkage_names_.count(sym_name))
+    {
+      elf_symbol_sptr sym = lookup_undefined_function_symbol(sym_name);
+      ABG_ASSERT(sym);
+      ABG_ASSERT(sym->is_function());
+      ABG_ASSERT(!sym->is_defined());
+      return sym;
+    }
+  return elf_symbol_sptr();
 }
 
 /// Test if a name is a the name of an undefined variable symbol.
 ///
 /// @param sym_name the symbol name to consider.
 ///
-/// @return true iff @p sym_name is the name of a an undefined
-/// variable symbol.
-bool
+// @return the undefined symbol if found, nil otherwise.
+elf_symbol_sptr
 symtab::variable_symbol_is_undefined(const string& sym_name)
 {
   collect_undefined_fns_and_vars_linkage_names();
-  return undefined_variable_linkage_names_.count(sym_name);
+  if (undefined_variable_linkage_names_.count(sym_name))
+    {
+      elf_symbol_sptr sym = lookup_undefined_variable_symbol(sym_name);
+      ABG_ASSERT(sym);
+      ABG_ASSERT(sym->is_variable());
+      ABG_ASSERT(!sym->is_defined());
+      return sym;
+    }
+  return elf_symbol_sptr();
 }
 
 /// A symbol sorting functor.
diff --git a/src/abg-symtab-reader.h b/src/abg-symtab-reader.h
index 43b40b79..390ee874 100644
--- a/src/abg-symtab-reader.h
+++ b/src/abg-symtab-reader.h
@@ -260,10 +260,10 @@ public:
   elf_symbol_sptr
   variable_symbol_is_exported(const GElf_Addr symbol_address);
 
-  bool
+  elf_symbol_sptr
   function_symbol_is_undefined(const string&);
 
-  bool
+  elf_symbol_sptr
   variable_symbol_is_undefined(const string&);
 
   static symtab_ptr
diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am
index 3e175209..5bf01528 100644
--- a/tests/data/Makefile.am
+++ b/tests/data/Makefile.am
@@ -2097,16 +2097,21 @@ test-abicompat/test6-var-changed-report-0.txt \
 test-abicompat/test6-var-changed-report-1.txt \
 test-abicompat/test6-var-changed-report-2.txt \
 test-abicompat/libtest7-fn-changed-libapp-v0.so \
+test-abicompat/libtest7-fn-changed-libapp-btf-v0.so \
 test-abicompat/libtest7-fn-changed-libapp-v1.so \
+test-abicompat/libtest7-fn-changed-libapp-btf-v1.so \
 test-abicompat/test7-fn-changed-app \
+test-abicompat/test7-fn-changed-app.btf \
 test-abicompat/test7-fn-changed-app.c \
 test-abicompat/test7-fn-changed-libapp-v0.c \
 test-abicompat/test7-fn-changed-libapp-v0.h \
 test-abicompat/test7-fn-changed-libapp-v1.c \
 test-abicompat/test7-fn-changed-libapp-v1.h \
 test-abicompat/test7-fn-changed-report-0.txt \
+test-abicompat/test7-fn-changed-report-0.1.txt \
 test-abicompat/test7-fn-changed-report-1.txt \
 test-abicompat/test7-fn-changed-report-2.txt \
+test-abicompat/test7-fn-changed-report-2.1.txt \
 test-abicompat/libtest8-fn-changed-libapp-v0.so \
 test-abicompat/libtest8-fn-changed-libapp-v1.so \
 test-abicompat/test8-fn-changed-app \
diff --git a/tests/data/test-abicompat/libtest7-fn-changed-libapp-btf-v0.so b/tests/data/test-abicompat/libtest7-fn-changed-libapp-btf-v0.so
new file mode 100755
index 0000000000000000000000000000000000000000..a61749662ff7d29cdfd9be39d5faf9cb95782909
GIT binary patch
diff --git a/tests/data/test-abicompat/libtest7-fn-changed-libapp-btf-v1.so b/tests/data/test-abicompat/libtest7-fn-changed-libapp-btf-v1.so
diff --git a/tests/data/test-abicompat/test7-fn-changed-app.btf b/tests/data/test-abicompat/test7-fn-changed-app.btf
new file mode 100755
index 0000000000000000000000000000000000000000..71a01ef448a2a429a77fc9dfa2e7061da2931841
GIT binary patch
diff --git a/tests/data/test-abicompat/test7-fn-changed-app.c b/tests/data/test-abicompat/test7-fn-changed-app.c
--- a/tests/data/test-abicompat/test7-fn-changed-app.c
+++ b/tests/data/test-abicompat/test7-fn-changed-app.c
@@ -1,5 +1,7 @@
 // Compile this with:
 // gcc -g -o test7-fn-changed-app -L. -ltest7-fn-changed-libapp-v0 test7-fn-changed-app.c
+// Or, for BTF debug info, with:
+// gcc -gbtf -o test7-fn-changed-app.btf -L. -ltest7-fn-changed-libapp-v0 test7-fn-changed-app.c
 
 #include <stdio.h>
 #include "test7-fn-changed-libapp-v0.h"
diff --git a/tests/data/test-abicompat/test7-fn-changed-libapp-v0.c b/tests/data/test-abicompat/test7-fn-changed-libapp-v0.c
index 8c3581e2..c36993ce 100644
--- a/tests/data/test-abicompat/test7-fn-changed-libapp-v0.c
+++ b/tests/data/test-abicompat/test7-fn-changed-libapp-v0.c
@@ -1,5 +1,7 @@
 // Compile this with:
 // gcc -g -Wall -fPIC -shared -o libtest7-fn-changed-libapp-v0.so test7-fn-changed-libapp-v0.c
+// Or, for BTF debug info with:
+// gcc -gbtf -Wall -fPIC -shared -o libtest7-fn-changed-libapp-btf-v0.so test7-fn-changed-libapp-v0.c
 
 #include <stdio.h>
 #include "test7-fn-changed-libapp-v0.h"
diff --git a/tests/data/test-abicompat/test7-fn-changed-libapp-v1.c b/tests/data/test-abicompat/test7-fn-changed-libapp-v1.c
index 92c44394..822efa0c 100644
--- a/tests/data/test-abicompat/test7-fn-changed-libapp-v1.c
+++ b/tests/data/test-abicompat/test7-fn-changed-libapp-v1.c
@@ -1,5 +1,8 @@
 // Compile this with:
 // gcc -g -Wall -fPIC -shared -o libtest7-fn-changed-libapp-v1.so test7-fn-changed-libapp-v1.c
+// Or, for BTF debug info, with:
+// gcc -gbtf -Wall -fPIC -shared -o libtest7-fn-changed-libapp-btf-v1.so test7-fn-changed-libapp-v1.c
+
 #include <stdio.h>
 #include "test7-fn-changed-libapp-v1.h"
 
diff --git a/tests/data/test-abicompat/test7-fn-changed-report-0.1.txt b/tests/data/test-abicompat/test7-fn-changed-report-0.1.txt
new file mode 100644
index 00000000..39b69d8f
--- /dev/null
+++ b/tests/data/test-abicompat/test7-fn-changed-report-0.1.txt
@@ -0,0 +1,19 @@
+ELF file 'test7-fn-changed-app.btf' might not be ABI compatible with 'libtest7-fn-changed-libapp-btf-v1.so' due to differences with 'libtest7-fn-changed-libapp-btf-v0.so' below:
+Functions changes summary: 0 Removed, 2 Changed, 0 Added functions
+Variables changes summary: 0 Removed, 0 Changed, 0 Added variable
+
+2 functions with some indirect sub-type change:
+
+  [C] 'function float add(float, float)' has some indirect sub-type changes:
+    return type changed:
+      type name changed from 'float' to 'int'
+      type size hasn't changed
+      mangled name changed from 'float' to int
+
+  [C] 'function void print(const Student)' has some indirect sub-type changes:
+    parameter 1 of type 'const Student' has sub-type changes:
+      in unqualified underlying type 'struct Student':
+        type size changed from 128 to 192 (in bits)
+        1 data member insertion:
+          'float percentage', at offset 128 (in bits)
+
diff --git a/tests/data/test-abicompat/test7-fn-changed-report-2.1.txt b/tests/data/test-abicompat/test7-fn-changed-report-2.1.txt
new file mode 100644
index 00000000..4645006e
--- /dev/null
+++ b/tests/data/test-abicompat/test7-fn-changed-report-2.1.txt
@@ -0,0 +1,16 @@
+functions defined in library 'libtest7-fn-changed-libapp-btf-v1.so'
+have sub-types that are different from what application 'test7-fn-changed-app.btf' expects:
+
+  function float add(float, float):
+    return type changed:
+      type name changed from 'float' to 'int'
+      type size hasn't changed
+      mangled name changed from 'float' to int
+
+  function void print(const Student):
+    parameter 1 of type 'const Student' has sub-type changes:
+      in unqualified underlying type 'struct Student':
+        type size changed from 128 to 192 (in bits)
+        1 data member insertion:
+          'float percentage', at offset 128 (in bits)
+
diff --git a/tests/test-abicompat.cc b/tests/test-abicompat.cc
index e992454c..320436c3 100644
--- a/tests/test-abicompat.cc
+++ b/tests/test-abicompat.cc
@@ -168,6 +168,17 @@ InOutSpec in_out_specs[] =
     "data/test-abicompat/test7-fn-changed-report-0.txt",
     "output/test-abicompat/test7-fn-changed-report-0.txt",
   },
+#ifdef WITH_BTF
+  {
+    "data/test-abicompat/test7-fn-changed-app.btf",
+    "data/test-abicompat/libtest7-fn-changed-libapp-btf-v0.so",
+    "data/test-abicompat/libtest7-fn-changed-libapp-btf-v1.so",
+    "",
+    "--show-base-names --no-show-locs --no-redundant --btf",
+    "data/test-abicompat/test7-fn-changed-report-0.1.txt",
+    "output/test-abicompat/test7-fn-changed-report-0.1.txt",
+  },
+#endif
   {
     "data/test-abicompat/test7-fn-changed-app",
     "data/test-abicompat/libtest7-fn-changed-libapp-v1.so",
@@ -186,6 +197,17 @@ InOutSpec in_out_specs[] =
     "data/test-abicompat/test7-fn-changed-report-2.txt",
     "output/test-abicompat/test7-fn-changed-report-2.txt",
   },
+#ifdef WITH_BTF
+  {
+    "data/test-abicompat/test7-fn-changed-app.btf",
+    "data/test-abicompat/libtest7-fn-changed-libapp-btf-v1.so",
+    "",
+    "",
+    "--show-base-names --no-show-locs --weak-mode",
+    "data/test-abicompat/test7-fn-changed-report-2.1.txt",
+    "output/test-abicompat/test7-fn-changed-report-2.1.txt",
+  },
+#endif
   {
     "data/test-abicompat/test8-fn-changed-app",
     "data/test-abicompat/libtest8-fn-changed-libapp-v1.so",
diff --git a/tools/abicompat.cc b/tools/abicompat.cc
index 4e5a8e22..9c4771c9 100644
--- a/tools/abicompat.cc
+++ b/tools/abicompat.cc
@@ -34,6 +34,7 @@
 #include <iostream>
 #include <memory>
 #include <string>
+#include "config.h"
 #include "abg-config.h"
 #include "abg-tools-utils.h"
 #include "abg-corpus.h"
@@ -44,6 +45,9 @@
 #ifdef WITH_CTF
 #include "abg-ctf-reader.h"
 #endif
+#ifdef WITH_BTF
+#include "abg-btf-reader.h"
+#endif
 
 using std::string;
 using std::cerr;
@@ -111,6 +115,9 @@ public:
 #ifdef WITH_CTF
   bool			use_ctf;
 #endif
+#ifdef WITH_BTF
+  bool			use_btf;
+#endif
 
   options(const char* program_name)
     :prog_name(program_name),
@@ -128,6 +135,10 @@ public:
 #ifdef WITH_CTF
     ,
       use_ctf()
+#endif
+#ifdef WITH_BTF
+    ,
+      use_btf()
 #endif
   {}
 }; // end struct options
@@ -237,6 +248,9 @@ display_usage(const string& prog_name, ostream& out)
     "just one version of the library.\n"
 #ifdef WITH_CTF
     << "  --ctf use CTF instead of DWARF in ELF files\n"
+#endif
+#ifdef WITH_BTF
+    << "  --btf use BTF instead of DWARF in ELF files\n"
 #endif
     ;
 }
@@ -344,6 +358,10 @@ parse_command_line(int argc, char* argv[], options& opts)
 #ifdef WITH_CTF
       else if (!strcmp(argv[i], "--ctf"))
         opts.use_ctf = true;
+#endif
+#ifdef WITH_BTF
+      else if (!strcmp(argv[i], "--btf"))
+        opts.use_btf = true;
 #endif
       else
 	{
