| // 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();}) || |
| ignored_linker_set_keys_.find(element->GetLinkerSetKey()) != |
| ignored_linker_set_keys_.end()) { |
| continue; |
| } |
| |
| // If this FunctionIR/GlobalVarIR is missing from the new ABI dump but a |
| // corresponding ElfFunctionIR/ElfObjectIR can be found in the new ABI dump, |
| // don't emit error on this element. |
| const std::string &element_linker_set_key = element->GetLinkerSetKey(); |
| if (diff_policy_options_.allow_adding_removing_referenced_apis && |
| 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; |
| |
| // The old and new linker set keys are supposed to be equal. |
| if (IgnoreSymbol<T>(old_element, ignored_symbols_, |
| [](const T *e) { return e->GetLinkerSetKey(); }) || |
| ignored_linker_set_keys_.find(old_element->GetLinkerSetKey()) != |
| ignored_linker_set_keys_.end()) { |
| continue; |
| } |
| |
| DiffWrapper<T> diff_wrapper( |
| old_element, new_element, ir_diff_dumper, old_types, new_types, |
| diff_policy_options_, &type_cache_, ignored_linker_set_keys_); |
| if (!diff_wrapper.DumpDiff(diff_kind)) { |
| llvm::errs() << "Failed to diff elements\n"; |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| |
| } // namespace diff |
| } // namespace header_checker |