mirror of
git://sourceware.org/git/libabigail.git
synced 2024-12-16 06:54:37 +00:00
01e5bfc4a4
In relocatable files, two symbols listed in the .symtab section can have the same value and yet be different. That is because those symbols can be *defined* in different sections. And the value of those symbols represent addresses (offsets) within their own respective sections (a.k.a section-relative addresses). In the same time, symbol address as referred-to in the DWARF information are *not* section-relative, rather, they are relative to the beginning of the whole binary. Until now, the DWARF-referred-to symbol addresses were translated into section-relative addresses, so that they could be compared to the other section-relative addresses we were getting from listing the symbols and their values from the .symtab section. The problem with that approach is that, during the translation from binary-relative to section-relative addresses we were wrongly assuming that all symbols referenced from the DWARF were defined in the .text section. This is wrong especially for ET_REL files because they could be defined in sections named .foo.text or .bar.text, for instance. This leads to issues where we wrongly consider that two symbols having the same value are the same. Because we wrongly assume that they are all defined in the same .text section. This patch fixes this problem by translating the section-relative addresses we see in .symtab into binary-relative addresses by adding the address of the section to the section-relative address. Those binary-addresses can thus safely be compared to the binary-relative addresses we see in the DWARF. And also, when two symbols have the same binary-relative address, we can now safely assume that they are the same -- they are aliases, basically. * src/abg-dwarf-reader.cc (read_context::{lookup_native_elf_symbol_from_index, maybe_adjust_et_rel_sym_addr_to_abs_addr}): Define new member functions. (read_context::lookup_elf_symbol_from_index): Add a new overload. Write the old overloads in terms of the new one. (read_context::{load_symbol_maps_from_symtab_section, populate_symbol_map_from_ksymtab_reloc}): Use the new maybe_adjust_et_rel_sym_addr_to_abs_addr function to translate the symbol value/address into a binary-relative address before adding it to the addr->sym maps. (read_context::maybe_adjust_{fn, var}_sym_address): Do not adjust DWARF-referred-to addresses of ET_REL symbols anymore. * tests/data/test-read-dwarf/PR25007-sdhci.ko: New binary test input. * tests/data/test-read-dwarf/PR25007-sdhci.ko.abi: ABI representation of the above. * tests/test-read-dwarf.cc: Add the new test input to the harness. * tests/data/test-diff-dwarf/test28-vtable-changes-report-0.txt: Adjust. * tests/data/test-diff-filter/test20-inline-report-0.txt: Likewise. * tests/data/test-diff-filter/test20-inline-report-1.txt: Likewise. * tests/data/test-diff-filter/test41-report-0.txt: Likewise. * tests/data/test-diff-filter/test9-report.txt: Likewise. Signed-off-by: Dodji Seketeli <dodji@redhat.com>
440 lines
12 KiB
C++
440 lines
12 KiB
C++
// -*- Mode: C++ -*-
|
|
//
|
|
// Copyright (C) 2013-2019 Red Hat, Inc.
|
|
//
|
|
// This file is part of the GNU Application Binary Interface Generic
|
|
// Analysis and Instrumentation Library (libabigail). This library is
|
|
// free software; you can redistribute it and/or modify it under the
|
|
// terms of the GNU Lesser General Public License as published by the
|
|
// Free Software Foundation; either version 3, or (at your option) any
|
|
// later version.
|
|
|
|
// This library is distributed in the hope that it will be useful, but
|
|
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
// General Lesser Public License for more details.
|
|
|
|
// You should have received a copy of the GNU Lesser General Public
|
|
// License along with this program; see the file COPYING-LGPLV3. If
|
|
// not, see <http://www.gnu.org/licenses/>.
|
|
|
|
// 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 <string>
|
|
#include <fstream>
|
|
#include <iostream>
|
|
#include <vector>
|
|
#include <cstdlib>
|
|
#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 abg_compat::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::create_write_context;
|
|
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;
|
|
const char* in_abi_path;
|
|
const char* out_abi_path;
|
|
};// end struct InOutSpec
|
|
|
|
|
|
InOutSpec in_out_specs[] =
|
|
{
|
|
{
|
|
"data/test-read-dwarf/test0",
|
|
"",
|
|
"data/test-read-dwarf/test0.abi",
|
|
"output/test-read-dwarf/test0.abi"
|
|
},
|
|
{
|
|
"data/test-read-dwarf/test1",
|
|
"",
|
|
"data/test-read-dwarf/test1.abi",
|
|
"output/test-read-dwarf/test1.abi"
|
|
},
|
|
{
|
|
"data/test-read-dwarf/test2.so",
|
|
"",
|
|
"data/test-read-dwarf/test2.so.abi",
|
|
"output/test-read-dwarf/test2.so.abi"
|
|
},
|
|
{
|
|
"data/test-read-dwarf/test3.so",
|
|
"",
|
|
"data/test-read-dwarf/test3.so.abi",
|
|
"output/test-read-dwarf/test3.so.abi"
|
|
},
|
|
{
|
|
"data/test-read-dwarf/test4.so",
|
|
"",
|
|
"data/test-read-dwarf/test4.so.abi",
|
|
"output/test-read-dwarf/test4.so.abi"
|
|
},
|
|
{
|
|
"data/test-read-dwarf/test5.o",
|
|
"",
|
|
"data/test-read-dwarf/test5.o.abi",
|
|
"output/test-read-dwarf/test5.o.abi"
|
|
},
|
|
{
|
|
"data/test-read-dwarf/test6.so",
|
|
"",
|
|
"data/test-read-dwarf/test6.so.abi",
|
|
"output/test-read-dwarf/test6.so.abi"
|
|
},
|
|
{
|
|
"data/test-read-dwarf/test7.so",
|
|
"",
|
|
"data/test-read-dwarf/test7.so.abi",
|
|
"output/test-read-dwarf/test7.so.abi"
|
|
},
|
|
{
|
|
"data/test-read-dwarf/test8-qualified-this-pointer.so",
|
|
"",
|
|
"data/test-read-dwarf/test8-qualified-this-pointer.so.abi",
|
|
"output/test-read-dwarf/test8-qualified-this-pointer.so.abi"
|
|
},
|
|
{
|
|
"data/test-read-dwarf/test9-pr18818-clang.so",
|
|
"",
|
|
"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",
|
|
"",
|
|
"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",
|
|
"",
|
|
"data/test-read-dwarf/test11-pr18828.so.abi",
|
|
"output/test-read-dwarf/test11-pr18828.so.abi",
|
|
},
|
|
{
|
|
"data/test-read-dwarf/test12-pr18844.so",
|
|
"",
|
|
"data/test-read-dwarf/test12-pr18844.so.abi",
|
|
"output/test-read-dwarf/test12-pr18844.so.abi",
|
|
},
|
|
{
|
|
"data/test-read-dwarf/test13-pr18894.so",
|
|
"",
|
|
"data/test-read-dwarf/test13-pr18894.so.abi",
|
|
"output/test-read-dwarf/test13-pr18894.so.abi",
|
|
},
|
|
{
|
|
"data/test-read-dwarf/test14-pr18893.so",
|
|
"",
|
|
"data/test-read-dwarf/test14-pr18893.so.abi",
|
|
"output/test-read-dwarf/test14-pr18893.so.abi",
|
|
},
|
|
{
|
|
"data/test-read-dwarf/test15-pr18892.so",
|
|
"",
|
|
"data/test-read-dwarf/test15-pr18892.so.abi",
|
|
"output/test-read-dwarf/test15-pr18892.so.abi",
|
|
},
|
|
{
|
|
"data/test-read-dwarf/test16-pr18904.so",
|
|
"",
|
|
"data/test-read-dwarf/test16-pr18904.so.abi",
|
|
"output/test-read-dwarf/test16-pr18904.so.abi",
|
|
},
|
|
{
|
|
"data/test-read-dwarf/test17-pr19027.so",
|
|
"",
|
|
"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",
|
|
"",
|
|
"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",
|
|
"",
|
|
"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",
|
|
"",
|
|
"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",
|
|
"",
|
|
"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",
|
|
"",
|
|
"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",
|
|
"",
|
|
"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",
|
|
"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",
|
|
"",
|
|
"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",
|
|
"",
|
|
"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",
|
|
"",
|
|
"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",
|
|
"",
|
|
"data/test-read-dwarf/PR24378-fn-is-not-scope.abi",
|
|
"output/test-read-dwarf/PR24378-fn-is-not-scope.abi",
|
|
},
|
|
{
|
|
"data/test-read-dwarf/PR25007-sdhci.ko",
|
|
"",
|
|
"data/test-read-dwarf/PR25007-sdhci.ko.abi",
|
|
"output/test-read-dwarf/PR25007-sdhci.ko.abi",
|
|
},
|
|
// This should be the last entry.
|
|
{NULL, NULL, 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<char**> di_roots;
|
|
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 (!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;
|
|
}
|
|
const write_context_sptr write_ctxt
|
|
= create_write_context(corp->get_environment(), of);
|
|
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> 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<abigail::workers::task_sptr>& completed_tasks =
|
|
task_queue.get_completed_tasks();
|
|
|
|
ABG_ASSERT(completed_tasks.size() == num_tests);
|
|
|
|
for (vector<abigail::workers::task_sptr>::const_iterator ti =
|
|
completed_tasks.begin();
|
|
ti != completed_tasks.end();
|
|
++ti)
|
|
{
|
|
test_task_sptr t = dynamic_pointer_cast<test_task>(*ti);
|
|
if (!t->is_ok)
|
|
{
|
|
is_ok = false;
|
|
if (!t->error_message.empty())
|
|
cerr << t->error_message << '\n';
|
|
}
|
|
}
|
|
|
|
return !is_ok;
|
|
}
|