1 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
2 // -*- Mode: C++ -*-
3 //
4 // Copyright (C) 2022-2023 Red Hat, Inc.
5 //
6 // Author: Dodji Seketeli
7
8 /// @file
9 ///
10 /// This file is part of the BTF testsuite. It reads ELF binaries
11 /// containing BTF, save them in XML corpus files and diff the
12 /// corpus files against reference XML corpus files.
13
14 #include <cstdlib>
15 #include <fstream>
16 #include <iostream>
17 #include <memory>
18 #include <string>
19 #include <vector>
20 #include "abg-btf-reader.h"
21 #include "test-read-common.h"
22
23 using std::string;
24 using std::cerr;
25 using std::vector;
26
27 using abigail::tests::read_common::InOutSpec;
28 using abigail::tests::read_common::test_task;
29 using abigail::tests::read_common::display_usage;
30 using abigail::tests::read_common::options;
31
32 using abigail::btf::create_reader;
33 using abigail::xml_writer::SEQUENCE_TYPE_ID_STYLE;
34 using abigail::xml_writer::HASH_TYPE_ID_STYLE;
35 using abigail::tools_utils::emit_prefix;
36
37 static InOutSpec in_out_specs[] =
38 {
39 {
40 "data/test-read-btf/test0.o",
41 "",
42 "",
43 SEQUENCE_TYPE_ID_STYLE,
44 "data/test-read-btf/test0.o.abi",
45 "output/test-read-btf/test0.o.abi",
46 "--btf",
47 },
48 {
49 "data/test-read-btf/test1.o",
50 "",
51 "",
52 SEQUENCE_TYPE_ID_STYLE,
53 "data/test-read-btf/test1.o.abi",
54 "output/test-read-btf/test1.o.abi",
55 "--btf",
56 },
57 // This should be the last entry.
58 {NULL, NULL, NULL, SEQUENCE_TYPE_ID_STYLE, NULL, NULL, NULL}
59 };
60
61 /// Task specialization to perform BTF tests.
62 struct test_task_btf : public test_task
63 {
64 test_task_btf(const InOutSpec &s,
65 string& a_out_abi_base,
66 string& a_in_elf_base,
67 string& a_in_abi_base);
68 virtual void
69 perform();
70
71 virtual
~test_task_btftest_task_btf72 ~test_task_btf()
73 {}
74 }; // end struct test_task_btf
75
76 /// Constructor.
77 ///
78 /// Task to be executed for each BTF test entry in @ref
79 /// abigail::tests::read_common::InOutSpec.
80 /// @param InOutSpec the array containing set of tests.
81 ///
82 /// @param a_out_abi_base the output base directory for abixml files.
83 ///
84 /// @param a_in_elf_base the input base directory for object files.
85 ///
86 /// @param a_in_elf_base the input base directory for expected
87 /// abixml files.
test_task_btf(const InOutSpec & s,string & a_out_abi_base,string & a_in_elf_base,string & a_in_abi_base)88 test_task_btf::test_task_btf(const InOutSpec &s,
89 string& a_out_abi_base,
90 string& a_in_elf_base,
91 string& a_in_abi_base)
92 : test_task(s, a_out_abi_base, a_in_elf_base, a_in_abi_base)
93 {}
94
95 /// The thread function to execute each BTF test entry in @ref
96 /// abigail::tests::read_common::InOutSpec.
97 ///
98 /// This reads the corpus into memory, saves it to disk, loads it
99 /// again and compares the new in-memory representation against the
100 void
perform()101 test_task_btf::perform()
102 {
103 abigail::ir::environment env;
104
105 set_in_elf_path();
106 set_in_suppr_spec_path();
107
108 abigail::fe_iface::status status =
109 abigail::fe_iface::STATUS_UNKNOWN;
110 vector<char**> di_roots;
111 ABG_ASSERT(abigail::tools_utils::file_exists(in_elf_path));
112
113 abigail::elf_based_reader_sptr rdr = abigail::btf::create_reader(in_elf_path,
114 di_roots, env);
115 ABG_ASSERT(rdr);
116
117 corpus_sptr corp = rdr->read_corpus(status);
118
119 // if there is no output and no input, assume that we do not care about the
120 // actual read result, just that it succeeded.
121 if (!spec.in_abi_path && !spec.out_abi_path)
122 {
123 // Phew! we made it here and we did not crash! yay!
124 return;
125 }
126 if (!corp)
127 {
128 error_message = string("failed to read ") + in_elf_path + "\n";
129 is_ok = false;
130 return;
131 }
132 corp->set_path(spec.in_elf_path);
133 // Do not take architecture names in comparison so that these
134 // test input binaries can come from whatever arch the
135 // programmer likes.
136 corp->set_architecture_name("");
137
138 if (!(is_ok = set_out_abi_path()))
139 return;
140
141 if (!(is_ok = serialize_corpus(out_abi_path, corp)))
142 return;
143
144 if (!(is_ok = run_abidw("--btf ")))
145 return;
146
147 if (!(is_ok = run_diff()))
148 return;
149 }
150
151 /// Create a new BTF instance for task to be execute by the testsuite.
152 ///
153 /// @param s the @ref abigail::tests::read_common::InOutSpec
154 /// tests container.
155 ///
156 /// @param a_out_abi_base the output base directory for abixml files.
157 ///
158 /// @param a_in_elf_base the input base directory for object files.
159 ///
160 /// @param a_in_abi_base the input base directory for abixml files.
161 ///
162 /// @return abigail::tests::read_common::test_task instance.
163 static test_task*
new_task(const InOutSpec * s,string & a_out_abi_base,string & a_in_elf_base,string & a_in_abi_base)164 new_task(const InOutSpec* s, string& a_out_abi_base,
165 string& a_in_elf_base, string& a_in_abi_base)
166 {
167 return new test_task_btf(*s, a_out_abi_base,
168 a_in_elf_base, a_in_abi_base);
169 }
170
171 int
main(int argc,char * argv[])172 main(int argc, char *argv[])
173 {
174 options opts;
175 if (!parse_command_line(argc, argv, opts))
176 {
177 if (!opts.wrong_option.empty())
178 emit_prefix(argv[0], cerr)
179 << "unrecognized option: " << opts.wrong_option << "\n";
180 display_usage(argv[0], cerr);
181 return 1;
182 }
183
184 // compute number of tests to be executed.
185 const size_t num_tests = sizeof(in_out_specs) / sizeof(InOutSpec) - 1;
186
187 return run_tests(num_tests, in_out_specs, opts, new_task);
188 }
189