blob: e7a8718603c0dfb8f736ceddae42cbae0a7faa1b [file] [log] [blame]
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- Mode: C++ -*-
//
// Copyright (C) 2021 Oracle, Inc.
//
// Author: Guillermo E. Martinez
/// @file
///
/// This file implement the CTF testsuite. It reads ELF binaries
/// containing CTF, save them in XML corpus files and diff the
/// corpus files against reference XML corpus files.
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <memory>
#include <string>
#include <vector>
#include "abg-ctf-reader.h"
#include "test-read-common.h"
using std::string;
using std::cerr;
using abigail::tests::read_common::InOutSpec;
using abigail::tests::read_common::test_task;
using abigail::tests::read_common::display_usage;
using abigail::tests::read_common::options;
using abigail::ctf_reader::read_context_sptr;
using abigail::ctf_reader::create_read_context;
using abigail::xml_writer::SEQUENCE_TYPE_ID_STYLE;
using abigail::xml_writer::HASH_TYPE_ID_STYLE;
using abigail::tools_utils::emit_prefix;
static InOutSpec in_out_specs[] =
{
{
"data/test-read-ctf/test0",
"",
"",
SEQUENCE_TYPE_ID_STYLE,
"data/test-read-ctf/test0.abi",
"output/test-read-ctf/test0.abi"
},
{
"data/test-read-ctf/test0",
"",
"",
HASH_TYPE_ID_STYLE,
"data/test-read-ctf/test0.hash.abi",
"output/test-read-ctf/test0.hash.abi"
},
{
"data/test-read-ctf/test1.so",
"",
"",
SEQUENCE_TYPE_ID_STYLE,
"data/test-read-ctf/test1.so.abi",
"output/test-read-ctf/test1.so.abi"
},
{
"data/test-read-ctf/test1.so",
"",
"",
HASH_TYPE_ID_STYLE,
"data/test-read-ctf/test1.so.hash.abi",
"output/test-read-ctf/test1.so.hash.abi"
},
{
"data/test-read-ctf/test2.so",
"",
"",
SEQUENCE_TYPE_ID_STYLE,
"data/test-read-ctf/test2.so.abi",
"output/test-read-ctf/test2.so.abi"
},
{
"data/test-read-ctf/test2.so",
"",
"",
HASH_TYPE_ID_STYLE,
"data/test-read-ctf/test2.so.hash.abi",
"output/test-read-ctf/test2.so.hash.abi"
},
{
"data/test-read-common/test3.so",
"",
"",
SEQUENCE_TYPE_ID_STYLE,
"data/test-read-ctf/test3.so.abi",
"output/test-read-ctf/test3.so.abi"
},
{
"data/test-read-common/test3.so",
"",
"",
HASH_TYPE_ID_STYLE,
"data/test-read-ctf/test3.so.hash.abi",
"output/test-read-ctf/test3.so.hash.abi"
},
{
"data/test-read-ctf/test-enum-many.o",
"",
"",
HASH_TYPE_ID_STYLE,
"data/test-read-ctf/test-enum-many.o.hash.abi",
"output/test-read-ctf/test-enum-many.o.hash.abi"
},
{
"data/test-read-ctf/test-ambiguous-struct-A.o",
"",
"",
HASH_TYPE_ID_STYLE,
"data/test-read-ctf/test-ambiguous-struct-A.o.hash.abi",
"output/test-read-ctf/test-ambiguous-struct-A.o.hash.abi"
},
{
"data/test-read-ctf/test-ambiguous-struct-B.o",
"",
"",
HASH_TYPE_ID_STYLE,
"data/test-read-ctf/test-ambiguous-struct-B.o.hash.abi",
"output/test-read-ctf/test-ambiguous-struct-B.o.hash.abi"
},
{
"data/test-read-ctf/test-conflicting-type-syms-a.o",
"",
"",
HASH_TYPE_ID_STYLE,
"data/test-read-ctf/test-conflicting-type-syms-a.o.hash.abi",
"output/test-read-ctf/test-conflicting-type-syms-a.o.hash.abi"
},
{
"data/test-read-ctf/test-conflicting-type-syms-b.o",
"",
"",
HASH_TYPE_ID_STYLE,
"data/test-read-ctf/test-conflicting-type-syms-b.o.hash.abi",
"output/test-read-ctf/test-conflicting-type-syms-b.o.hash.abi"
},
{
"data/test-read-common/test4.so",
"",
"",
SEQUENCE_TYPE_ID_STYLE,
"data/test-read-ctf/test4.so.abi",
"output/test-read-ctf/test4.so.abi"
},
{
"data/test-read-common/test4.so",
"",
"",
HASH_TYPE_ID_STYLE,
"data/test-read-ctf/test4.so.hash.abi",
"output/test-read-ctf/test4.so.hash.abi"
},
{
"data/test-read-ctf/test5.o",
"",
"",
SEQUENCE_TYPE_ID_STYLE,
"data/test-read-ctf/test5.o.abi",
"output/test-read-ctf/test5.o.abi"
},
{
"data/test-read-ctf/test7.o",
"",
"",
SEQUENCE_TYPE_ID_STYLE,
"data/test-read-ctf/test7.o.abi",
"output/test-read-ctf/test7.o.abi"
},
{
"data/test-read-ctf/test8.o",
"",
"",
SEQUENCE_TYPE_ID_STYLE,
"data/test-read-ctf/test8.o.abi",
"output/test-read-ctf/test8.o.abi"
},
{
"data/test-read-ctf/test9.o",
"",
"",
SEQUENCE_TYPE_ID_STYLE,
"data/test-read-ctf/test9.o.abi",
"output/test-read-ctf/test9.o.abi"
},
{
"data/test-read-ctf/test-enum.o",
"",
"",
SEQUENCE_TYPE_ID_STYLE,
"data/test-read-ctf/test-enum.o.abi",
"output/test-read-ctf/test-enum.o.abi"
},
{
"data/test-read-ctf/test-enum-symbol.o",
"",
"",
HASH_TYPE_ID_STYLE,
"data/test-read-ctf/test-enum-symbol.o.hash.abi",
"output/test-read-ctf/test-enum-symbol.o.hash.abi"
},
{
"data/test-read-ctf/test-dynamic-array.o",
"",
"",
SEQUENCE_TYPE_ID_STYLE,
"data/test-read-ctf/test-dynamic-array.o.abi",
"output/test-read-ctf/test-dynamic-array.o.abi"
},
{
"data/test-read-common/PR27700/test-PR27700.o",
"",
"data/test-read-common/PR27700/pub-incdir",
HASH_TYPE_ID_STYLE,
"data/test-read-ctf/PR27700/test-PR27700.abi",
"output/test-read-ctf/PR27700/test-PR27700.abi",
},
{
"data/test-read-ctf/test-callback.o",
"",
"",
SEQUENCE_TYPE_ID_STYLE,
"data/test-read-ctf/test-callback.abi",
"output/test-read-ctf/test-callback.abi",
},
{
"data/test-read-ctf/test-array-of-pointers.o",
"",
"",
SEQUENCE_TYPE_ID_STYLE,
"data/test-read-ctf/test-array-of-pointers.abi",
"output/test-read-ctf/test-array-of-pointers.abi",
},
{
"data/test-read-ctf/test-functions-declaration.o",
"",
"",
SEQUENCE_TYPE_ID_STYLE,
"data/test-read-ctf/test-functions-declaration.abi",
"output/test-read-ctf/test-functions-declaration.abi",
},
{
"data/test-read-ctf/test-forward-type-decl.o",
"",
"",
SEQUENCE_TYPE_ID_STYLE,
"data/test-read-ctf/test-forward-type-decl.abi",
"output/test-read-ctf/test-forward-type-decl.abi",
},
{
"data/test-read-ctf/test-list-struct.o",
"",
"",
SEQUENCE_TYPE_ID_STYLE,
"data/test-read-ctf/test-list-struct.abi",
"output/test-read-ctf/test-list-struct.abi",
},
{
"data/test-read-ctf/test-callback2.o",
"",
"",
SEQUENCE_TYPE_ID_STYLE,
"data/test-read-ctf/test-callback2.abi",
"output/test-read-ctf/test-callback2.abi",
},
// This should be the last entry.
{NULL, NULL, NULL, SEQUENCE_TYPE_ID_STYLE, NULL, NULL}
};
/// Task specialization to perform CTF tests.
struct test_task_ctf : public test_task
{
test_task_ctf(const InOutSpec &s,
string& a_out_abi_base,
string& a_in_elf_base,
string& a_in_abi_base);
virtual void
perform();
virtual
~test_task_ctf()
{}
}; // end struct test_task_ctf
/// Constructor.
///
/// Task to be executed for each CTF test entry in @ref
/// abigail::tests::read_common::InOutSpec.
/// @param InOutSpec the array containing set of tests.
///
/// @param a_out_abi_base the output base directory for abixml files.
///
/// @param a_in_elf_base the input base directory for object files.
///
/// @param a_in_elf_base the input base directory for expected
/// abixml files.
test_task_ctf::test_task_ctf(const InOutSpec &s,
string& a_out_abi_base,
string& a_in_elf_base,
string& a_in_abi_base)
: test_task(s, a_out_abi_base, a_in_elf_base, a_in_abi_base)
{}
/// The thread function to execute each CTF test entry in @ref
/// abigail::tests::read_common::InOutSpec.
///
/// This reads the corpus into memory, saves it to disk, loads it
/// again and compares the new in-memory representation against the
void
test_task_ctf::perform()
{
abigail::ir::environment_sptr env;
set_in_elf_path();
set_in_suppr_spec_path();
env.reset(new abigail::ir::environment);
abigail::elf_reader::status status =
abigail::elf_reader::STATUS_UNKNOWN;
ABG_ASSERT(abigail::tools_utils::file_exists(in_elf_path));
read_context_sptr ctxt = create_read_context(in_elf_path,
env.get());
ABG_ASSERT(ctxt);
corpus_sptr corp = read_corpus(ctxt.get(), status);
// if there is no output and no input, assume that we do not care about the
// actual read result, just that it succeeded.
if (!spec.in_abi_path && !spec.out_abi_path)
{
// Phew! we made it here and we did not crash! yay!
return;
}
if (!corp)
{
error_message = string("failed to read ") + in_elf_path + "\n";
is_ok = false;
return;
}
corp->set_path(spec.in_elf_path);
// Do not take architecture names in comparison so that these
// test input binaries can come from whatever arch the
// programmer likes.
corp->set_architecture_name("");
if (!(is_ok = set_out_abi_path()))
return;
if (!(is_ok = serialize_corpus(out_abi_path, corp)))
return;
if (!(is_ok = run_abidw("--ctf ")))
return;
if (!(is_ok = run_diff()))
return;
}
/// Create a new CTF instance for task to be execute by the testsuite.
///
/// @param s the @ref abigail::tests::read_common::InOutSpec
/// tests container.
///
/// @param a_out_abi_base the output base directory for abixml files.
///
/// @param a_in_elf_base the input base directory for object files.
///
/// @param a_in_abi_base the input base directory for abixml files.
///
/// @return abigail::tests::read_common::test_task instance.
static test_task*
new_task(const InOutSpec* s, string& a_out_abi_base,
string& a_in_elf_base, string& a_in_abi_base)
{
return new test_task_ctf(*s, a_out_abi_base,
a_in_elf_base, a_in_abi_base);
}
int
main(int argc, char *argv[])
{
options opts;
if (!parse_command_line(argc, argv, opts))
{
if (!opts.wrong_option.empty())
emit_prefix(argv[0], cerr)
<< "unrecognized option: " << opts.wrong_option << "\n";
display_usage(argv[0], cerr);
return 1;
}
// compute number of tests to be executed.
const size_t num_tests = sizeof(in_out_specs) / sizeof(InOutSpec) - 1;
return run_tests(num_tests, in_out_specs, opts, new_task);
}