1 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
2 // -*- Mode: C++ -*-
3 //
4 // Copyright (C) 2021 Oracle, Inc.
5 //
6 // Author: Guillermo E. Martinez
7
8 /// @file
9 ///
10 /// This file implement the CTF testsuite. It reads ELF binaries
11 /// containing CTF, 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-ctf-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::ctf_reader::read_context_sptr;
33 using abigail::ctf_reader::create_read_context;
34 using abigail::xml_writer::SEQUENCE_TYPE_ID_STYLE;
35 using abigail::xml_writer::HASH_TYPE_ID_STYLE;
36 using abigail::tools_utils::emit_prefix;
37
38 static InOutSpec in_out_specs[] =
39 {
40 {
41 "data/test-read-ctf/test0",
42 "",
43 "",
44 SEQUENCE_TYPE_ID_STYLE,
45 "data/test-read-ctf/test0.abi",
46 "output/test-read-ctf/test0.abi"
47 },
48 {
49 "data/test-read-ctf/test0",
50 "",
51 "",
52 HASH_TYPE_ID_STYLE,
53 "data/test-read-ctf/test0.hash.abi",
54 "output/test-read-ctf/test0.hash.abi"
55 },
56 {
57 "data/test-read-ctf/test1.so",
58 "",
59 "",
60 SEQUENCE_TYPE_ID_STYLE,
61 "data/test-read-ctf/test1.so.abi",
62 "output/test-read-ctf/test1.so.abi"
63 },
64 {
65 "data/test-read-ctf/test1.so",
66 "",
67 "",
68 HASH_TYPE_ID_STYLE,
69 "data/test-read-ctf/test1.so.hash.abi",
70 "output/test-read-ctf/test1.so.hash.abi"
71 },
72 {
73 "data/test-read-ctf/test2.so",
74 "",
75 "",
76 SEQUENCE_TYPE_ID_STYLE,
77 "data/test-read-ctf/test2.so.abi",
78 "output/test-read-ctf/test2.so.abi"
79 },
80 {
81 "data/test-read-ctf/test2.so",
82 "",
83 "",
84 HASH_TYPE_ID_STYLE,
85 "data/test-read-ctf/test2.so.hash.abi",
86 "output/test-read-ctf/test2.so.hash.abi"
87 },
88 {
89 "data/test-read-common/test3.so",
90 "",
91 "",
92 SEQUENCE_TYPE_ID_STYLE,
93 "data/test-read-ctf/test3.so.abi",
94 "output/test-read-ctf/test3.so.abi"
95 },
96 {
97 "data/test-read-common/test3.so",
98 "",
99 "",
100 HASH_TYPE_ID_STYLE,
101 "data/test-read-ctf/test3.so.hash.abi",
102 "output/test-read-ctf/test3.so.hash.abi"
103 },
104 {
105 "data/test-read-ctf/test-enum-many.o",
106 "",
107 "",
108 HASH_TYPE_ID_STYLE,
109 "data/test-read-ctf/test-enum-many.o.hash.abi",
110 "output/test-read-ctf/test-enum-many.o.hash.abi"
111 },
112 {
113 "data/test-read-ctf/test-ambiguous-struct-A.o",
114 "",
115 "",
116 HASH_TYPE_ID_STYLE,
117 "data/test-read-ctf/test-ambiguous-struct-A.o.hash.abi",
118 "output/test-read-ctf/test-ambiguous-struct-A.o.hash.abi"
119 },
120 {
121 "data/test-read-ctf/test-ambiguous-struct-B.o",
122 "",
123 "",
124 HASH_TYPE_ID_STYLE,
125 "data/test-read-ctf/test-ambiguous-struct-B.o.hash.abi",
126 "output/test-read-ctf/test-ambiguous-struct-B.o.hash.abi"
127 },
128 {
129 "data/test-read-ctf/test-conflicting-type-syms-a.o",
130 "",
131 "",
132 HASH_TYPE_ID_STYLE,
133 "data/test-read-ctf/test-conflicting-type-syms-a.o.hash.abi",
134 "output/test-read-ctf/test-conflicting-type-syms-a.o.hash.abi"
135 },
136 {
137 "data/test-read-ctf/test-conflicting-type-syms-b.o",
138 "",
139 "",
140 HASH_TYPE_ID_STYLE,
141 "data/test-read-ctf/test-conflicting-type-syms-b.o.hash.abi",
142 "output/test-read-ctf/test-conflicting-type-syms-b.o.hash.abi"
143 },
144 {
145 "data/test-read-common/test4.so",
146 "",
147 "",
148 SEQUENCE_TYPE_ID_STYLE,
149 "data/test-read-ctf/test4.so.abi",
150 "output/test-read-ctf/test4.so.abi"
151 },
152 {
153 "data/test-read-common/test4.so",
154 "",
155 "",
156 HASH_TYPE_ID_STYLE,
157 "data/test-read-ctf/test4.so.hash.abi",
158 "output/test-read-ctf/test4.so.hash.abi"
159 },
160 {
161 "data/test-read-ctf/test5.o",
162 "",
163 "",
164 SEQUENCE_TYPE_ID_STYLE,
165 "data/test-read-ctf/test5.o.abi",
166 "output/test-read-ctf/test5.o.abi"
167 },
168 {
169 "data/test-read-ctf/test7.o",
170 "",
171 "",
172 SEQUENCE_TYPE_ID_STYLE,
173 "data/test-read-ctf/test7.o.abi",
174 "output/test-read-ctf/test7.o.abi"
175 },
176 {
177 "data/test-read-ctf/test8.o",
178 "",
179 "",
180 SEQUENCE_TYPE_ID_STYLE,
181 "data/test-read-ctf/test8.o.abi",
182 "output/test-read-ctf/test8.o.abi"
183 },
184 {
185 "data/test-read-ctf/test9.o",
186 "",
187 "",
188 SEQUENCE_TYPE_ID_STYLE,
189 "data/test-read-ctf/test9.o.abi",
190 "output/test-read-ctf/test9.o.abi"
191 },
192 {
193 "data/test-read-ctf/test-enum.o",
194 "",
195 "",
196 SEQUENCE_TYPE_ID_STYLE,
197 "data/test-read-ctf/test-enum.o.abi",
198 "output/test-read-ctf/test-enum.o.abi"
199 },
200 {
201 "data/test-read-ctf/test-enum-symbol.o",
202 "",
203 "",
204 HASH_TYPE_ID_STYLE,
205 "data/test-read-ctf/test-enum-symbol.o.hash.abi",
206 "output/test-read-ctf/test-enum-symbol.o.hash.abi"
207 },
208 {
209 "data/test-read-ctf/test-dynamic-array.o",
210 "",
211 "",
212 SEQUENCE_TYPE_ID_STYLE,
213 "data/test-read-ctf/test-dynamic-array.o.abi",
214 "output/test-read-ctf/test-dynamic-array.o.abi"
215 },
216 {
217 "data/test-read-ctf/test-anonymous-fields.o",
218 "",
219 "",
220 SEQUENCE_TYPE_ID_STYLE,
221 "data/test-read-ctf/test-anonymous-fields.o.abi",
222 "output/test-read-ctf/test-anonymous-fields.o.abi"
223 },
224 {
225 "data/test-read-common/PR27700/test-PR27700.o",
226 "",
227 "data/test-read-common/PR27700/pub-incdir",
228 HASH_TYPE_ID_STYLE,
229 "data/test-read-ctf/PR27700/test-PR27700.abi",
230 "output/test-read-ctf/PR27700/test-PR27700.abi",
231 },
232 {
233 "data/test-read-ctf/test-callback.o",
234 "",
235 "",
236 SEQUENCE_TYPE_ID_STYLE,
237 "data/test-read-ctf/test-callback.abi",
238 "output/test-read-ctf/test-callback.abi",
239 },
240 {
241 "data/test-read-ctf/test-array-of-pointers.o",
242 "",
243 "",
244 SEQUENCE_TYPE_ID_STYLE,
245 "data/test-read-ctf/test-array-of-pointers.abi",
246 "output/test-read-ctf/test-array-of-pointers.abi",
247 },
248 {
249 "data/test-read-ctf/test-functions-declaration.o",
250 "",
251 "",
252 SEQUENCE_TYPE_ID_STYLE,
253 "data/test-read-ctf/test-functions-declaration.abi",
254 "output/test-read-ctf/test-functions-declaration.abi",
255 },
256 {
257 "data/test-read-ctf/test-forward-type-decl.o",
258 "",
259 "",
260 SEQUENCE_TYPE_ID_STYLE,
261 "data/test-read-ctf/test-forward-type-decl.abi",
262 "output/test-read-ctf/test-forward-type-decl.abi",
263 },
264 {
265 "data/test-read-ctf/test-list-struct.o",
266 "",
267 "",
268 SEQUENCE_TYPE_ID_STYLE,
269 "data/test-read-ctf/test-list-struct.abi",
270 "output/test-read-ctf/test-list-struct.abi",
271 },
272 {
273 "data/test-read-common/test-PR26568-1.o",
274 "",
275 "",
276 SEQUENCE_TYPE_ID_STYLE,
277 "data/test-read-ctf/test-PR26568-1.o.abi",
278 "output/test-read-ctf/test-PR26568-1.o.abi",
279 },
280 {
281 "data/test-read-common/test-PR26568-2.o",
282 "",
283 "",
284 SEQUENCE_TYPE_ID_STYLE,
285 "data/test-read-ctf/test-PR26568-2.o.abi",
286 "output/test-read-ctf/test-PR26568-2.o.abi",
287 },
288 {
289 "data/test-read-ctf/test-callback2.o",
290 "",
291 "",
292 SEQUENCE_TYPE_ID_STYLE,
293 "data/test-read-ctf/test-callback2.abi",
294 "output/test-read-ctf/test-callback2.abi",
295 },
296 // This should be the last entry.
297 {NULL, NULL, NULL, SEQUENCE_TYPE_ID_STYLE, NULL, NULL}
298 };
299
300 /// Task specialization to perform CTF tests.
301 struct test_task_ctf : public test_task
302 {
303 test_task_ctf(const InOutSpec &s,
304 string& a_out_abi_base,
305 string& a_in_elf_base,
306 string& a_in_abi_base);
307 virtual void
308 perform();
309
310 virtual
~test_task_ctftest_task_ctf311 ~test_task_ctf()
312 {}
313 }; // end struct test_task_ctf
314
315 /// Constructor.
316 ///
317 /// Task to be executed for each CTF test entry in @ref
318 /// abigail::tests::read_common::InOutSpec.
319 /// @param InOutSpec the array containing set of tests.
320 ///
321 /// @param a_out_abi_base the output base directory for abixml files.
322 ///
323 /// @param a_in_elf_base the input base directory for object files.
324 ///
325 /// @param a_in_elf_base the input base directory for expected
326 /// abixml files.
test_task_ctf(const InOutSpec & s,string & a_out_abi_base,string & a_in_elf_base,string & a_in_abi_base)327 test_task_ctf::test_task_ctf(const InOutSpec &s,
328 string& a_out_abi_base,
329 string& a_in_elf_base,
330 string& a_in_abi_base)
331 : test_task(s, a_out_abi_base, a_in_elf_base, a_in_abi_base)
332 {}
333
334 /// The thread function to execute each CTF test entry in @ref
335 /// abigail::tests::read_common::InOutSpec.
336 ///
337 /// This reads the corpus into memory, saves it to disk, loads it
338 /// again and compares the new in-memory representation against the
339 void
perform()340 test_task_ctf::perform()
341 {
342 abigail::ir::environment_sptr env;
343
344 set_in_elf_path();
345 set_in_suppr_spec_path();
346
347 env.reset(new abigail::ir::environment);
348 abigail::elf_reader::status status =
349 abigail::elf_reader::STATUS_UNKNOWN;
350 vector<char**> di_roots;
351 ABG_ASSERT(abigail::tools_utils::file_exists(in_elf_path));
352
353 read_context_sptr ctxt = create_read_context(in_elf_path,
354 di_roots,
355 env.get());
356 ABG_ASSERT(ctxt);
357
358 corpus_sptr corp = read_corpus(ctxt.get(), status);
359 // if there is no output and no input, assume that we do not care about the
360 // actual read result, just that it succeeded.
361 if (!spec.in_abi_path && !spec.out_abi_path)
362 {
363 // Phew! we made it here and we did not crash! yay!
364 return;
365 }
366 if (!corp)
367 {
368 error_message = string("failed to read ") + in_elf_path + "\n";
369 is_ok = false;
370 return;
371 }
372 corp->set_path(spec.in_elf_path);
373 // Do not take architecture names in comparison so that these
374 // test input binaries can come from whatever arch the
375 // programmer likes.
376 corp->set_architecture_name("");
377
378 if (!(is_ok = set_out_abi_path()))
379 return;
380
381 if (!(is_ok = serialize_corpus(out_abi_path, corp)))
382 return;
383
384 if (!(is_ok = run_abidw("--ctf ")))
385 return;
386
387 if (!(is_ok = run_diff()))
388 return;
389 }
390
391 /// Create a new CTF instance for task to be execute by the testsuite.
392 ///
393 /// @param s the @ref abigail::tests::read_common::InOutSpec
394 /// tests container.
395 ///
396 /// @param a_out_abi_base the output base directory for abixml files.
397 ///
398 /// @param a_in_elf_base the input base directory for object files.
399 ///
400 /// @param a_in_abi_base the input base directory for abixml files.
401 ///
402 /// @return abigail::tests::read_common::test_task instance.
403 static test_task*
new_task(const InOutSpec * s,string & a_out_abi_base,string & a_in_elf_base,string & a_in_abi_base)404 new_task(const InOutSpec* s, string& a_out_abi_base,
405 string& a_in_elf_base, string& a_in_abi_base)
406 {
407 return new test_task_ctf(*s, a_out_abi_base,
408 a_in_elf_base, a_in_abi_base);
409 }
410
411 int
main(int argc,char * argv[])412 main(int argc, char *argv[])
413 {
414 options opts;
415 if (!parse_command_line(argc, argv, opts))
416 {
417 if (!opts.wrong_option.empty())
418 emit_prefix(argv[0], cerr)
419 << "unrecognized option: " << opts.wrong_option << "\n";
420 display_usage(argv[0], cerr);
421 return 1;
422 }
423
424 // compute number of tests to be executed.
425 const size_t num_tests = sizeof(in_out_specs) / sizeof(InOutSpec) - 1;
426
427 return run_tests(num_tests, in_out_specs, opts, new_task);
428 }
429