diff --git a/tests/Makefile.am b/tests/Makefile.am
index 2d8ab9e794a4..c2d4d1dc5d35 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -49,6 +49,7 @@ runtestini			\
 runtestkmiwhitelist		\
 runtestlookupsyms		\
 runtestreadwrite		\
+runtestsymtab			\
 runtesttoolsutils		\
 $(FEDABIPKGDIFF_TEST) 		\
 $(ZIP_ARCHIVE_TESTS)		\
@@ -154,6 +155,9 @@ runtestelfhelpers_LDADD = libcatch.la $(top_builddir)/src/libabigail.la
 runtestcxxcompat_SOURCES = test-cxx-compat.cc
 runtestcxxcompat_LDADD = libcatch.la $(top_builddir)/src/libabigail.la
 
+runtestsymtab_SOURCES = test-symtab.cc
+runtestsymtab_LDADD = libtestutils.la libcatch.la $(top_builddir)/src/libabigail.la
+
 runtestsvg_SOURCES=test-svg.cc
 runtestsvg_LDADD=$(top_builddir)/src/libabigail.la
 
diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am
index 7e91f52c44fe..6505de594ab2 100644
--- a/tests/data/Makefile.am
+++ b/tests/data/Makefile.am
@@ -1700,4 +1700,24 @@ test-ini/test01-equal-in-property-string.abignore \
 test-kmi-whitelist/whitelist-with-single-entry \
 test-kmi-whitelist/whitelist-with-another-single-entry \
 test-kmi-whitelist/whitelist-with-duplicate-entry \
-test-kmi-whitelist/whitelist-with-two-sections
+test-kmi-whitelist/whitelist-with-two-sections \
+\
+test-symtab/basic/Makefile \
+test-symtab/basic/empty.c \
+test-symtab/basic/empty.so \
+test-symtab/basic/link_against_me.c \
+test-symtab/basic/link_against_me.so \
+test-symtab/basic/no_debug_info.c \
+test-symtab/basic/no_debug_info.so \
+test-symtab/basic/one_function_one_variable.c \
+test-symtab/basic/one_function_one_variable.so \
+test-symtab/basic/one_function_one_variable_undefined.c \
+test-symtab/basic/one_function_one_variable_undefined.so \
+test-symtab/basic/single_function.c \
+test-symtab/basic/single_function.so \
+test-symtab/basic/single_undefined_function.c \
+test-symtab/basic/single_undefined_function.so \
+test-symtab/basic/single_undefined_variable.c \
+test-symtab/basic/single_undefined_variable.so \
+test-symtab/basic/single_variable.c \
+test-symtab/basic/single_variable.so
diff --git a/tests/data/test-symtab/basic/Makefile b/tests/data/test-symtab/basic/Makefile
new file mode 100644
index 000000000000..c84d128a4e28
--- /dev/null
+++ b/tests/data/test-symtab/basic/Makefile
@@ -0,0 +1,19 @@
+srcs = $(wildcard *.c)
+libs = $(srcs:.c=.so)
+
+flags = -fPIC
+
+all:	$(libs)
+
+clean:
+	rm $(libs)
+
+no_debug_info.so: no_debug_info.c Makefile
+	$(CC) $< -shared $(flags) -o $@
+
+link_against_me.so: link_against_me.c
+	$(CC) $< -shared $(flags) -o $@
+
+%.so: %.c Makefile link_against_me.so
+	$(CC) $< -shared $(flags) -g -o $@ link_against_me.so
+
diff --git a/tests/data/test-symtab/basic/empty.c b/tests/data/test-symtab/basic/empty.c
new file mode 100644
index 000000000000..5bb380ba0db8
--- /dev/null
+++ b/tests/data/test-symtab/basic/empty.c
@@ -0,0 +1 @@
+// empty!
diff --git a/tests/data/test-symtab/basic/empty.so b/tests/data/test-symtab/basic/empty.so
new file mode 100755
index 0000000000000000000000000000000000000000..28a54747116d9aa30b02192080e09300835c6716
GIT binary patch
diff --git a/tests/data/test-symtab/basic/link_against_me.c b/tests/data/test-symtab/basic/link_against_me.c
new file mode 100644
index 000000000000..42d6a46bd23d
--- /dev/null
+++ b/tests/data/test-symtab/basic/link_against_me.c
@@ -0,0 +1,2 @@
+int undefined_variable =1;
+void undefined_function() { }
diff --git a/tests/data/test-symtab/basic/link_against_me.so b/tests/data/test-symtab/basic/link_against_me.so
new file mode 100755
index 0000000000000000000000000000000000000000..33c8b5d62cd639f4cb96ff2b363326fbf1818668
GIT binary patch
diff --git a/tests/data/test-symtab/basic/no_debug_info.c b/tests/data/test-symtab/basic/no_debug_info.c
new file mode 100644
index 000000000000..5bb380ba0db8
--- /dev/null
+++ b/tests/data/test-symtab/basic/no_debug_info.c
@@ -0,0 +1 @@
+// empty!
diff --git a/tests/data/test-symtab/basic/no_debug_info.so b/tests/data/test-symtab/basic/no_debug_info.so
new file mode 100755
index 0000000000000000000000000000000000000000..827c1eee3e4e8f326af7e360ea444627ee59bee6
GIT binary patch
diff --git a/tests/data/test-symtab/basic/one_function_one_variable.c b/tests/data/test-symtab/basic/one_function_one_variable.c
new file mode 100644
index 000000000000..f31122a88fb8
--- /dev/null
+++ b/tests/data/test-symtab/basic/one_function_one_variable.c
@@ -0,0 +1,3 @@
+void exported_function(){}
+
+int exported_variable;
diff --git a/tests/data/test-symtab/basic/one_function_one_variable.so b/tests/data/test-symtab/basic/one_function_one_variable.so
new file mode 100755
index 0000000000000000000000000000000000000000..51d918c7ec9be6e5a900bcccc5f0964486d76cb4
GIT binary patch
diff --git a/tests/data/test-symtab/basic/one_function_one_variable_undefined.c b/tests/data/test-symtab/basic/one_function_one_variable_undefined.c
new file mode 100644
index 000000000000..53ff5d82c5f8
--- /dev/null
+++ b/tests/data/test-symtab/basic/one_function_one_variable_undefined.c
@@ -0,0 +1,7 @@
+extern int undefined_variable;
+void undefined_function(int);
+
+
+static void local_function(){
+	undefined_function(undefined_variable);
+}
diff --git a/tests/data/test-symtab/basic/one_function_one_variable_undefined.so b/tests/data/test-symtab/basic/one_function_one_variable_undefined.so
new file mode 100755
index 0000000000000000000000000000000000000000..fc2356c2aa0af88bad9f3c0f88837bebd5a53575
GIT binary patch
diff --git a/tests/data/test-symtab/basic/single_function.c b/tests/data/test-symtab/basic/single_function.c
new file mode 100644
index 000000000000..8ac09016eb4d
--- /dev/null
+++ b/tests/data/test-symtab/basic/single_function.c
@@ -0,0 +1 @@
+void exported_function(){}
diff --git a/tests/data/test-symtab/basic/single_function.so b/tests/data/test-symtab/basic/single_function.so
new file mode 100755
index 0000000000000000000000000000000000000000..9b7e45d6ab0f03d602ff2ac50be3539bf909ace6
GIT binary patch
diff --git a/tests/data/test-symtab/basic/single_undefined_function.c b/tests/data/test-symtab/basic/single_undefined_function.c
new file mode 100644
index 000000000000..484d6a5c6943
--- /dev/null
+++ b/tests/data/test-symtab/basic/single_undefined_function.c
@@ -0,0 +1,5 @@
+void undefined_function();
+
+static void local_function() {
+  undefined_function();
+}
diff --git a/tests/data/test-symtab/basic/single_undefined_function.so b/tests/data/test-symtab/basic/single_undefined_function.so
new file mode 100755
index 0000000000000000000000000000000000000000..96d19e3ed7e37160315025ad081ba54f4be03c12
GIT binary patch
diff --git a/tests/data/test-symtab/basic/single_undefined_variable.c b/tests/data/test-symtab/basic/single_undefined_variable.c
new file mode 100644
index 000000000000..b0602c2d4e6a
--- /dev/null
+++ b/tests/data/test-symtab/basic/single_undefined_variable.c
@@ -0,0 +1,7 @@
+extern int undefined_variable;
+
+static void other_local_function(int variable) { (void)variable; }
+
+static void local_function(){
+	other_local_function(undefined_variable);
+}
diff --git a/tests/data/test-symtab/basic/single_undefined_variable.so b/tests/data/test-symtab/basic/single_undefined_variable.so
new file mode 100755
index 0000000000000000000000000000000000000000..7188667cc6d6495bd7712e0f31ec5ed3f75b8737
GIT binary patch
diff --git a/tests/data/test-symtab/basic/single_variable.c b/tests/data/test-symtab/basic/single_variable.c
new file mode 100644
index 000000000000..620e156dccbc
--- /dev/null
+++ b/tests/data/test-symtab/basic/single_variable.c
@@ -0,0 +1 @@
+int exported_variable;
diff --git a/tests/data/test-symtab/basic/single_variable.so b/tests/data/test-symtab/basic/single_variable.so
new file mode 100755
index 0000000000000000000000000000000000000000..6ba3f4343c69b87a8ec280b6cc59b3db7fd33d8d
GIT binary patch
diff --git a/tests/test-symtab.cc b/tests/test-symtab.cc
new file mode 100644
index 000000000000..62f8998af5d0
--- /dev/null
+++ b/tests/test-symtab.cc
@@ -0,0 +1,205 @@
+// -*- Mode: C++ -*-
+//
+// Copyright (C) 2020 Google, Inc.
+//
+// This file is part of the GNU Application Binary Interface Generic
+// Analysis and Instrumentation Library (libabigail).  This library is
+// free software; you can redistribute it and/or modify it under the
+// terms of the GNU Lesser General Public License as published by the
+// Free Software Foundation; either version 2, or (at your option) any
+// later version.
+
+// This library is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Lesser Public License for more details.
+
+// You should have received a copy of the GNU Lesser General Public
+// License along with this program; see the file COPYING-LGPLV3.  If
+// not, see <http://www.gnu.org/licenses/>.
+
+// Author: Matthias Maennich
+
+/// @file
+///
+/// This program tests symtab invariants through abg-corpus.
+
+#include <iostream>
+#include <limits>
+#include <vector>
+
+#include "abg-corpus.h"
+#include "abg-dwarf-reader.h"
+#include "abg-ir.h"
+#include "lib/catch.hpp"
+#include "test-utils.h"
+
+using namespace abigail;
+
+using dwarf_reader::create_read_context;
+using dwarf_reader::read_context_sptr;
+using dwarf_reader::read_corpus_from_elf;
+using ir::environment;
+using ir::environment_sptr;
+
+static const std::string test_data_dir =
+    std::string(abigail::tests::get_src_dir()) + "/tests/data/test-symtab/";
+
+dwarf_reader::status
+read_corpus(const std::string path, corpus_sptr& result)
+{
+  const std::string& absolute_path = test_data_dir + path;
+
+  environment_sptr	    env(new environment);
+  const std::vector<char**> debug_info_root_paths;
+  read_context_sptr	    ctxt =
+      create_read_context(absolute_path, debug_info_root_paths, env.get());
+
+  dwarf_reader::status status = dwarf_reader::STATUS_UNKNOWN;
+  result = read_corpus_from_elf(*ctxt, status);
+
+  REQUIRE(status != dwarf_reader::STATUS_UNKNOWN);
+  return status;
+}
+
+TEST_CASE("Symtab::Empty", "[symtab, basic]")
+{
+  const std::string	     binary = "basic/empty.so";
+  corpus_sptr		     corpus_ptr;
+  const dwarf_reader::status status = read_corpus(binary, corpus_ptr);
+  REQUIRE(corpus_ptr);
+
+  REQUIRE((status & dwarf_reader::STATUS_OK));
+
+  // TODO: Those two assertions are currently not met. Empty symtabs are
+  //       currently treated like the error case.
+  // REQUIRE((status & dwarf_reader::STATUS_OK));
+  // REQUIRE((status & dwarf_reader::STATUS_NO_SYMBOLS_FOUND));
+}
+
+TEST_CASE("Symtab::NoDebugInfo", "[symtab, basic]")
+{
+  const std::string	     binary = "basic/no_debug_info.so";
+  corpus_sptr		     corpus_ptr;
+  const dwarf_reader::status status = read_corpus(binary, corpus_ptr);
+  REQUIRE(corpus_ptr);
+
+  REQUIRE(status
+	  == (dwarf_reader::STATUS_OK
+	      | dwarf_reader::STATUS_DEBUG_INFO_NOT_FOUND));
+}
+
+// this value indicates in the following helper method, that we do not want to
+// assert for this particular value. In other words, N is a placeholder for an
+// arbitrary value.
+#define N std::numeric_limits<size_t>::max()
+
+corpus_sptr
+assert_symbol_count(const std::string& path,
+		    size_t	       function_symbols = 0,
+		    size_t	       variable_symbols = 0,
+		    size_t	       undefined_function_symbols = 0,
+		    size_t	       undefined_variable_symbols = 0)
+{
+  corpus_sptr		     corpus_ptr;
+  const dwarf_reader::status status = read_corpus(path, corpus_ptr);
+  REQUIRE(corpus_ptr);
+
+  REQUIRE(status == dwarf_reader::STATUS_OK);
+  const corpus& corpus = *corpus_ptr;
+
+  if (function_symbols != N)
+    {
+      CHECK(corpus.get_sorted_fun_symbols().size() == function_symbols);
+      CHECK(corpus.get_fun_symbol_map().size() == function_symbols);
+      CHECK(corpus.get_fun_symbol_map_sptr()->size() == function_symbols);
+    }
+  if (variable_symbols != N)
+    {
+      CHECK(corpus.get_sorted_var_symbols().size() == variable_symbols);
+      CHECK(corpus.get_var_symbol_map().size() == variable_symbols);
+      CHECK(corpus.get_var_symbol_map_sptr()->size() == variable_symbols);
+    }
+  if (undefined_variable_symbols != N)
+    {
+      CHECK(corpus.get_sorted_undefined_fun_symbols().size()
+	    == undefined_function_symbols);
+      CHECK(corpus.get_undefined_fun_symbol_map().size()
+	    == undefined_function_symbols);
+      CHECK(corpus.get_undefined_fun_symbol_map_sptr()->size()
+	    == undefined_function_symbols);
+    }
+  if (undefined_function_symbols != N)
+    {
+      CHECK(corpus.get_sorted_undefined_var_symbols().size()
+	    == undefined_variable_symbols);
+      CHECK(corpus.get_undefined_var_symbol_map().size()
+	    == undefined_variable_symbols);
+      CHECK(corpus.get_undefined_var_symbol_map_sptr()->size()
+	    == undefined_variable_symbols);
+    }
+
+  return corpus_ptr;
+}
+
+TEST_CASE("Symtab::SimpleSymtabs", "[symtab, basic]")
+{
+  GIVEN("a binary with no exported symbols")
+  {
+    // TODO: should pass, but does currently not as empty tables are treated
+    //       like the error case, but this is an edge case anyway.
+    // assert_symbol_count("empty.so");
+  }
+
+  GIVEN("a binary with a single exported function")
+  {
+    const std::string	   binary = "basic/single_function.so";
+    const corpus_sptr&	   corpus = assert_symbol_count(binary, 1, 0);
+    const elf_symbol_sptr& symbol =
+	corpus->lookup_function_symbol("exported_function");
+    REQUIRE(symbol);
+    CHECK(!corpus->lookup_variable_symbol("exported_function"));
+    CHECK(symbol == corpus->lookup_function_symbol(*symbol));
+    CHECK(symbol != corpus->lookup_variable_symbol(*symbol));
+  }
+
+  GIVEN("a binary with a single exported variable")
+  {
+    const std::string	   binary = "basic/single_variable.so";
+    const corpus_sptr&	   corpus = assert_symbol_count(binary, 0, 1);
+    const elf_symbol_sptr& symbol =
+	corpus->lookup_variable_symbol("exported_variable");
+    REQUIRE(symbol);
+    CHECK(!corpus->lookup_function_symbol("exported_variable"));
+    CHECK(symbol == corpus->lookup_variable_symbol(*symbol));
+    CHECK(symbol != corpus->lookup_function_symbol(*symbol));
+  }
+
+  GIVEN("a binary with one function and one variable exported")
+  {
+    const std::string  binary = "basic/one_function_one_variable.so";
+    const corpus_sptr& corpus = assert_symbol_count(binary, 1, 1);
+    CHECK(corpus->lookup_function_symbol("exported_function"));
+    CHECK(!corpus->lookup_variable_symbol("exported_function"));
+    CHECK(corpus->lookup_variable_symbol("exported_variable"));
+    CHECK(!corpus->lookup_function_symbol("exported_variable"));
+  }
+
+  GIVEN("a binary with a single undefined function")
+  {
+    const std::string  binary = "basic/single_undefined_function.so";
+    const corpus_sptr& corpus = assert_symbol_count(binary, 0, 0, 1, 0);
+  }
+
+  GIVEN("a binary with a single undefined variable")
+  {
+    const std::string  binary = "basic/single_undefined_variable.so";
+    const corpus_sptr& corpus = assert_symbol_count(binary, 0, 0, 0, 1);
+  }
+
+  GIVEN("a binary with one function and one variable undefined")
+  {
+    const std::string  binary = "basic/one_function_one_variable_undefined.so";
+    const corpus_sptr& corpus = assert_symbol_count(binary, 0, 0, 1, 1);
+  }
+}
