// 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 "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_); std::unique_ptr<repr::IRReader> new_reader = repr::IRReader::CreateIRReader(text_format_new_); if (!old_reader || !new_reader || !old_reader->ReadDump(old_dump_) || !new_reader->ReadDump(new_dump_)) { llvm::errs() << "Could not create Text Format readers\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()); 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->GetUniqueId(), record_type); break; case repr::EnumTypeKind: enum_types.emplace( static_cast<const repr::EnumTypeIR *>(type)->GetUniqueId(), 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