// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // -*- Mode: C++ -*- // // Copyright (C) 2013-2020 Red Hat, Inc. // // Author: Dodji Seketeli /// @file read ELF binaries containing DWARF, save them in XML corpus /// files and diff the corpus files against reference XML corpus /// files. #include #include #include #include #include #include #include "abg-ir.h" #include "abg-dwarf-reader.h" #include "abg-workers.h" #include "abg-writer.h" #include "abg-tools-utils.h" #include "test-utils.h" using std::vector; using std::string; using std::ofstream; using std::cerr; using std::dynamic_pointer_cast; using abigail::tests::get_build_dir; using abigail::dwarf_reader::read_corpus_from_elf; using abigail::dwarf_reader::read_context; using abigail::dwarf_reader::read_context_sptr; using abigail::dwarf_reader::create_read_context; using abigail::xml_writer::SEQUENCE_TYPE_ID_STYLE; using abigail::xml_writer::HASH_TYPE_ID_STYLE; using abigail::xml_writer::create_write_context; using abigail::xml_writer::set_type_id_style; using abigail::xml_writer::type_id_style_kind; using abigail::xml_writer::write_context_sptr; using abigail::xml_writer::write_corpus; /// This is an aggregate that specifies where a test shall get its /// input from, and where it shall write its ouput to. struct InOutSpec { const char* in_elf_path; const char* in_suppr_spec_path; type_id_style_kind type_id_style; const char* in_abi_path; const char* out_abi_path; };// end struct InOutSpec InOutSpec in_out_specs[] = { { "data/test-read-dwarf/test0", "", SEQUENCE_TYPE_ID_STYLE, "data/test-read-dwarf/test0.abi", "output/test-read-dwarf/test0.abi" }, { "data/test-read-dwarf/test0", "", HASH_TYPE_ID_STYLE, "data/test-read-dwarf/test0.hash.abi", "output/test-read-dwarf/test0.hash.abi" }, { "data/test-read-dwarf/test1", "", SEQUENCE_TYPE_ID_STYLE, "data/test-read-dwarf/test1.abi", "output/test-read-dwarf/test1.abi" }, { "data/test-read-dwarf/test1", "", HASH_TYPE_ID_STYLE, "data/test-read-dwarf/test1.hash.abi", "output/test-read-dwarf/test1.hash.abi" }, { "data/test-read-dwarf/test2.so", "", SEQUENCE_TYPE_ID_STYLE, "data/test-read-dwarf/test2.so.abi", "output/test-read-dwarf/test2.so.abi" }, { "data/test-read-dwarf/test2.so", "", HASH_TYPE_ID_STYLE, "data/test-read-dwarf/test2.so.hash.abi", "output/test-read-dwarf/test2.so.hash.abi" }, { "data/test-read-dwarf/test3.so", "", SEQUENCE_TYPE_ID_STYLE, "data/test-read-dwarf/test3.so.abi", "output/test-read-dwarf/test3.so.abi" }, { "data/test-read-dwarf/test3.so", "", HASH_TYPE_ID_STYLE, "data/test-read-dwarf/test3.so.hash.abi", "output/test-read-dwarf/test3.so.hash.abi" }, // suppress all except the main symbol of a group of aliases { "data/test-read-dwarf/test3.so", "data/test-read-dwarf/test3-alias-1.suppr", HASH_TYPE_ID_STYLE, "data/test-read-dwarf/test3-alias-1.so.hash.abi", "output/test-read-dwarf/test3-alias-1.so.hash.abi" }, // suppress the main symbol of a group of aliases { "data/test-read-dwarf/test3.so", "data/test-read-dwarf/test3-alias-2.suppr", HASH_TYPE_ID_STYLE, "data/test-read-dwarf/test3-alias-2.so.hash.abi", "output/test-read-dwarf/test3-alias-2.so.hash.abi" }, // suppress all except one non main symbol of a group of aliases { "data/test-read-dwarf/test3.so", "data/test-read-dwarf/test3-alias-3.suppr", HASH_TYPE_ID_STYLE, "data/test-read-dwarf/test3-alias-3.so.hash.abi", "output/test-read-dwarf/test3-alias-3.so.hash.abi" }, // suppress all symbols of a group of aliases { "data/test-read-dwarf/test3.so", "data/test-read-dwarf/test3-alias-4.suppr", HASH_TYPE_ID_STYLE, "data/test-read-dwarf/test3-alias-4.so.hash.abi", "output/test-read-dwarf/test3-alias-4.so.hash.abi" }, // suppress the main symbols with alias (function+variable) in .o file { "data/test-read-dwarf/test-suppressed-alias.o", "data/test-read-dwarf/test-suppressed-alias.suppr", HASH_TYPE_ID_STYLE, "data/test-read-dwarf/test-suppressed-alias.o.abi", "output/test-read-dwarf/test-suppressed-alias.o.abi", }, { "data/test-read-dwarf/test4.so", "", SEQUENCE_TYPE_ID_STYLE, "data/test-read-dwarf/test4.so.abi", "output/test-read-dwarf/test4.so.abi" }, { "data/test-read-dwarf/test4.so", "", HASH_TYPE_ID_STYLE, "data/test-read-dwarf/test4.so.hash.abi", "output/test-read-dwarf/test4.so.hash.abi" }, { "data/test-read-dwarf/test5.o", "", SEQUENCE_TYPE_ID_STYLE, "data/test-read-dwarf/test5.o.abi", "output/test-read-dwarf/test5.o.abi" }, { "data/test-read-dwarf/test5.o", "", HASH_TYPE_ID_STYLE, "data/test-read-dwarf/test5.o.hash.abi", "output/test-read-dwarf/test5.o.hash.abi" }, { "data/test-read-dwarf/test6.so", "", SEQUENCE_TYPE_ID_STYLE, "data/test-read-dwarf/test6.so.abi", "output/test-read-dwarf/test6.so.abi" }, { "data/test-read-dwarf/test6.so", "", HASH_TYPE_ID_STYLE, "data/test-read-dwarf/test6.so.hash.abi", "output/test-read-dwarf/test6.so.hash.abi" }, { "data/test-read-dwarf/test7.so", "", SEQUENCE_TYPE_ID_STYLE, "data/test-read-dwarf/test7.so.abi", "output/test-read-dwarf/test7.so.abi" }, { "data/test-read-dwarf/test7.so", "", HASH_TYPE_ID_STYLE, "data/test-read-dwarf/test7.so.hash.abi", "output/test-read-dwarf/test7.so.hash.abi" }, { "data/test-read-dwarf/test8-qualified-this-pointer.so", "", SEQUENCE_TYPE_ID_STYLE, "data/test-read-dwarf/test8-qualified-this-pointer.so.abi", "output/test-read-dwarf/test8-qualified-this-pointer.so.abi" }, { "data/test-read-dwarf/test8-qualified-this-pointer.so", "", HASH_TYPE_ID_STYLE, "data/test-read-dwarf/test8-qualified-this-pointer.so.hash.abi", "output/test-read-dwarf/test8-qualified-this-pointer.so.hash.abi" }, { "data/test-read-dwarf/test9-pr18818-clang.so", "", SEQUENCE_TYPE_ID_STYLE, "data/test-read-dwarf/test9-pr18818-clang.so.abi", "output/test-read-dwarf/test9-pr18818-clang.so.abi" }, { "data/test-read-dwarf/test10-pr18818-gcc.so", "", SEQUENCE_TYPE_ID_STYLE, "data/test-read-dwarf/test10-pr18818-gcc.so.abi", "output/test-read-dwarf/test10-pr18818-gcc.so.abi" }, { "data/test-read-dwarf/test11-pr18828.so", "", SEQUENCE_TYPE_ID_STYLE, "data/test-read-dwarf/test11-pr18828.so.abi", "output/test-read-dwarf/test11-pr18828.so.abi", }, { "data/test-read-dwarf/test12-pr18844.so", "", SEQUENCE_TYPE_ID_STYLE, "data/test-read-dwarf/test12-pr18844.so.abi", "output/test-read-dwarf/test12-pr18844.so.abi", }, { "data/test-read-dwarf/test13-pr18894.so", "", SEQUENCE_TYPE_ID_STYLE, "data/test-read-dwarf/test13-pr18894.so.abi", "output/test-read-dwarf/test13-pr18894.so.abi", }, { "data/test-read-dwarf/test14-pr18893.so", "", SEQUENCE_TYPE_ID_STYLE, "data/test-read-dwarf/test14-pr18893.so.abi", "output/test-read-dwarf/test14-pr18893.so.abi", }, { "data/test-read-dwarf/test15-pr18892.so", "", SEQUENCE_TYPE_ID_STYLE, "data/test-read-dwarf/test15-pr18892.so.abi", "output/test-read-dwarf/test15-pr18892.so.abi", }, { "data/test-read-dwarf/test16-pr18904.so", "", SEQUENCE_TYPE_ID_STYLE, "data/test-read-dwarf/test16-pr18904.so.abi", "output/test-read-dwarf/test16-pr18904.so.abi", }, { "data/test-read-dwarf/test17-pr19027.so", "", SEQUENCE_TYPE_ID_STYLE, "data/test-read-dwarf/test17-pr19027.so.abi", "output/test-read-dwarf/test17-pr19027.so.abi", }, { "data/test-read-dwarf/test18-pr19037-libvtkRenderingLIC-6.1.so", "", SEQUENCE_TYPE_ID_STYLE, "data/test-read-dwarf/test18-pr19037-libvtkRenderingLIC-6.1.so.abi", "output/test-read-dwarf/test18-pr19037-libvtkRenderingLIC-6.1.so.abi", }, { "data/test-read-dwarf/test19-pr19023-libtcmalloc_and_profiler.so", "", SEQUENCE_TYPE_ID_STYLE, "data/test-read-dwarf/test19-pr19023-libtcmalloc_and_profiler.so.abi", "output/test-read-dwarf/test19-pr19023-libtcmalloc_and_profiler.so.abi", }, { "data/test-read-dwarf/test20-pr19025-libvtkParallelCore-6.1.so", "", SEQUENCE_TYPE_ID_STYLE, "data/test-read-dwarf/test20-pr19025-libvtkParallelCore-6.1.so.abi", "output/test-read-dwarf/test20-pr19025-libvtkParallelCore-6.1.so.abi", }, { "data/test-read-dwarf/test21-pr19092.so", "", SEQUENCE_TYPE_ID_STYLE, "data/test-read-dwarf/test21-pr19092.so.abi", "output/test-read-dwarf/test21-pr19092.so.abi", }, { "data/test-read-dwarf/test22-pr19097-libstdc++.so.6.0.17.so", "", SEQUENCE_TYPE_ID_STYLE, "data/test-read-dwarf/test22-pr19097-libstdc++.so.6.0.17.so.abi", "output/test-read-dwarf/test22-pr19097-libstdc++.so.6.0.17.so.abi", }, { "data/test-read-dwarf/libtest23.so", "", SEQUENCE_TYPE_ID_STYLE, "data/test-read-dwarf/libtest23.so.abi", "output/test-read-dwarf/libtest23.so.abi", }, { "data/test-read-dwarf/libtest24-drop-fns.so", "data/test-read-dwarf/test24-drop-fns-0.suppr", SEQUENCE_TYPE_ID_STYLE, "data/test-read-dwarf/libtest24-drop-fns.so.abi", "output/test-read-dwarf/libtest24-drop-fns.so.abi", }, { "data/test-read-dwarf/libtest24-drop-fns.so", "", SEQUENCE_TYPE_ID_STYLE, "data/test-read-dwarf/libtest24-drop-fns-2.so.abi", "output/test-read-dwarf/libtest24-drop-fns-2.so.abi", }, { "data/test-read-dwarf/PR22015-libboost_iostreams.so", "", SEQUENCE_TYPE_ID_STYLE, "data/test-read-dwarf/PR22015-libboost_iostreams.so.abi", "output/test-read-dwarf/PR22015-libboost_iostreams.so.abi", }, { "data/test-read-dwarf/PR22122-libftdc.so", "", SEQUENCE_TYPE_ID_STYLE, "data/test-read-dwarf/PR22122-libftdc.so.abi", "output/test-read-dwarf/PR22122-libftdc.so.abi", }, { "data/test-read-dwarf/PR24378-fn-is-not-scope.o", "", SEQUENCE_TYPE_ID_STYLE, "data/test-read-dwarf/PR24378-fn-is-not-scope.abi", "output/test-read-dwarf/PR24378-fn-is-not-scope.abi", }, #if defined(HAVE_R_AARCH64_ABS64_MACRO) && defined(HAVE_R_AARCH64_PREL32_MACRO) { "data/test-read-dwarf/PR25007-sdhci.ko", "", SEQUENCE_TYPE_ID_STYLE, "data/test-read-dwarf/PR25007-sdhci.ko.abi", "output/test-read-dwarf/PR25007-sdhci.ko.abi", }, #endif #if defined HAVE_DW_FORM_strx { "data/test-read-dwarf/PR25042-libgdbm-clang-dwarf5.so.6.0.0", "", SEQUENCE_TYPE_ID_STYLE, "data/test-read-dwarf/PR25042-libgdbm-clang-dwarf5.so.6.0.0.abi", "output/test-read-dwarf/PR25042-libgdbm-clang-dwarf5.so.6.0.0.abi", }, #endif { "data/test-read-dwarf/test25-bogus-binary.elf", "", SEQUENCE_TYPE_ID_STYLE, NULL, NULL, }, { "data/test-read-dwarf/test26-bogus-binary.elf", "", SEQUENCE_TYPE_ID_STYLE, NULL, NULL, }, { "data/test-read-dwarf/test27-bogus-binary.elf", "", SEQUENCE_TYPE_ID_STYLE, NULL, NULL, }, { "data/test-read-dwarf/PR26261/PR26261-exe", "", SEQUENCE_TYPE_ID_STYLE, "data/test-read-dwarf/PR26261/PR26261-exe.abi", "output/test-read-dwarf/PR26261/PR26261-exe.abi", }, { "data/test-read-dwarf/test-PR26568-1.o", "", SEQUENCE_TYPE_ID_STYLE, "data/test-read-dwarf/test-PR26568-1.o.abi", "output/test-read-dwarf/test-PR26568-1.o.abi", }, { "data/test-read-dwarf/test-PR26568-2.o", "", SEQUENCE_TYPE_ID_STYLE, "data/test-read-dwarf/test-PR26568-2.o.abi", "output/test-read-dwarf/test-PR26568-2.o.abi", }, { "data/test-read-dwarf/test-libandroid.so", "", HASH_TYPE_ID_STYLE, "data/test-read-dwarf/test-libandroid.so.abi", "output/test-read-dwarf/test-libandroid.so.abi", }, // This should be the last entry. {NULL, NULL, SEQUENCE_TYPE_ID_STYLE, NULL, NULL} }; using abigail::suppr::suppression_sptr; using abigail::suppr::suppressions_type; using abigail::suppr::read_suppressions; /// Set the suppression specification to use when reading the ELF binary. /// /// @param read_ctxt the context used to read the ELF binary. /// /// @param path the path to the suppression specification to read. static void set_suppressions(read_context& read_ctxt, const string& path) { suppressions_type supprs; read_suppressions(path, supprs); add_read_context_suppressions(read_ctxt, supprs); } /// The task that peforms the tests. struct test_task : public abigail::workers::task { bool is_ok; InOutSpec spec; string error_message; string out_abi_base; string in_elf_base; string in_abi_base; test_task(const InOutSpec &s, string& a_out_abi_base, string& a_in_elf_base, string& a_in_abi_base) : is_ok(true), spec(s), out_abi_base(a_out_abi_base), in_elf_base(a_in_elf_base), in_abi_base(a_in_abi_base) {} /// The actual test. /// /// This reads the corpus into memory, saves it to disk, loads it /// again and compares the new in-memory representation against the /// saved one. virtual void perform() { string in_elf_path, in_abi_path, in_suppr_spec_path, out_abi_path; abigail::ir::environment_sptr env; in_elf_path = in_elf_base + spec.in_elf_path; if (spec.in_suppr_spec_path) in_suppr_spec_path = in_elf_base + spec.in_suppr_spec_path; else in_suppr_spec_path.clear(); env.reset(new abigail::ir::environment); abigail::dwarf_reader::status status = abigail::dwarf_reader::STATUS_UNKNOWN; vector di_roots; ABG_ASSERT(abigail::tools_utils::file_exists(in_elf_path)); read_context_sptr ctxt = create_read_context(in_elf_path, di_roots, env.get()); ABG_ASSERT(ctxt); if (!in_suppr_spec_path.empty()) set_suppressions(*ctxt, in_suppr_spec_path); abigail::corpus_sptr corp = read_corpus_from_elf(*ctxt, 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(""); out_abi_path = out_abi_base + spec.out_abi_path; if (!abigail::tools_utils::ensure_parent_dir_created(out_abi_path)) { error_message = string("Could not create parent directory for ") + out_abi_path; is_ok = false; return; } ofstream of(out_abi_path.c_str(), std::ios_base::trunc); if (!of.is_open()) { error_message = string("failed to read ") + out_abi_path + "\n"; is_ok = false; return; } write_context_sptr write_ctxt = create_write_context(corp->get_environment(), of); set_type_id_style(*write_ctxt, spec.type_id_style); is_ok = write_corpus(*write_ctxt, corp, /*indent=*/0); of.close(); string abidw = string(get_build_dir()) + "/tools/abidw"; string cmd = abidw + " --abidiff " + in_elf_path; if (system(cmd.c_str())) { error_message = string("ABIs differ:\n") + in_elf_path + "\nand:\n" + out_abi_path + "\n"; is_ok = false; } in_abi_path = in_abi_base + spec.in_abi_path; cmd = "diff -u " + in_abi_path + " " + out_abi_path; if (system(cmd.c_str())) is_ok = false; } }; // end struct test_task typedef shared_ptr test_task_sptr; int main(int argc, char *argv[]) { bool no_parallel = false; if (argc == 2) { if (argv[1] == string("--no-parallel")) no_parallel = true; else { cerr << "unrecognized option\n"; cerr << "usage: " << argv[0] << " [--no-parallel]\n" ; return 1; } } /// Create a task queue. The max number of worker threads of the /// queue is the number of the concurrent threads supported by the /// processor of the machine this code runs on. But if /// --no-parallel was provided then the number of worker threads /// equals 1. const size_t num_tests = sizeof(in_out_specs) / sizeof (InOutSpec) - 1; size_t num_workers = (no_parallel ? 1 : std::min(abigail::workers::get_number_of_threads(), num_tests)); abigail::workers::queue task_queue(num_workers); bool is_ok = true; string out_abi_base = string(get_build_dir()) + "/tests/"; string in_elf_base = string(abigail::tests::get_src_dir()) + "/tests/"; string in_abi_base = in_elf_base; for (InOutSpec *s = in_out_specs; s->in_elf_path; ++s) { test_task_sptr t(new test_task(*s, out_abi_base, in_elf_base, in_abi_base)); ABG_ASSERT(task_queue.schedule_task(t)); } /// Wait for all worker threads to finish their job, and wind down. task_queue.wait_for_workers_to_complete(); // Now walk the results and print whatever error messages need to be // printed. const vector& completed_tasks = task_queue.get_completed_tasks(); ABG_ASSERT(completed_tasks.size() == num_tests); for (vector::const_iterator ti = completed_tasks.begin(); ti != completed_tasks.end(); ++ti) { test_task_sptr t = dynamic_pointer_cast(*ti); if (!t->is_ok) { is_ok = false; if (!t->error_message.empty()) cerr << t->error_message << '\n'; } } return !is_ok; }