libabigail/tests/test-symtab.cc
Matthias Maennich 7851ca2b91 abg-corpus: remove symbol maps and their setters
With the prework in previous commits, we are now able to drop the
public symbols maps in corpus::priv and replace them by private members
with access through getters. The getters use the new symtab
implementation to generate the maps on the fly. Setters are not required
anymore and are removed. Also remove redundant getters.

We could also remove the getters for the symbol maps and the local
caching variable and leave it all to lookup_symbol, but this is left for
a later change.

	* include/abg-corpus.h (corpus::set_fun_symbol_map): Remove
	method declaration.
	(corpus::set_undefined_fun_symbol_map): Likewise.
	(corpus::set_var_symbol_map): Likewise.
	(corpus::set_undefined_var_symbol_map): Likewise.
	(corpus::get_fun_symbol_map_sptr): Likewise.
	(corpus::get_undefined_fun_symbol_map_sptr): Likewise.
	(corpus::get_var_symbol_map_sptr): Likewise.
	(corpus::get_undefined_var_symbol_map_sptr): Likewise.
	* src/abg-corpus-priv.h (corpus::priv::var_symbol_map): make
	private and mutable
	(corpus::priv::undefined_var_symbol_map): Likewise.
	(corpus::priv::fun_symbol_map): Likewise.
	(corpus::priv::undefined_fun_symbol_map): Likewise.
	(corpus::priv::get_fun_symbol_map): New method declaration.
	(corpus::priv::get_undefined_fun_symbol_map): Likewise.
	(corpus::priv::get_var_symbol_map): Likewise.
	(corpus::priv::get_undefined_var_symbol_map): Likewise.
	* src/abg-corpus.cc (corpus::priv::get_fun_symbol_map): New
	method implementation.
	(corpus::priv::get_undefined_fun_symbol_map): Likewise.
	(corpus::priv::get_var_symbol_map): Likewise.
	(corpus::priv::get_undefined_var_symbol_map): Likewise.
	(corpus::is_empty): depend on symtab only.
	(corpus::set_fun_symbol_map): Remove method.
	(corpus::set_undefined_fun_symbol_map): Likewise.
	(corpus::set_var_symbol_map): Likewise.
	(corpus::set_undefined_var_symbol_map): Likewise.
	(corpus::get_fun_symbol_map_sptr): Likewise.
	(corpus::get_undefined_fun_symbol_map_sptr): Likewise.
	(corpus::get_var_symbol_map_sptr): Likewise.
	(corpus::get_undefined_var_symbol_map_sptr): Likewise.
	(corpus::get_fun_symbol_map): Use corpus::priv proxy method.
	(corpus::get_undefined_fun_symbol_map): Likewise.
	(corpus::get_var_symbol_map): Likewise.
	(corpus::get_undefined_var_symbol_map): Likewise.
	* src/abg-dwarf-reader.cc (read_debug_info_into_corpus): Do not
	set corpus symbol maps anymore.
	* src/abg-reader.cc (read_corpus_from_input): Likewise.
	* tests/test-symtab.cc (assert_symbol_count): Do not access the
	corpus symbol maps through sptr anymore.
	* tests/data/test-read-dwarf/PR25007-sdhci.ko.abi: Adjust
	expected test output.

Reviewed-by: Giuliano Procida <gprocida@google.com>
Reviewed-by: Dodji Seketeli <dodji@seketeli.org>
Signed-off-by: Matthias Maennich <maennich@google.com>
Signed-off-by: Dodji Seketeli <dodji@redhat.com>
2021-04-02 15:48:01 +02:00

277 lines
9.5 KiB
C++

// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- Mode: C++ -*-
//
// Copyright (C) 2020 Google, Inc.
//
// 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(),
/* load_all_type = */ true, /* linux_kernel_mode = */ true);
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_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;
size_t total_symbols = 0;
if (function_symbols != N)
{
CHECK(corpus.get_sorted_fun_symbols().size() == function_symbols);
CHECK(corpus.get_fun_symbol_map().size() == function_symbols);
total_symbols += function_symbols;
}
if (variable_symbols != N)
{
CHECK(corpus.get_sorted_var_symbols().size() == variable_symbols);
CHECK(corpus.get_var_symbol_map().size() == variable_symbols);
total_symbols += 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);
total_symbols += 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);
total_symbols += undefined_variable_symbols;
}
// assert the corpus reports being empty consistently with the symbol count
CHECK(corpus.is_empty() == (total_symbols == 0));
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);
}
}
static const char* kernel_versions[] = { "4.14", "4.19", "5.4", "5.6" };
static const size_t nr_kernel_versions =
sizeof(kernel_versions) / sizeof(kernel_versions[0]);
TEST_CASE("Symtab::SimpleKernelSymtabs", "[symtab, basic, kernel, ksymtab]")
{
for (size_t i = 0; i < nr_kernel_versions; ++i)
{
const std::string base_path =
"kernel-" + std::string(kernel_versions[i]) + "/";
GIVEN("The binaries in " + base_path)
{
GIVEN("a kernel module 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(base_path + "empty.so");
}
GIVEN("a kernel module with a single exported function")
{
const std::string binary = base_path + "single_function.ko";
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 kernel module with a single GPL exported function")
{
const std::string binary = base_path + "single_function_gpl.ko";
const corpus_sptr& corpus = assert_symbol_count(binary, 1, 0);
const elf_symbol_sptr& symbol =
corpus->lookup_function_symbol("exported_function_gpl");
REQUIRE(symbol);
CHECK(!corpus->lookup_variable_symbol("exported_function_gpl"));
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 = base_path + "single_variable.ko";
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 a single GPL exported variable")
{
const std::string binary = base_path + "single_variable_gpl.ko";
const corpus_sptr& corpus = assert_symbol_count(binary, 0, 1);
const elf_symbol_sptr& symbol =
corpus->lookup_variable_symbol("exported_variable_gpl");
REQUIRE(symbol);
CHECK(!corpus->lookup_function_symbol("exported_variable_gpl"));
CHECK(symbol == corpus->lookup_variable_symbol(*symbol));
CHECK(symbol != corpus->lookup_function_symbol(*symbol));
}
GIVEN("a binary with one function and one variable (GPL) exported")
{
const std::string binary = base_path + "one_of_each.ko";
const corpus_sptr& corpus = assert_symbol_count(binary, 2, 2);
CHECK(corpus->lookup_function_symbol("exported_function"));
CHECK(!corpus->lookup_variable_symbol("exported_function"));
CHECK(corpus->lookup_function_symbol("exported_function_gpl"));
CHECK(!corpus->lookup_variable_symbol("exported_function_gpl"));
CHECK(corpus->lookup_variable_symbol("exported_variable"));
CHECK(!corpus->lookup_function_symbol("exported_variable"));
CHECK(corpus->lookup_variable_symbol("exported_variable_gpl"));
CHECK(!corpus->lookup_function_symbol("exported_variable_gpl"));
}
}
}
}