402 lines
16 KiB
C++
402 lines
16 KiB
C++
// Copyright (C) 2016 The Android Open Source Project
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
#include "diff/abi_diff.h"
|
|
|
|
#include "repr/ir_reader.h"
|
|
#include "utils/header_abi_util.h"
|
|
|
|
#include <llvm/Support/raw_ostream.h>
|
|
|
|
#include <memory>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
|
namespace header_checker {
|
|
namespace diff {
|
|
|
|
|
|
repr::CompatibilityStatusIR HeaderAbiDiff::GenerateCompatibilityReport() {
|
|
std::unique_ptr<repr::IRReader> old_reader =
|
|
repr::IRReader::CreateIRReader(text_format_old_);
|
|
if (!old_reader || !old_reader->ReadDump(old_dump_)) {
|
|
llvm::errs() << "Failed to read old ABI dump: " << old_dump_ << "\n";
|
|
::exit(1);
|
|
}
|
|
|
|
std::unique_ptr<repr::IRReader> new_reader =
|
|
repr::IRReader::CreateIRReader(text_format_new_);
|
|
if (!new_reader || !new_reader->ReadDump(new_dump_)) {
|
|
llvm::errs() << "Failed to read new ABI dump: " << new_dump_ << "\n";
|
|
::exit(1);
|
|
}
|
|
|
|
std::unique_ptr<repr::IRDiffDumper> ir_diff_dumper =
|
|
repr::IRDiffDumper::CreateIRDiffDumper(text_format_diff_, cr_);
|
|
repr::CompatibilityStatusIR status =
|
|
CompareTUs(old_reader->GetModule(), new_reader->GetModule(),
|
|
ir_diff_dumper.get());
|
|
if (!ir_diff_dumper->Dump()) {
|
|
llvm::errs() << "Could not dump diff report\n";
|
|
::exit(1);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
repr::CompatibilityStatusIR HeaderAbiDiff::CompareTUs(
|
|
const repr::ModuleIR &old_tu, const repr::ModuleIR &new_tu,
|
|
repr::IRDiffDumper *ir_diff_dumper) {
|
|
// Collect all old and new types in maps, so that we can refer to them by
|
|
// type name / linker_set_key later.
|
|
const AbiElementMap<const repr::TypeIR *> old_types =
|
|
old_tu.GetTypeGraph();
|
|
const AbiElementMap<const repr::TypeIR *> new_types =
|
|
new_tu.GetTypeGraph();
|
|
|
|
// CollectDynsymExportables() fills in added, removed, unsafe, and safe function diffs.
|
|
if (!CollectDynsymExportables(old_tu.GetFunctions(), new_tu.GetFunctions(),
|
|
old_tu.GetElfFunctions(),
|
|
new_tu.GetElfFunctions(),
|
|
old_types, new_types,
|
|
ir_diff_dumper) ||
|
|
!CollectDynsymExportables(old_tu.GetGlobalVariables(),
|
|
new_tu.GetGlobalVariables(),
|
|
old_tu.GetElfObjects(),
|
|
new_tu.GetElfObjects(),
|
|
old_types, new_types,
|
|
ir_diff_dumper)) {
|
|
llvm::errs() << "Unable to collect dynsym exportables\n";
|
|
::exit(1);
|
|
}
|
|
|
|
// By the time this call is reached, all referenced types have been diffed.
|
|
// So all additional calls on ir_diff_dumper get DiffKind::Unreferenced.
|
|
if (check_all_apis_ && !CollectUserDefinedTypes(old_tu, new_tu, old_types,
|
|
new_types, ir_diff_dumper)) {
|
|
llvm::errs() << "Unable to collect user defined types\n";
|
|
::exit(1);
|
|
}
|
|
|
|
repr::CompatibilityStatusIR combined_status =
|
|
ir_diff_dumper->GetCompatibilityStatusIR();
|
|
|
|
ir_diff_dumper->AddLibNameIR(lib_name_);
|
|
ir_diff_dumper->AddArchIR(arch_);
|
|
ir_diff_dumper->AddCompatibilityStatusIR(combined_status);
|
|
return combined_status;
|
|
}
|
|
|
|
std::pair<AbiElementMap<const repr::EnumTypeIR *>,
|
|
AbiElementMap<const repr::RecordTypeIR *>>
|
|
HeaderAbiDiff::ExtractUserDefinedTypes(const repr::ModuleIR &tu) {
|
|
AbiElementMap<const repr::EnumTypeIR *> enum_types;
|
|
AbiElementMap<const repr::RecordTypeIR *> record_types;
|
|
// Iterate through the ODRListMap, if there is more than 1 element in the
|
|
// list, we cannot really unique the type by name, so skip it. If not, add a
|
|
// map entry UniqueId -> const Record(Enum)TypeIR *.
|
|
for (auto &it : tu.GetODRListMap()) {
|
|
auto &odr_list = it.second;
|
|
if (odr_list.size() != 1) {
|
|
continue;
|
|
}
|
|
const repr::TypeIR *type = odr_list.begin()->type_ir_;
|
|
const repr::RecordTypeIR *record_type = nullptr;
|
|
switch (type->GetKind()) {
|
|
case repr::RecordTypeKind:
|
|
record_type = static_cast<const repr::RecordTypeIR *>(type);
|
|
if (record_type->IsAnonymous()) {
|
|
continue;
|
|
}
|
|
record_types.emplace(
|
|
record_type->GetLinkerSetKey(), record_type);
|
|
break;
|
|
case repr::EnumTypeKind:
|
|
enum_types.emplace(
|
|
static_cast<const repr::EnumTypeIR *>(type)->GetLinkerSetKey(),
|
|
static_cast<const repr::EnumTypeIR *>(type));
|
|
break;
|
|
case repr::FunctionTypeKind:
|
|
continue;
|
|
default:
|
|
// Only user defined types should have ODR list entries.
|
|
assert(0);
|
|
}
|
|
}
|
|
return std::make_pair(std::move(enum_types), std::move(record_types));
|
|
}
|
|
|
|
bool HeaderAbiDiff::CollectUserDefinedTypes(
|
|
const repr::ModuleIR &old_tu, const repr::ModuleIR &new_tu,
|
|
const AbiElementMap<const repr::TypeIR *> &old_types_map,
|
|
const AbiElementMap<const repr::TypeIR *> &new_types_map,
|
|
repr::IRDiffDumper *ir_diff_dumper) {
|
|
|
|
auto old_enums_and_records_extracted = ExtractUserDefinedTypes(old_tu);
|
|
auto new_enums_and_records_extracted = ExtractUserDefinedTypes(new_tu);
|
|
|
|
return (CollectUserDefinedTypesInternal(
|
|
old_enums_and_records_extracted.second,
|
|
new_enums_and_records_extracted.second, old_types_map,
|
|
new_types_map, ir_diff_dumper) &&
|
|
CollectUserDefinedTypesInternal(
|
|
old_enums_and_records_extracted.first,
|
|
new_enums_and_records_extracted.first,
|
|
old_types_map, new_types_map, ir_diff_dumper));
|
|
}
|
|
|
|
template <typename T>
|
|
bool HeaderAbiDiff::CollectUserDefinedTypesInternal(
|
|
const AbiElementMap<const T*> &old_ud_types_map,
|
|
const AbiElementMap<const T*> &new_ud_types_map,
|
|
const AbiElementMap<const repr::TypeIR *> &old_types_map,
|
|
const AbiElementMap<const repr::TypeIR *> &new_types_map,
|
|
repr::IRDiffDumper *ir_diff_dumper) {
|
|
|
|
return (Collect(old_ud_types_map, new_ud_types_map, nullptr, nullptr,
|
|
ir_diff_dumper, old_types_map, new_types_map) &&
|
|
PopulateCommonElements(old_ud_types_map, new_ud_types_map,
|
|
old_types_map, new_types_map, ir_diff_dumper,
|
|
repr::DiffMessageIR::Unreferenced));
|
|
}
|
|
|
|
template <typename T, typename ElfSymbolType>
|
|
bool HeaderAbiDiff::CollectDynsymExportables(
|
|
const AbiElementMap<T> &old_exportables,
|
|
const AbiElementMap<T> &new_exportables,
|
|
const AbiElementMap<ElfSymbolType> &old_elf_symbols,
|
|
const AbiElementMap<ElfSymbolType> &new_elf_symbols,
|
|
const AbiElementMap<const repr::TypeIR *> &old_types_map,
|
|
const AbiElementMap<const repr::TypeIR *> &new_types_map,
|
|
repr::IRDiffDumper *ir_diff_dumper) {
|
|
AbiElementMap<const T *> old_exportables_map;
|
|
AbiElementMap<const T *> new_exportables_map;
|
|
AbiElementMap<const repr::ElfSymbolIR *> old_elf_symbol_map;
|
|
AbiElementMap<const repr::ElfSymbolIR *> new_elf_symbol_map;
|
|
|
|
utils::AddToMap(&old_exportables_map, old_exportables,
|
|
[](auto e) { return e->first;},
|
|
[](auto e) {return &(e->second);});
|
|
utils::AddToMap(&new_exportables_map, new_exportables,
|
|
[](auto e) { return e->first;},
|
|
[](auto e) { return &(e->second);});
|
|
|
|
utils::AddToMap(&old_elf_symbol_map, old_elf_symbols,
|
|
[](auto e) { return e->first;},
|
|
[](auto e) {return &(e->second);});
|
|
utils::AddToMap(&new_elf_symbol_map, new_elf_symbols,
|
|
[](auto e) { return e->first;},
|
|
[](auto e) {return &(e->second);});
|
|
|
|
if (!Collect(old_exportables_map,
|
|
new_exportables_map, &old_elf_symbol_map, &new_elf_symbol_map,
|
|
ir_diff_dumper, old_types_map, new_types_map) ||
|
|
!CollectElfSymbols(old_elf_symbol_map, new_elf_symbol_map,
|
|
ir_diff_dumper) ||
|
|
!PopulateCommonElements(old_exportables_map, new_exportables_map,
|
|
old_types_map, new_types_map, ir_diff_dumper,
|
|
repr::DiffMessageIR::Referenced)) {
|
|
llvm::errs() << "Diffing dynsym exportables failed\n";
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Collect the added and removed elements. The ELF maps are needed because the
|
|
// metadata for some symbols might be absent from AST. For example, if a
|
|
// function Foo() is defined in an assembly file on target A, but in a C/C++
|
|
// file on target B. Even though Foo() does not have metadata surrounding it
|
|
// when building target A, it doesn't mean that Foo() is not a part of the ABI
|
|
// of the library.
|
|
template <typename T>
|
|
bool HeaderAbiDiff::Collect(
|
|
const AbiElementMap<const T*> &old_elements_map,
|
|
const AbiElementMap<const T*> &new_elements_map,
|
|
const AbiElementMap<const repr::ElfSymbolIR *> *old_elf_map,
|
|
const AbiElementMap<const repr::ElfSymbolIR *> *new_elf_map,
|
|
repr::IRDiffDumper *ir_diff_dumper,
|
|
const AbiElementMap<const repr::TypeIR *> &old_types_map,
|
|
const AbiElementMap<const repr::TypeIR *> &new_types_map) {
|
|
if (!PopulateRemovedElements(
|
|
old_elements_map, new_elements_map, old_elf_map, new_elf_map,
|
|
ir_diff_dumper, repr::DiffMessageIR::Removed, old_types_map) ||
|
|
!PopulateRemovedElements(
|
|
new_elements_map, old_elements_map, new_elf_map, old_elf_map,
|
|
ir_diff_dumper, repr::DiffMessageIR::Added, new_types_map)) {
|
|
llvm::errs() << "Populating functions in report failed\n";
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool HeaderAbiDiff::CollectElfSymbols(
|
|
const AbiElementMap<const repr::ElfSymbolIR *> &old_symbols,
|
|
const AbiElementMap<const repr::ElfSymbolIR *> &new_symbols,
|
|
repr::IRDiffDumper *ir_diff_dumper) {
|
|
std::vector<const repr::ElfSymbolIR *> removed_elements =
|
|
utils::FindRemovedElements(old_symbols, new_symbols);
|
|
|
|
std::vector<const repr::ElfSymbolIR *> added_elements =
|
|
utils::FindRemovedElements(new_symbols, old_symbols);
|
|
|
|
return (PopulateElfElements(removed_elements, ir_diff_dumper,
|
|
repr::IRDiffDumper::DiffKind::Removed) &&
|
|
PopulateElfElements(added_elements, ir_diff_dumper,
|
|
repr::IRDiffDumper::DiffKind::Added));
|
|
}
|
|
|
|
bool HeaderAbiDiff::PopulateElfElements(
|
|
std::vector<const repr::ElfSymbolIR *> &elf_elements,
|
|
repr::IRDiffDumper *ir_diff_dumper,
|
|
repr::IRDiffDumper::DiffKind diff_kind) {
|
|
for (auto &&elf_element : elf_elements) {
|
|
if (allow_adding_removing_weak_symbols_ &&
|
|
elf_element->GetBinding() == repr::ElfSymbolIR::Weak) {
|
|
continue;
|
|
}
|
|
if (!ir_diff_dumper->AddElfSymbolMessageIR(elf_element, diff_kind)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
template <typename T>
|
|
bool HeaderAbiDiff::PopulateRemovedElements(
|
|
const AbiElementMap<const T*> &old_elements_map,
|
|
const AbiElementMap<const T*> &new_elements_map,
|
|
const AbiElementMap<const repr::ElfSymbolIR *> *old_elf_map,
|
|
const AbiElementMap<const repr::ElfSymbolIR *> *new_elf_map,
|
|
repr::IRDiffDumper *ir_diff_dumper,
|
|
repr::IRDiffDumper::DiffKind diff_kind,
|
|
const AbiElementMap<const repr::TypeIR *> &removed_types_map) {
|
|
std::vector<const T *> removed_elements =
|
|
utils::FindRemovedElements(old_elements_map, new_elements_map);
|
|
if (!DumpLoneElements(removed_elements, old_elf_map, new_elf_map,
|
|
ir_diff_dumper, diff_kind, removed_types_map)) {
|
|
llvm::errs() << "Dumping added or removed element to report failed\n";
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Find the common elements (common records, common enums, common functions etc)
|
|
// Dump the differences (we need type maps for this diff since we'll get
|
|
// reachable types from here)
|
|
template <typename T>
|
|
bool HeaderAbiDiff::PopulateCommonElements(
|
|
const AbiElementMap<const T *> &old_elements_map,
|
|
const AbiElementMap<const T *> &new_elements_map,
|
|
const AbiElementMap<const repr::TypeIR *> &old_types,
|
|
const AbiElementMap<const repr::TypeIR *> &new_types,
|
|
repr::IRDiffDumper *ir_diff_dumper,
|
|
repr::IRDiffDumper::DiffKind diff_kind) {
|
|
std::vector<std::pair<const T *, const T *>> common_elements =
|
|
utils::FindCommonElements(old_elements_map, new_elements_map);
|
|
if (!DumpDiffElements(common_elements, old_types, new_types,
|
|
ir_diff_dumper, diff_kind)) {
|
|
llvm::errs() << "Dumping difference in common element to report failed\n";
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
template <typename T>
|
|
bool HeaderAbiDiff::DumpLoneElements(
|
|
std::vector<const T *> &elements,
|
|
const AbiElementMap<const repr::ElfSymbolIR *> *old_elf_map,
|
|
const AbiElementMap<const repr::ElfSymbolIR *> *new_elf_map,
|
|
repr::IRDiffDumper *ir_diff_dumper,
|
|
repr::IRDiffDumper::DiffKind diff_kind,
|
|
const AbiElementMap<const repr::TypeIR *> &types_map) {
|
|
std::smatch source_file_match;
|
|
std::regex source_file_regex(" at ");
|
|
|
|
for (auto &&element : elements) {
|
|
if (IgnoreSymbol<T>(element, ignored_symbols_,
|
|
[](const T *e) {return e->GetLinkerSetKey();})) {
|
|
continue;
|
|
}
|
|
|
|
// If an element (FunctionIR or GlobalVarIR) is missing from the new ABI
|
|
// dump but a corresponding ELF symbol (ElfFunctionIR or ElfObjectIR) can
|
|
// be found in the new ABI dump file, don't emit error on this element.
|
|
// This may happen when the standard reference target implements the
|
|
// function (or the global variable) in C/C++ and the target-under-test
|
|
// implements the function (or the global variable) in assembly.
|
|
const std::string &element_linker_set_key = element->GetLinkerSetKey();
|
|
if (new_elf_map &&
|
|
new_elf_map->find(element_linker_set_key) != new_elf_map->end()) {
|
|
continue;
|
|
}
|
|
|
|
// If the `-ignore-weak-symbols` option is enabled, ignore the element if
|
|
// it was a weak symbol.
|
|
if (allow_adding_removing_weak_symbols_ && old_elf_map) {
|
|
auto elem_it = old_elf_map->find(element_linker_set_key);
|
|
if (elem_it != old_elf_map->end() &&
|
|
elem_it->second->GetBinding() == repr::ElfSymbolIR::Weak) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// If the record / enum has source file information, skip it.
|
|
if (std::regex_search(element_linker_set_key, source_file_match,
|
|
source_file_regex)) {
|
|
continue;
|
|
}
|
|
|
|
auto element_copy = *element;
|
|
ReplaceTypeIdsWithTypeNames(types_map, &element_copy);
|
|
if (!ir_diff_dumper->AddLinkableMessageIR(&element_copy, diff_kind)) {
|
|
llvm::errs() << "Couldn't dump added or removed element\n";
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
template <typename T>
|
|
bool HeaderAbiDiff::DumpDiffElements(
|
|
std::vector<std::pair<const T *,const T *>> &pairs,
|
|
const AbiElementMap<const repr::TypeIR *> &old_types,
|
|
const AbiElementMap<const repr::TypeIR *> &new_types,
|
|
repr::IRDiffDumper *ir_diff_dumper,
|
|
repr::IRDiffDumper::DiffKind diff_kind) {
|
|
for (auto &&pair : pairs) {
|
|
const T *old_element = pair.first;
|
|
const T *new_element = pair.second;
|
|
|
|
if (IgnoreSymbol<T>(old_element, ignored_symbols_,
|
|
[](const T *e) {return e->GetLinkerSetKey();})) {
|
|
continue;
|
|
}
|
|
|
|
DiffWrapper<T> diff_wrapper(
|
|
old_element, new_element, ir_diff_dumper, old_types, new_types,
|
|
diff_policy_options_, &type_cache_);
|
|
if (!diff_wrapper.DumpDiff(diff_kind)) {
|
|
llvm::errs() << "Failed to diff elements\n";
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
} // namespace diff
|
|
} // namespace header_checker
|