--- /dev/null
+// -*- 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);
+ }
+}