From 83fc92bb30b2b70a557bf8c5118d5d60194cf8b4 Mon Sep 17 00:00:00 2001 From: Jayant Chowdhary Date: Wed, 5 Jul 2017 17:56:33 -0700 Subject: [PATCH] Introduce recursive dumping/diffing of types. 1) Types are now quantifed into one of the following: a) RecordType - structures/ unions/ classes. b) EnumType - enumerated types. c) QualifiedType - types with qualifiers intact. d) PointerType - pointer to a type. e) LvalueReferenceType f) RvalueReferenceType g) ArrayType 2) ABI diffing now diffs based on types directly / indirectly reachable by function signatures / global variable types by default. Also added an option to header-abi-diff: -check-all-apis : Include changes to all APIs exported via exported headers whether directly / indirectly used by function signatures / global variable types or not. 3) Introduced an internal representation of abi information. This enables us to switch formats of abi-dumps / abi-diffs more easily without changing the AST parsing code. For example: In the future, if we wanted to add options to view the abi-dumps in html / xml we could just add classes which extended IRDumper and implement dumping methods. 4) The linked ABI dump of a shared library also includes a list of the exported functions and objects collected from the .dynsym table. This makes it possible to avoid false reports of ABI breakages when functions/ global variables are moved to assembly files. Test: abi-dumping and linking: make -j64 from ToT abi_diffing: change parameter types directly, indirectly. Ran tests/test.py with soong running the built tool, all tests passed. Bug: 62060883 Bug: 63865902 Change-Id: Id9ef757165a1669049fde20bda4147d5074cc191 --- vndk/tools/header-checker/Android.bp | 15 +- vndk/tools/header-checker/README.md | 9 +- .../header-abi-diff/src/abi_diff.cpp | 395 +++-- .../header-abi-diff/src/abi_diff.h | 133 +- .../header-abi-diff/src/abi_diff_wrappers.cpp | 798 +++++++--- .../header-abi-diff/src/abi_diff_wrappers.h | 175 ++- .../header-abi-diff/src/header_abi_diff.cpp | 65 +- .../header-abi-dumper/src/abi_wrappers.cpp | 852 ++++++----- .../header-abi-dumper/src/abi_wrappers.h | 163 +- .../header-abi-dumper/src/ast_processing.cpp | 134 +- .../header-abi-dumper/src/ast_processing.h | 17 +- .../src/header_abi_linker.cpp | 151 +- .../header-abi-util/include/header_abi_util.h | 57 + .../include/ir_representation.h | 1126 ++++++++++++++ .../include/ir_representation_protobuf.h | 418 +++++ .../header-abi-util/src/ir_representation.cpp | 67 + .../src/ir_representation_protobuf.cpp | 1353 +++++++++++++++++ .../tools/header-checker/proto/abi_diff.proto | 151 +- .../tools/header-checker/proto/abi_dump.proto | 129 +- .../header-checker/tests/input/example1.h | 35 +- .../header-checker/tests/input/example2.h | 1 + 21 files changed, 5077 insertions(+), 1167 deletions(-) create mode 100644 vndk/tools/header-checker/header-abi-util/include/ir_representation.h create mode 100644 vndk/tools/header-checker/header-abi-util/include/ir_representation_protobuf.h create mode 100644 vndk/tools/header-checker/header-abi-util/src/ir_representation.cpp create mode 100644 vndk/tools/header-checker/header-abi-util/src/ir_representation_protobuf.cpp diff --git a/vndk/tools/header-checker/Android.bp b/vndk/tools/header-checker/Android.bp index 71cf17594..3f29d066f 100644 --- a/vndk/tools/header-checker/Android.bp +++ b/vndk/tools/header-checker/Android.bp @@ -24,11 +24,14 @@ cc_defaults { cflags: [ "-Wall", "-Werror", - "-std=c++11", "-DGOOGLE_PROTOBUF_NO_RTTI", "-UNDEBUG" ], + cppflags: [ + "-std=c++14", + ], + target: { windows: { enabled: false @@ -40,8 +43,8 @@ cc_defaults { name: "header-checker-lib-defaults", static_libs: [ - "libclangTooling", "libclangToolingCore", + "libclangTooling", "libclangFrontendTool", "libclangFrontend", "libclangDriver", @@ -134,8 +137,8 @@ cc_binary_host { ], static_libs: [ - "libheader-checker-proto", "libheader-abi-util", + "libheader-checker-proto", ], target: { @@ -269,8 +272,14 @@ cc_library_static { "libLLVMMCParser", "libLLVMCore", "libLLVMSupport", + "libheader-checker-proto", ], + shared_libs: [ + "libprotobuf-cpp-full", + ], + + cflags: [ "-Wcast-qual", "-Wno-long-long", diff --git a/vndk/tools/header-checker/README.md b/vndk/tools/header-checker/README.md index 9412f60ca..242786a95 100644 --- a/vndk/tools/header-checker/README.md +++ b/vndk/tools/header-checker/README.md @@ -7,6 +7,7 @@ ## Usage header-abi-dumper -o -I -I .. -- + For options : header-abi-dumper --help # VNDK Header Abi Linker @@ -16,6 +17,7 @@ ## Usage header-abi-linker -o ... + For options : header-abi-linker --help # VNDK Header Abi Diff @@ -24,10 +26,13 @@ abi's exposed by the two dumps. # Return Value - 1: InCompatible - 0: Compatible or Compatible Extension. + 0: Compatible + 1: Changes to APIs unreferenced by symbols in the .dynsym table + 4: Compatible Extension + 8: Incompatible ## Usage header-abi-diff -old -new -o + For options : header-abi-diff --help diff --git a/vndk/tools/header-checker/header-abi-diff/src/abi_diff.cpp b/vndk/tools/header-checker/header-abi-diff/src/abi_diff.cpp index 45aac6416..7d21566d8 100644 --- a/vndk/tools/header-checker/header-abi-diff/src/abi_diff.cpp +++ b/vndk/tools/header-checker/header-abi-diff/src/abi_diff.cpp @@ -14,169 +14,262 @@ #include "abi_diff.h" +#include + #include #include #include #include -#include -#include #include #include #include -CompatibilityStatus HeaderAbiDiff::GenerateCompatibilityReport() { - abi_dump::TranslationUnit old_tu; - abi_dump::TranslationUnit new_tu; - std::ifstream old_input(old_dump_); - std::ifstream new_input(new_dump_); - google::protobuf::io::IstreamInputStream text_iso(&old_input); - google::protobuf::io::IstreamInputStream text_isn(&new_input); - - if (!google::protobuf::TextFormat::Parse(&text_iso, &old_tu) || - !google::protobuf::TextFormat::Parse(&text_isn, &new_tu)) { - llvm::errs() << "Failed to generate compatibility report\n"; +abi_util::CompatibilityStatusIR HeaderAbiDiff::GenerateCompatibilityReport() { + using abi_util::TextFormatToIRReader; + std::unique_ptr old_reader = + TextFormatToIRReader::CreateTextFormatToIRReader("protobuf", old_dump_); + std::unique_ptr new_reader = + TextFormatToIRReader::CreateTextFormatToIRReader("protobuf", new_dump_); + if (!old_reader || !new_reader || !old_reader->ReadDump() || + !new_reader->ReadDump()) { + llvm::errs() << "Could not create Text Format readers\n"; ::exit(1); } - return CompareTUs(old_tu, new_tu); + std::unique_ptr ir_diff_dumper = + abi_util::IRDiffDumper::CreateIRDiffDumper("protobuf", cr_); + abi_util::CompatibilityStatusIR status = + CompareTUs(old_reader.get(), new_reader.get(), ir_diff_dumper.get()); + if (!ir_diff_dumper->Dump()) { + llvm::errs() << "Could not dump diff report\n"; + ::exit(1); + } + return status; } -CompatibilityStatus HeaderAbiDiff::CompareTUs( - const abi_dump::TranslationUnit &old_tu, - const abi_dump::TranslationUnit &new_tu) { - std::unique_ptr diff_tu( - new abi_diff::TranslationUnitDiff); - CompatibilityStatus record_status = Collect( - diff_tu->mutable_records_added(), diff_tu->mutable_records_removed(), - diff_tu->mutable_records_diff(), old_tu.records(), new_tu.records(), - ignored_symbols_); +template +static void AddTypesToMap(std::map *dst, + const abi_util::TextFormatToIRReader *tu, F func) { + AddToMap(dst, tu->GetRecordTypes(), func); + AddToMap(dst, tu->GetEnumTypes(), func); + AddToMap(dst, tu->GetPointerTypes(), func); + AddToMap(dst, tu->GetBuiltinTypes(), func); + AddToMap(dst, tu->GetArrayTypes(), func); + AddToMap(dst, tu->GetLvalueReferenceTypes(), func); + AddToMap(dst, tu->GetRvalueReferenceTypes(), func); + AddToMap(dst, tu->GetQualifiedTypes(), func); +} - CompatibilityStatus function_status = Collect( - diff_tu->mutable_functions_added(), diff_tu->mutable_functions_removed(), - diff_tu->mutable_functions_diff(), old_tu.functions(), - new_tu.functions(), ignored_symbols_); +abi_util::CompatibilityStatusIR HeaderAbiDiff::CompareTUs( + const abi_util::TextFormatToIRReader *old_tu, + const abi_util::TextFormatToIRReader *new_tu, + abi_util::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. + std::map old_types; + std::map new_types; + AddTypesToMap(&old_types, old_tu, + [](const abi_util::TypeIR *e) {return e->GetLinkerSetKey();}); + AddTypesToMap(&new_types, new_tu, + [](const abi_util::TypeIR *e) {return e->GetLinkerSetKey();}); - CompatibilityStatus enum_status = Collect( - diff_tu->mutable_enums_added(), diff_tu->mutable_enums_removed(), - diff_tu->mutable_enums_diff(), old_tu.enums(), new_tu.enums(), - ignored_symbols_); - - CompatibilityStatus global_var_status = Collect( - diff_tu->mutable_global_vars_added(), - diff_tu->mutable_global_vars_removed(), - diff_tu->mutable_global_vars_diff(), old_tu.global_vars(), - new_tu.global_vars(), ignored_symbols_); - - CompatibilityStatus combined_status = - record_status | function_status | enum_status | global_var_status; - - if (combined_status & CompatibilityStatus::INCOMPATIBLE) { - combined_status = CompatibilityStatus::INCOMPATIBLE; - } else if (combined_status & CompatibilityStatus::EXTENSION) { - combined_status = CompatibilityStatus::EXTENSION; - } else { - combined_status = CompatibilityStatus::COMPATIBLE; - } - diff_tu->set_compatibility_status(combined_status); - diff_tu->set_lib_name(lib_name_); - diff_tu->set_arch(arch_); - std::ofstream text_output(cr_); - google::protobuf::io::OstreamOutputStream text_os(&text_output); - - if(!google::protobuf::TextFormat::Print(*diff_tu, &text_os)) { - llvm::errs() << "Unable to dump report\n"; + // Collect 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); + } + + abi_util::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; } -template -abi_diff::CompatibilityStatus HeaderAbiDiff::Collect( - google::protobuf::RepeatedPtrField *elements_added, - google::protobuf::RepeatedPtrField *elements_removed, - google::protobuf::RepeatedPtrField *elements_diff, - const google::protobuf::RepeatedPtrField &old_srcs, - const google::protobuf::RepeatedPtrField &new_srcs, - const std::set &ignored_symbols) { - assert(elements_added != nullptr); - assert(elements_removed != nullptr); - assert(elements_diff != nullptr); +bool HeaderAbiDiff::CollectUserDefinedTypes( + const abi_util::TextFormatToIRReader *old_tu, + const abi_util::TextFormatToIRReader *new_tu, + const std::map &old_types_map, + const std::map &new_types_map, + abi_util::IRDiffDumper *ir_diff_dumper) { + return CollectUserDefinedTypesInternal( + old_tu->GetRecordTypes(), new_tu->GetRecordTypes(), old_types_map, + new_types_map, ir_diff_dumper) && + CollectUserDefinedTypesInternal(old_tu->GetEnumTypes(), + new_tu->GetEnumTypes(), old_types_map, + new_types_map, ir_diff_dumper); +} - std::map old_elements_map; - std::map new_elements_map; - AddToMap(&old_elements_map, old_srcs); - AddToMap(&new_elements_map, new_srcs); +template +bool HeaderAbiDiff::CollectUserDefinedTypesInternal( + const std::vector &old_ud_types, + const std::vector &new_ud_types, + const std::map &old_types_map, + const std::map &new_types_map, + abi_util::IRDiffDumper *ir_diff_dumper) { + // No elf information for records and enums. + std::map old_ud_types_map; + std::map new_ud_types_map; - if (!PopulateRemovedElements(elements_removed, old_elements_map, - new_elements_map, ignored_symbols) || - !PopulateRemovedElements(elements_added, new_elements_map, - old_elements_map, ignored_symbols) || - !PopulateCommonElements(elements_diff, old_elements_map, - new_elements_map, ignored_symbols)) { + abi_util::AddToMap(&old_ud_types_map, old_ud_types, + [](const T *e) + { return e->GetLinkerSetKey();}); + + abi_util::AddToMap(&new_ud_types_map, new_ud_types, + [](const T *e) + { return e->GetLinkerSetKey();}); + + return Collect(old_ud_types_map, new_ud_types_map, nullptr, nullptr, + ir_diff_dumper) && + PopulateCommonElements(old_ud_types_map, new_ud_types_map, old_types_map, + new_types_map, ir_diff_dumper, + abi_util::IRDiffDumper::Unreferenced); +} + +template +bool HeaderAbiDiff::CollectDynsymExportables( + const std::vector &old_exportables, + const std::vector &new_exportables, + const std::vector &old_elf_symbols, + const std::vector &new_elf_symbols, + const std::map &old_types_map, + const std::map &new_types_map, + abi_util::IRDiffDumper *ir_diff_dumper) { + std::map old_exportables_map; + std::map new_exportables_map; + std::map old_elf_symbol_map; + std::map new_elf_symbol_map; + + abi_util::AddToMap(&old_exportables_map, old_exportables, + [](const T *e) + { return e->GetLinkerSetKey();}); + abi_util::AddToMap(&new_exportables_map, new_exportables, + [](const T *e) + { return e->GetLinkerSetKey();}); + abi_util::AddToMap( + &old_elf_symbol_map, old_elf_symbols, + [](const ElfSymbolType *symbol) { return symbol->GetName();}); + abi_util::AddToMap( + &new_elf_symbol_map, new_elf_symbols, + [](const ElfSymbolType *symbol) { return symbol->GetName();}); + + if (!Collect(old_exportables_map, + new_exportables_map, &old_elf_symbol_map, &new_elf_symbol_map, + ir_diff_dumper) || + !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, + abi_util::IRDiffDumper::Referenced)) { + llvm::errs() << "Diffing dynsym exportables failed\n"; + return false; + } + return true; +} + +// Collect added and removed Elements. The elf set is needed since some symbols +// might not have meta-data about them collected through the AST. For eg: if a +// function Foo is defined in an assembly file on target A, but in a c/c++ file +// on target B, foo does not have meta-data surrounding it when building target +// A, this does not mean it is not in the ABI + API of the library. + +template +bool HeaderAbiDiff::Collect( + const std::map &old_elements_map, + const std::map &new_elements_map, + const std::map *old_elf_map, + const std::map *new_elf_map, + abi_util::IRDiffDumper *ir_diff_dumper) { + if (!PopulateRemovedElements( + old_elements_map, new_elements_map, new_elf_map, ir_diff_dumper, + abi_util::IRDiffDumper::Removed) || + !PopulateRemovedElements(new_elements_map, old_elements_map, old_elf_map, + ir_diff_dumper, + abi_util::IRDiffDumper::DiffKind::Added)) { llvm::errs() << "Populating functions in report failed\n"; - ::exit(1); + return false; } - if (elements_diff->size() || elements_removed->size()) { - return CompatibilityStatus::INCOMPATIBLE; + return true; +} + +bool HeaderAbiDiff::CollectElfSymbols( + const std::map &old_symbols, + const std::map &new_symbols, + abi_util::IRDiffDumper *ir_diff_dumper) { + std::vector removed_elements = + abi_util::FindRemovedElements(old_symbols, new_symbols); + + std::vector added_elements = + abi_util::FindRemovedElements(new_symbols, old_symbols); + + return PopulateElfElements(removed_elements, ir_diff_dumper, + abi_util::IRDiffDumper::DiffKind::Removed) && + PopulateElfElements(added_elements, ir_diff_dumper, + abi_util::IRDiffDumper::DiffKind::Added); +} + +bool HeaderAbiDiff::PopulateElfElements( + std::vector &elf_elements, + abi_util::IRDiffDumper *ir_diff_dumper, + abi_util::IRDiffDumper::DiffKind diff_kind) { + for (auto &&elf_element : elf_elements) { + if (!ir_diff_dumper->AddElfSymbolMessageIR(elf_element, diff_kind)) { + return false; + } } - if (elements_added->size()) { - return CompatibilityStatus::EXTENSION; - } - return CompatibilityStatus::COMPATIBLE; + return true; } template bool HeaderAbiDiff::PopulateRemovedElements( - google::protobuf::RepeatedPtrField *dst, const std::map &old_elements_map, const std::map &new_elements_map, - const std::set &ignored_symbols) { - - std::vector removed_elements; - for (auto &&map_element : old_elements_map) { - const T *element = map_element.second; - auto new_element = - new_elements_map.find(element->basic_abi().linker_set_key()); - if (new_element == new_elements_map.end()) { - removed_elements.emplace_back(element); - } - } - if (!DumpLoneElements(dst, removed_elements, ignored_symbols)) { + const std::map *elf_map, + abi_util::IRDiffDumper *ir_diff_dumper, + abi_util::IRDiffDumper::DiffKind diff_kind) { + std::vector removed_elements = + abi_util::FindRemovedElements(old_elements_map, new_elements_map); + if (!DumpLoneElements(removed_elements, elf_map, ir_diff_dumper, diff_kind)) { llvm::errs() << "Dumping added / removed element to report failed\n"; return false; } return true; } -template +template bool HeaderAbiDiff::PopulateCommonElements( - google::protobuf::RepeatedPtrField *dst, const std::map &old_elements_map, const std::map &new_elements_map, - const std::set &ignored_symbols) { - std::vector> common_elements; - typename std::map::const_iterator old_element = - old_elements_map.begin(); - typename std::map::const_iterator new_element = - new_elements_map.begin(); - while (old_element != old_elements_map.end() && - new_element != new_elements_map.end()) { - if (old_element->first == new_element->first) { - common_elements.emplace_back(std::make_pair( - old_element->second, new_element->second)); - old_element++; - new_element++; - continue; - } - if (old_element->first < new_element->first) { - old_element++; - } else { - new_element++; - } - } - if (!DumpDiffElements(dst, common_elements, ignored_symbols)) { + const std::map &old_types, + const std::map &new_types, + abi_util::IRDiffDumper *ir_diff_dumper, + abi_util::IRDiffDumper::DiffKind diff_kind) { + std::vector> common_elements = + abi_util::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; } @@ -185,48 +278,62 @@ bool HeaderAbiDiff::PopulateCommonElements( template bool HeaderAbiDiff::DumpLoneElements( - google::protobuf::RepeatedPtrField *dst, std::vector &elements, - const std::set &ignored_symbols) { + const std::map *elf_map, + abi_util::IRDiffDumper *ir_diff_dumper, + abi_util::IRDiffDumper::DiffKind diff_kind) { + // If the record / enum has source file information, skip it. + std::smatch source_file_match; + std::regex source_file_regex(" at "); for (auto &&element : elements) { - if (abi_diff_wrappers::IgnoreSymbol(element, ignored_symbols)) { + if (abi_diff_wrappers::IgnoreSymbol( + element, ignored_symbols_, + [](const T *e) {return e->GetLinkerSetKey();})) { continue; } - T *added_element = dst->Add(); - if (!added_element) { - llvm::errs() << "Adding element diff failed\n"; + // The element does exist in the .dynsym table, we do not have meta-data + // surrounding the element. + const std::string &element_linker_set_key = element->GetLinkerSetKey(); + if ((elf_map != nullptr) && + (elf_map->find(element_linker_set_key) != elf_map->end())) { + continue; + } + if (std::regex_search(element_linker_set_key, source_file_match, + source_file_regex)) { + continue; + } + if (!ir_diff_dumper->AddLinkableMessageIR(element, diff_kind)) { + llvm::errs() << "Couldn't dump added /removed element\n"; return false; } - *added_element = *element; } return true; } -template + +template bool HeaderAbiDiff::DumpDiffElements( - google::protobuf::RepeatedPtrField *dst, std::vector> &pairs, - const std::set &ignored_symbols) { + const std::map &old_types, + const std::map &new_types, + abi_util::IRDiffDumper *ir_diff_dumper, + abi_util::IRDiffDumper::DiffKind diff_kind) { for (auto &&pair : pairs) { const T *old_element = pair.first; const T *new_element = pair.second; - // Not having inheritance from protobuf messages makes this - // restrictive code. - if (abi_diff_wrappers::IgnoreSymbol(old_element, ignored_symbols)) { + + if (abi_diff_wrappers::IgnoreSymbol( + old_element, ignored_symbols_, + [](const T *e) {return e->GetLinkerSetKey();})) { continue; } - abi_diff_wrappers::DiffWrapper diff_wrapper(old_element, - new_element); - std::unique_ptr decl_diff_ptr = diff_wrapper.Get(); - if (!decl_diff_ptr) { - continue; - } - TDiff *added_element_diff = dst->Add(); - if (!added_element_diff) { - llvm::errs() << "Adding element diff failed\n"; + abi_diff_wrappers::DiffWrapper diff_wrapper(old_element, new_element, + ir_diff_dumper, old_types, + new_types, &type_cache_); + if (!diff_wrapper.DumpDiff(diff_kind)) { + llvm::errs() << "Failed to diff elements\n"; return false; } - *added_element_diff = *decl_diff_ptr; } return true; } diff --git a/vndk/tools/header-checker/header-abi-diff/src/abi_diff.h b/vndk/tools/header-checker/header-abi-diff/src/abi_diff.h index df8a6d15b..3cabc8bdb 100644 --- a/vndk/tools/header-checker/header-abi-diff/src/abi_diff.h +++ b/vndk/tools/header-checker/header-abi-diff/src/abi_diff.h @@ -14,6 +14,8 @@ #include "abi_diff_wrappers.h" +#include + #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-parameter" #pragma clang diagnostic ignored "-Wnested-anon-types" @@ -21,68 +23,102 @@ #include "proto/abi_diff.pb.h" #pragma clang diagnostic pop -#include -#include -#include #include #include - -typedef abi_diff::CompatibilityStatus CompatibilityStatus; - class HeaderAbiDiff { public: HeaderAbiDiff(const std::string &lib_name, const std::string &arch, const std::string &old_dump, const std::string &new_dump, const std::string &compatibility_report, - const std::set &ignored_symbols) + const std::set &ignored_symbols, + bool check_all_apis) : lib_name_(lib_name), arch_(arch), old_dump_(old_dump), new_dump_(new_dump), cr_(compatibility_report), - ignored_symbols_(ignored_symbols) { } + ignored_symbols_(ignored_symbols), check_all_apis_(check_all_apis) { } - CompatibilityStatus GenerateCompatibilityReport(); + abi_util::CompatibilityStatusIR GenerateCompatibilityReport(); private: - CompatibilityStatus CompareTUs(const abi_dump::TranslationUnit &old_tu, - const abi_dump::TranslationUnit &new_tu); - // Collect* methods fill in the diff_tu. - template - static CompatibilityStatus Collect( - google::protobuf::RepeatedPtrField *elements_added, - google::protobuf::RepeatedPtrField *elements_removed, - google::protobuf::RepeatedPtrField *elements_diff, - const google::protobuf::RepeatedPtrField &old_srcs, - const google::protobuf::RepeatedPtrField &new_srcs, - const std::set &ignored_symbols); + abi_util::CompatibilityStatusIR CompareTUs( + const abi_util::TextFormatToIRReader *old_tu, + const abi_util::TextFormatToIRReader *new_tu, + abi_util::IRDiffDumper *ir_diff_dumper); + + template + bool CollectDynsymExportables( + const std::vector &old_exportables, + const std::vector &new_exportables, + const std::vector &old_elf_symbols, + const std::vector &new_elf_symbols, + const std::map &old_types_map, + const std::map &new_types_map, + abi_util::IRDiffDumper *ir_diff_dumper); template - static inline void AddToMap(std::map *dst, - const google::protobuf::RepeatedPtrField &src); + bool Collect( + const std::map &old_elements_map, + const std::map &new_elements_map, + const std::map *old_elf_map, + const std::map *new_elf_map, + abi_util::IRDiffDumper *ir_diff_dumper); + + bool CollectElfSymbols( + const std::map &old_symbols, + const std::map &new_symbols, + abi_util::IRDiffDumper *ir_diff_dumper); + + bool PopulateElfElements( + std::vector &elf_elements, + abi_util::IRDiffDumper *ir_diff_dumper, + abi_util::IRDiffDumper::DiffKind diff_kind); template - static bool PopulateRemovedElements( - google::protobuf::RepeatedPtrField *dst, + bool PopulateRemovedElements( const std::map &old_elements_map, const std::map &new_elements_map, - const std::set &ignored_symbols); + const std::map *elf_map, + abi_util::IRDiffDumper *ir_diff_dumper, + abi_util::IRDiffDumper::DiffKind diff_kind); - template - static bool PopulateCommonElements( - google::protobuf::RepeatedPtrField *dst, + template + bool PopulateCommonElements( const std::map &old_elements_map, const std::map &new_elements_map, - const std::set &ignored_symbols); + const std::map &old_types, + const std::map &new_types, + abi_util::IRDiffDumper *ir_diff_dumper, + abi_util::IRDiffDumper::DiffKind diff_kind); - template - static bool DumpDiffElements( - google::protobuf::RepeatedPtrField *dst, + template + bool DumpDiffElements( std::vector> &pairs, - const std::set &ignored_symbols); + const std::map &old_types, + const std::map &new_types, + abi_util::IRDiffDumper *ir_diff_dumper, + abi_util::IRDiffDumper::DiffKind diff_kind); template - static bool DumpLoneElements(google::protobuf::RepeatedPtrField *dst, - std::vector &elements, - const std::set &ignored_symbols); + bool DumpLoneElements( + std::vector &elements, + const std::map *elf_map, + abi_util::IRDiffDumper *ir_diff_dumper, + abi_util::IRDiffDumper::DiffKind diff_kind); + + bool CollectUserDefinedTypes( + const abi_util::TextFormatToIRReader *old_tu, + const abi_util::TextFormatToIRReader *new_tu, + const std::map &old_types_map, + const std::map &new_types_map, + abi_util::IRDiffDumper *ir_diff_dumper); + + template + bool CollectUserDefinedTypesInternal( + const std::vector &old_ud_types, + const std::vector &new_ud_types, + const std::map &old_types_map, + const std::map &new_types_map, + abi_util::IRDiffDumper *ir_diff_dumper); private: const std::string &lib_name_; @@ -91,27 +127,6 @@ class HeaderAbiDiff { const std::string &new_dump_; const std::string &cr_; const std::set &ignored_symbols_; + bool check_all_apis_; + std::set type_cache_; }; - -template -inline void HeaderAbiDiff::AddToMap( - std::map *dst, - const google::protobuf::RepeatedPtrField &src) { - for (auto &&element : src) { - dst->insert(std::make_pair(element.basic_abi().linker_set_key(), &element)); - } -} - -static inline CompatibilityStatus operator|(CompatibilityStatus f, - CompatibilityStatus s) { - return static_cast( - static_cast::type>(f) | - static_cast::type>(s)); -} - -static inline CompatibilityStatus operator&( - CompatibilityStatus f, CompatibilityStatus s) { - return static_cast( - static_cast::type>(f) & - static_cast::type>(s)); -} diff --git a/vndk/tools/header-checker/header-abi-diff/src/abi_diff_wrappers.cpp b/vndk/tools/header-checker/header-abi-diff/src/abi_diff_wrappers.cpp index e75d4ce55..e9a0047cb 100644 --- a/vndk/tools/header-checker/header-abi-diff/src/abi_diff_wrappers.cpp +++ b/vndk/tools/header-checker/header-abi-diff/src/abi_diff_wrappers.cpp @@ -14,52 +14,23 @@ #include "abi_diff_wrappers.h" -#include - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunused-parameter" -#pragma clang diagnostic ignored "-Wnested-anon-types" -#include "proto/abi_dump.pb.h" -#include "proto/abi_diff.pb.h" -#pragma clang diagnostic pop +#include #include -using abi_diff::RecordDeclDiff; -using abi_diff::RecordFieldDeclDiff; -using abi_diff::CXXBaseSpecifierDiff; -using abi_diff::CXXVTableDiff; -using abi_diff::EnumDeclDiff; -using abi_diff::ReturnTypeDiff; -using abi_diff::ParamDeclDiff; -using abi_diff::FunctionDeclDiff; -using abi_diff::EnumDeclDiff; -using abi_diff::EnumFieldDeclDiff; -using abi_diff::GlobalVarDeclDiff; -using abi_dump::RecordDecl; -using abi_dump::RecordFieldDecl; -using abi_dump::EnumDecl; -using abi_dump::EnumFieldDecl; -using abi_dump::FunctionDecl; -using abi_dump::ParamDecl; -using abi_dump::VTableComponent; -using abi_dump::CXXBaseSpecifier; -using abi_dump::GlobalVarDecl; -using abi_dump::BasicNamedAndTypedDecl; - namespace abi_diff_wrappers { -static bool IsAccessDownGraded(abi_dump::AccessSpecifier old_access, - abi_dump::AccessSpecifier new_access) { +static bool IsAccessDownGraded(abi_util::AccessSpecifierIR old_access, + abi_util::AccessSpecifierIR new_access) { bool access_downgraded = false; switch (old_access) { - case abi_dump::AccessSpecifier::protected_access: - if (new_access == abi_dump::AccessSpecifier::private_access) { + case abi_util::AccessSpecifierIR::ProtectedAccess: + if (new_access == abi_util::AccessSpecifierIR::PrivateAccess) { access_downgraded = true; } break; - case abi_dump::AccessSpecifier::public_access: - if (new_access != abi_dump::AccessSpecifier::public_access) { + case abi_util::AccessSpecifierIR::PublicAccess: + if (new_access != abi_util::AccessSpecifierIR::PublicAccess) { access_downgraded = true; } break; @@ -69,255 +40,590 @@ static bool IsAccessDownGraded(abi_dump::AccessSpecifier old_access, return access_downgraded; } -static std::string CpptoCAdjustment(const std::string &type) { - std::string adjusted_type_name = - abi_util::FindAndReplace(type, "\\bstruct ", ""); - - return adjusted_type_name; +static std::string Unwind(const std::deque *type_queue) { + if (!type_queue) { + return ""; + } + std::string stack_str; + std::deque type_queue_copy = *type_queue; + while (!type_queue_copy.empty()) { + stack_str += type_queue_copy.front() + "-> "; + type_queue_copy.pop_front(); + } + return stack_str; } -static bool CompareTypeNames(const abi_dump::BasicTypeAbi &old_abi, - const abi_dump::BasicTypeAbi &new_abi) { - // Strip of leading 'struct' keyword from type names - std::string old_type = old_abi.name(); - std::string new_type = new_abi.name(); - old_type = CpptoCAdjustment(old_type); - new_type = CpptoCAdjustment(new_type); - // TODO: Add checks for C++ built-in types vs C corresponding types. - return old_type != new_type; +void DiffWrapperBase::CompareEnumFields( + const std::vector &old_fields, + const std::vector &new_fields, + abi_util::EnumTypeDiffIR *enum_type_diff_ir) { + std::map old_fields_map; + std::map new_fields_map; + abi_util::AddToMap(&old_fields_map, old_fields, + [](const abi_util::EnumFieldIR *f) + {return f->GetName();}); + abi_util::AddToMap(&new_fields_map, new_fields, + [](const abi_util::EnumFieldIR *f) + {return f->GetName();}); + std::vector removed_fields = + abi_util::FindRemovedElements(old_fields_map, new_fields_map); + + std::vector added_fields = + abi_util::FindRemovedElements(new_fields_map, old_fields_map); + + enum_type_diff_ir->SetFieldsAdded(std::move(added_fields)); + + enum_type_diff_ir->SetFieldsRemoved(std::move(removed_fields)); + + std::vector> cf = + abi_util::FindCommonElements(old_fields_map, new_fields_map); + std::vector enum_field_diffs; + for (auto &&common_fields : cf) { + if (common_fields.first->GetValue() != common_fields.second->GetValue()) { + abi_util::EnumFieldDiffIR enum_field_diff_ir(common_fields.first, + common_fields.second); + enum_field_diffs.emplace_back(std::move(enum_field_diff_ir)); + } + } + enum_type_diff_ir->SetFieldsDiff(std::move(enum_field_diffs)); } -static bool DiffBasicTypeAbi(const abi_dump::BasicTypeAbi &old_abi, - const abi_dump::BasicTypeAbi &new_abi) { - // We need to add a layer of indirection to account for issues when C and C++ - // are mixed. For example some types like wchar_t are in-built types for C++ - // but not for C. Another example would be clang reporting C structures - // without the leading "struct" keyword when headers defining them are - // included in C++ files. - bool name_comparison = CompareTypeNames(old_abi, new_abi); - bool size_comparison = (old_abi.size() != new_abi.size()); - bool alignment_comparison = (old_abi.alignment() != new_abi.alignment()); - return name_comparison || size_comparison || alignment_comparison; +DiffStatus DiffWrapperBase::CompareEnumTypes( + const abi_util::EnumTypeIR *old_type, const abi_util::EnumTypeIR *new_type, + std::deque *type_queue, + abi_util::IRDiffDumper::DiffKind diff_kind) { + if (old_type->GetName() != new_type->GetName()) { + return DiffStatus::direct_diff; + } + auto enum_type_diff_ir = std::make_unique(); + enum_type_diff_ir->SetName(old_type->GetName()); + const std::string &old_underlying_type = old_type->GetUnderlyingType(); + const std::string &new_underlying_type = new_type->GetUnderlyingType(); + if (old_underlying_type != new_underlying_type) { + enum_type_diff_ir->SetUnderlyingTypeDiff( + std::make_unique>( + old_underlying_type, new_underlying_type)); + } + CompareEnumFields(old_type->GetFields(), new_type->GetFields(), + enum_type_diff_ir.get()); + if ((enum_type_diff_ir->IsExtended() || + enum_type_diff_ir->IsIncompatible()) && + !ir_diff_dumper_->AddDiffMessageIR(enum_type_diff_ir.get(), + Unwind(type_queue), diff_kind)) { + llvm::errs() << "AddDiffMessage on EnumTypeDiffIR failed\n"; + ::exit(1); + } + return DiffStatus::no_diff; } -template -static bool Diff(const T &old_element, const T &new_element) { - // Can be specialized for future changes in the format. - return DiffBasicTypeAbi(old_element.basic_abi().type_abi(), - new_element.basic_abi().type_abi()) || - (old_element.basic_abi().name() != new_element.basic_abi().name()) || - IsAccessDownGraded(old_element.basic_abi().access(), - new_element.basic_abi().access()); +bool DiffWrapperBase::CompareVTableComponents( + const abi_util::VTableComponentIR &old_component, + const abi_util::VTableComponentIR &new_component) { + return old_component.GetName() == new_component.GetName() && + old_component.GetValue() == new_component.GetValue() && + old_component.GetKind() == new_component.GetKind(); } -template <> -bool Diff(const EnumFieldDecl &old_element, - const EnumFieldDecl &new_element) { - // Can be specialized for future changes in the format. - return DiffBasicTypeAbi(old_element.basic_abi().type_abi(), - new_element.basic_abi().type_abi()) || - (old_element.enum_field_value() != new_element.enum_field_value()) || - (old_element.basic_abi().name() != new_element.basic_abi().name()); +bool DiffWrapperBase::CompareVTables( + const abi_util::RecordTypeIR *old_record, + const abi_util::RecordTypeIR *new_record) { + + const std::vector &old_components = + old_record->GetVTableLayout().GetVTableComponents(); + const std::vector &new_components = + new_record->GetVTableLayout().GetVTableComponents(); + if (old_components.size() > new_components.size()) { + // Something in the vtable got deleted. + return false; + } + uint32_t i = 0; + while (i < old_components.size()) { + auto &old_element = old_components.at(i); + auto &new_element = new_components.at(i); + if (!CompareVTableComponents(old_element, new_element)) { + return false; + } + i++; + } + return true; } -template <> -bool Diff(const ParamDecl &old_element, - const ParamDecl &new_element) { - // Can be specialized for future changes in the format. - return DiffBasicTypeAbi(old_element.basic_abi().type_abi(), - new_element.basic_abi().type_abi()); +bool DiffWrapperBase::CompareSizeAndAlignment( + const abi_util::TypeIR *old_type, + const abi_util::TypeIR *new_type) { + return old_type->GetSize() == new_type->GetSize() && + old_type->GetAlignment() == new_type->GetAlignment(); } -template <> -bool Diff(const CXXBaseSpecifier &old_element, - const CXXBaseSpecifier &new_element) { - // Can be specialized for future changes in the format. - return (DiffBasicTypeAbi(old_element.basic_abi().type_abi(), - new_element.basic_abi().type_abi()) || - old_element.basic_abi().access() != new_element.basic_abi().access() || - old_element.is_virtual() != new_element.is_virtual()); +std::unique_ptr +DiffWrapperBase::CompareCommonRecordFields( + const abi_util::RecordFieldIR *old_field, + const abi_util::RecordFieldIR *new_field, + std::deque *type_queue, + abi_util::IRDiffDumper::DiffKind diff_kind) { + if (old_field->GetOffset() != new_field->GetOffset() || + // TODO: Should this be an inquality check instead ? Some compilers can + // make signatures dependant on absolute values of access specifiers. + IsAccessDownGraded(old_field->GetAccess(), new_field->GetAccess()) || + CompareAndDumpTypeDiff(old_field->GetReferencedType(), + new_field->GetReferencedType(), + type_queue, diff_kind) == + DiffStatus::direct_diff) { + return std::make_unique(old_field, new_field); + } + return nullptr; } -template <> -bool Diff(const VTableComponent &old_element, - const VTableComponent &new_element) { - bool kind_comparison = old_element.kind() != new_element.kind(); - bool mangled_name_comparison = old_element.mangled_component_name() != - new_element.mangled_component_name(); - bool value_comparison = - old_element.component_value() != new_element.component_value(); - return kind_comparison || mangled_name_comparison || value_comparison; +std::pair, + std::vector> +DiffWrapperBase::CompareRecordFields( + const std::vector &old_fields, + const std::vector &new_fields, + std::deque *type_queue, + abi_util::IRDiffDumper::DiffKind diff_kind) { + std::pair, + std::vector> diffed_and_removed_fields; + std::map old_fields_map; + std::map new_fields_map; + std::map old_fields_offset_map; + std::map new_fields_offset_map; + + abi_util::AddToMap(&old_fields_map, old_fields, + [](const abi_util::RecordFieldIR *f) + {return f->GetName();}); + abi_util::AddToMap(&new_fields_map, new_fields, + [](const abi_util::RecordFieldIR *f) + {return f->GetName();}); + abi_util::AddToMap(&old_fields_offset_map, old_fields, + [](const abi_util::RecordFieldIR *f) + {return f->GetOffset();}); + abi_util::AddToMap(&new_fields_offset_map, new_fields, + [](const abi_util::RecordFieldIR *f) + {return f->GetOffset();}); + // If a field is removed from the map field_name -> offset see if another + // field is present at the same offset and compare the size and type etc, + // remove it from the removed fields if they're compatible. + std::vector removed_fields = + abi_util::FindRemovedElements(old_fields_map, new_fields_map); + uint32_t i = 0; + for (auto &&removed_field : removed_fields) { + // For the removed field, get the corresponding offset from the old map. + // Compare the fields from old map and new map if there's a direct diff, + // continue, otherwise remove that field from the removed fields map. + uint64_t old_field_offset = removed_field->GetOffset(); + auto corresponding_field_at_same_offset = + new_fields_offset_map.find(old_field_offset); + // Correctly reported as removed. + if (corresponding_field_at_same_offset == new_fields_offset_map.end()) { + continue; + } + auto comparison_result = CompareCommonRecordFields( + removed_field, corresponding_field_at_same_offset->second, + type_queue, diff_kind); + if (comparison_result != nullptr) { + continue; + } + removed_fields.erase(removed_fields.begin() + i); + i++; + } + diffed_and_removed_fields.second = std::move(removed_fields); + std::vector> cf = + abi_util::FindCommonElements(old_fields_map, new_fields_map); + for (auto &&common_fields : cf) { + std::unique_ptr diffed_field_ptr = + CompareCommonRecordFields(common_fields.first, common_fields.second, + type_queue, diff_kind); + if (diffed_field_ptr != nullptr) { + diffed_and_removed_fields.first.emplace_back( + std::move(*(diffed_field_ptr.release()))); + } + } + return diffed_and_removed_fields; } -// This function fills in a *Diff Message's repeated field. For eg: -// RecordDeclDiff's CXXBaseSpecifierDiff fields and well as FieldDeclDiff -// fields. -template -template -bool DiffWrapperBase::GetElementDiffs( - google::protobuf::RepeatedPtrField *dst, - const google::protobuf::RepeatedPtrField &old_elements, - const google::protobuf::RepeatedPtrField &new_elements) { - bool differs = false; - assert(dst != nullptr); +bool DiffWrapperBase::CompareBaseSpecifiers( + const std::vector &old_base_specifiers, + const std::vector &new_base_specifiers, + std::deque *type_queue, + abi_util::IRDiffDumper::DiffKind diff_kind) { + if (old_base_specifiers.size() != new_base_specifiers.size()) { + return false; + } int i = 0; - int j = 0; - while (i < old_elements.size() && j < new_elements.size()) { - const Element &old_element = old_elements.Get(i); - const Element &new_element = new_elements.Get(i); - - if (Diff(old_element, new_element)) { - ElementDiff *diff = dst->Add(); - Element *old_elementp = nullptr; - Element *new_elementp = nullptr; - if (!diff || !(old_elementp = diff->mutable_old()) || - !(new_elementp = diff->mutable_new_())) { - llvm::errs() << "Failed to add diff element\n"; - ::exit(1); - } - *old_elementp = old_element; - *new_elementp = new_element; - diff->set_index(i); - differs = true; + while (i < old_base_specifiers.size()) { + if (CompareAndDumpTypeDiff(old_base_specifiers.at(i).GetReferencedType(), + new_base_specifiers.at(i).GetReferencedType(), + type_queue, diff_kind) == + DiffStatus::direct_diff || + (old_base_specifiers.at(i).GetAccess() != + new_base_specifiers.at(i).GetAccess())) { + return false; } i++; - j++; } - if (old_elements.size() != new_elements.size()) { - GetExtraElementDiffs(dst, i, j, old_elements, new_elements); - differs = true; - } - return differs; + return true; } -template -template -void DiffWrapperBase::GetExtraElementDiffs( - google::protobuf::RepeatedPtrField *dst, int i, int j, - const google::protobuf::RepeatedPtrField &old_elements, - const google::protobuf::RepeatedPtrField &new_elements) { - assert(dst != nullptr); - while (i < old_elements.size()) { - const Element &old_element = old_elements.Get(i); - ElementDiff *diff = dst->Add(); - Element *old_elementp = nullptr; - if (!diff || !(old_elementp = diff->mutable_old())) { - llvm::errs() << "Failed to add diff element\n"; - ::exit(1); - } - *old_elementp = old_element; - diff->set_index(i); +void DiffWrapperBase::CompareTemplateInfo( + const std::vector &old_template_elements, + const std::vector &new_template_elements, + std::deque *type_queue, + abi_util::IRDiffDumper::DiffKind diff_kind) { + uint32_t old_template_size = old_template_elements.size(); + assert(old_template_size == new_template_elements.size()); + uint32_t i = 0; + while (i < old_template_size) { + const abi_util::TemplateElementIR &old_template_element = + old_template_elements[i]; + const abi_util::TemplateElementIR &new_template_element = + new_template_elements[i]; + CompareAndDumpTypeDiff(old_template_element.GetReferencedType(), + new_template_element.GetReferencedType(), + type_queue, diff_kind); i++; - } - while (j < new_elements.size()) { - const Element &new_element = new_elements.Get(j); - ElementDiff *diff = dst->Add(); - Element *new_elementp = nullptr; - if (!diff || !(new_elementp = diff->mutable_new_())) { - llvm::errs() << "Failed to add diff element\n"; - ::exit(1); - } - *new_elementp = new_element; - diff->set_index(j); - j++; - } + } } -static bool DiffBasicNamedAndTypedDecl(BasicNamedAndTypedDecl *type_diff_old, - BasicNamedAndTypedDecl *type_diff_new, - const BasicNamedAndTypedDecl &old, - const BasicNamedAndTypedDecl &new_) { - assert(type_diff_old != nullptr); - assert(type_diff_new != nullptr); - if (DiffBasicTypeAbi(old.type_abi(), new_.type_abi()) || - IsAccessDownGraded(old.access(), new_.access())) { - *(type_diff_old) = old; - *(type_diff_new) = new_; +DiffStatus DiffWrapperBase::CompareRecordTypes( + const abi_util::RecordTypeIR *old_type, + const abi_util::RecordTypeIR *new_type, + std::deque *type_queue, + abi_util::IRDiffDumper::DiffKind diff_kind) { + auto record_type_diff_ir = std::make_unique(); + // Compare names. + if (old_type->GetName() != new_type->GetName()) { + // Do not dump anything since the record types themselves are fundamentally + // different. + return DiffStatus::direct_diff; + } + record_type_diff_ir->SetName(old_type->GetName()); + if (old_type->GetAccess() != new_type->GetAccess()) { + record_type_diff_ir->SetAccessDiff( + std::make_unique( + old_type->GetAccess(), new_type->GetAccess())); + } + + if (!CompareSizeAndAlignment(old_type, new_type)) { + record_type_diff_ir->SetTypeDiff( + std::make_unique( + std::make_pair(old_type->GetSize(), new_type->GetSize()), + std::make_pair(old_type->GetAlignment(), + new_type->GetAlignment()))); + } + if (!CompareVTables(old_type, new_type)) { + record_type_diff_ir->SetVTableLayoutDiff( + std::make_unique( + old_type->GetVTableLayout(), new_type->GetVTableLayout())); + } + std::pair, + std::vector> field_diffs; + field_diffs = CompareRecordFields(old_type->GetFields(), + new_type->GetFields(), type_queue, + diff_kind); + record_type_diff_ir->SetFieldDiffs(std::move(field_diffs.first)); + record_type_diff_ir->SetFieldsRemoved(std::move(field_diffs.second)); + const std::vector &old_bases = + old_type->GetBases(); + const std::vector &new_bases = + new_type->GetBases(); + + if (!CompareBaseSpecifiers(old_bases, new_bases, type_queue, diff_kind)) { + record_type_diff_ir->SetBaseSpecifierDiffs( + std::make_unique(old_bases, + new_bases)); + } + if (record_type_diff_ir->DiffExists() && + !ir_diff_dumper_->AddDiffMessageIR(record_type_diff_ir.get(), + Unwind(type_queue), diff_kind)) { + llvm::errs() << "AddDiffMessage on record type failed\n"; + ::exit(1); + } // No need to add a dump for an extension since records can't be "extended". + + CompareTemplateInfo(old_type->GetTemplateElements(), + new_type->GetTemplateElements(), + type_queue, diff_kind); + + return DiffStatus::no_diff; +} + +DiffStatus DiffWrapperBase::CompareLvalueReferenceTypes( + const abi_util::LvalueReferenceTypeIR *old_type, + const abi_util::LvalueReferenceTypeIR *new_type, + std::deque *type_queue, + abi_util::IRDiffDumper::DiffKind diff_kind) { + return CompareAndDumpTypeDiff(old_type->GetReferencedType(), + new_type->GetReferencedType(), + type_queue, diff_kind); +} + +DiffStatus DiffWrapperBase::CompareRvalueReferenceTypes( + const abi_util::RvalueReferenceTypeIR *old_type, + const abi_util::RvalueReferenceTypeIR *new_type, + std::deque *type_queue, + abi_util::IRDiffDumper::DiffKind diff_kind) { + return CompareAndDumpTypeDiff(old_type->GetReferencedType(), + new_type->GetReferencedType(), + type_queue, diff_kind); +} + +DiffStatus DiffWrapperBase::CompareQualifiedTypes( + const abi_util::QualifiedTypeIR *old_type, + const abi_util::QualifiedTypeIR *new_type, + std::deque *type_queue, + abi_util::IRDiffDumper::DiffKind diff_kind) { + // If all the qualifiers are not the same, return direct_diff, else + // recursively compare the unqualified types. + if (old_type->IsConst() != new_type->IsConst() || + old_type->IsVolatile() != new_type->IsVolatile() || + old_type->IsRestricted() != new_type->IsRestricted()) { + return DiffStatus::direct_diff; + } + return CompareAndDumpTypeDiff(old_type->GetReferencedType(), + new_type->GetReferencedType(), + type_queue, diff_kind); +} + +DiffStatus DiffWrapperBase::ComparePointerTypes( + const abi_util::PointerTypeIR *old_type, + const abi_util::PointerTypeIR *new_type, + std::deque *type_queue, + abi_util::IRDiffDumper::DiffKind diff_kind) { + // The following need to be the same for two pointer types to be considered + // equivalent: + // 1) Number of pointer indirections are the same. + // 2) The ultimate pointee is the same. + assert(CompareSizeAndAlignment(old_type, new_type)); + return CompareAndDumpTypeDiff(old_type->GetReferencedType(), + new_type->GetReferencedType(), + type_queue, diff_kind); +} + +DiffStatus DiffWrapperBase::CompareBuiltinTypes( + const abi_util::BuiltinTypeIR *old_type, + const abi_util::BuiltinTypeIR *new_type) { + // If the size, alignment and is_unsigned are the same, return no_diff + // else return direct_diff. + uint64_t old_signedness = old_type->IsUnsigned(); + uint64_t new_signedness = new_type->IsUnsigned(); + + if (!CompareSizeAndAlignment(old_type, new_type) || + old_signedness != new_signedness || + old_type->IsIntegralType() != new_type->IsIntegralType()) { + return DiffStatus::direct_diff; + } + return DiffStatus::no_diff; +} + +DiffStatus DiffWrapperBase::CompareFunctionParameters( + const std::vector &old_parameters, + const std::vector &new_parameters, + std::deque *type_queue, + abi_util::IRDiffDumper::DiffKind diff_kind) { + size_t old_parameters_size = old_parameters.size(); + if (old_parameters_size != new_parameters.size()) { + return DiffStatus::direct_diff; + } + uint64_t i = 0; + while (i < old_parameters_size) { + const abi_util::ParamIR &old_parameter = old_parameters.at(i); + const abi_util::ParamIR &new_parameter = new_parameters.at(i); + if ((CompareAndDumpTypeDiff(old_parameter.GetReferencedType(), + new_parameter.GetReferencedType(), + type_queue, diff_kind) == + DiffStatus::direct_diff) || + (old_parameter.GetIsDefault() != new_parameter.GetIsDefault())) { + return DiffStatus::direct_diff; + } + i++; + } + return DiffStatus::no_diff; +} + +DiffStatus DiffWrapperBase::CompareAndDumpTypeDiff( + const abi_util::TypeIR *old_type, const abi_util::TypeIR *new_type, + abi_util::LinkableMessageKind kind, std::deque *type_queue, + abi_util::IRDiffDumper::DiffKind diff_kind) { + if (kind == abi_util::LinkableMessageKind::BuiltinTypeKind) { + return CompareBuiltinTypes( + static_cast(old_type), + static_cast(new_type)); + } + + if (kind == abi_util::LinkableMessageKind::QualifiedTypeKind) { + return CompareQualifiedTypes( + static_cast(old_type), + static_cast(new_type), + type_queue, diff_kind); + } + + if (kind == abi_util::LinkableMessageKind::EnumTypeKind) { + return CompareEnumTypes( + static_cast(old_type), + static_cast(new_type), + type_queue, diff_kind); + + } + + if (kind == abi_util::LinkableMessageKind::LvalueReferenceTypeKind) { + return CompareLvalueReferenceTypes( + static_cast(old_type), + static_cast(new_type), + type_queue, diff_kind); + + } + + if (kind == abi_util::LinkableMessageKind::RvalueReferenceTypeKind) { + return CompareRvalueReferenceTypes( + static_cast(old_type), + static_cast(new_type), + type_queue, diff_kind); + } + + if (kind == abi_util::LinkableMessageKind::PointerTypeKind) { + return ComparePointerTypes( + static_cast(old_type), + static_cast(new_type), + type_queue, diff_kind); + } + + if (kind == abi_util::LinkableMessageKind::RecordTypeKind) { + return CompareRecordTypes( + static_cast(old_type), + static_cast(new_type), + type_queue, diff_kind); + } + return DiffStatus::no_diff; +} + +static DiffStatus CompareDistinctKindMessages( + const abi_util::TypeIR *old_type, const abi_util::TypeIR *new_type) { + // For these types to be considered ABI compatible, the very least requirement + // is that their sizes and alignments should be equal. + // TODO: Fill in + return DiffStatus::direct_diff; +} + +DiffStatus DiffWrapperBase::CompareAndDumpTypeDiff( + const std::string &old_type_str, const std::string &new_type_str, + std::deque *type_queue, + abi_util::IRDiffDumper::DiffKind diff_kind) { + // If either of the types are not found in their respective maps, the type + // was not exposed in a public header and we do a simple string comparison. + // Any diff found using a simple string comparison will be a direct diff. + + // Check the map for types which have already been compared + bool same_type_str = (old_type_str == new_type_str); + if (same_type_str) { + // These types have already been diffed, return without further comparison. + if (!type_cache_->insert(old_type_str).second) { + return DiffStatus::no_diff; + } + type_queue->push_back(old_type_str); + } + std::map::const_iterator old_it = + old_types_.find(old_type_str); + std::map::const_iterator new_it = + new_types_.find(new_type_str); + if (old_it == old_types_.end() || new_it == new_types_.end()) { + // Do a simple string comparison. + if (!type_queue->empty()) { + type_queue->pop_back(); + } + return (old_type_str == new_type_str) ? + DiffStatus::no_diff : DiffStatus::direct_diff; + } + abi_util::LinkableMessageKind old_kind = + old_it->second->GetKind(); + abi_util::LinkableMessageKind new_kind = + new_it->second->GetKind(); + DiffStatus diff_status = DiffStatus::no_diff; + if (old_kind != new_kind) { + diff_status = CompareDistinctKindMessages(old_it->second, new_it->second); + } else { + diff_status = CompareAndDumpTypeDiff(old_it->second , new_it->second , + old_kind, type_queue, diff_kind); + } + if (!type_queue->empty()) { + type_queue->pop_back(); + } + return diff_status; +} + +template <> +bool DiffWrapper::DumpDiff( + abi_util::IRDiffDumper::DiffKind diff_kind) { + std::deque type_queue; + if (oldp_->GetName() != newp_->GetName()) { + llvm::errs() << "Comparing two different unreferenced records\n"; + return false; + } + if (!type_cache_->insert(oldp_->GetName()).second) { return true; } - return false; + CompareRecordTypes(oldp_, newp_, &type_queue, diff_kind); + return true; } template <> -std::unique_ptr -DiffWrapper::Get() { - std::unique_ptr record_diff(new RecordDeclDiff()); - assert(oldp_->basic_abi().linker_set_key() == - newp_->basic_abi().linker_set_key()); - record_diff->set_name(oldp_->basic_abi().name()); - google::protobuf::RepeatedPtrField *fdiffs = - record_diff->mutable_field_diffs(); - google::protobuf::RepeatedPtrField *bdiffs = - record_diff->mutable_base_diffs(); - google::protobuf::RepeatedPtrField *vtdiffs = - record_diff->mutable_vtable_diffs(); - assert(fdiffs != nullptr && bdiffs != nullptr); - // Template Information isn't diffed since the linker_set_key includes the - // mangled name which includes template information. - if (GetElementDiffs(fdiffs, oldp_->fields(), newp_->fields()) || - GetElementDiffs(bdiffs, oldp_->base_specifiers(), - newp_->base_specifiers()) || - GetElementDiffs(vtdiffs, oldp_->vtable_layout().vtable_components(), - newp_->vtable_layout().vtable_components()) || - DiffBasicNamedAndTypedDecl( - record_diff->mutable_type_diff()->mutable_old(), - record_diff->mutable_type_diff()->mutable_new_(), - oldp_->basic_abi(), newp_->basic_abi())) { - return record_diff; +bool DiffWrapper::DumpDiff( + abi_util::IRDiffDumper::DiffKind diff_kind) { + std::deque type_queue; + if (oldp_->GetName() != newp_->GetName()) { + llvm::errs() << "Comparing two different unreferenced enums\n"; + return false; } - return nullptr; + if (!type_cache_->insert(oldp_->GetName()).second) { + return true; + } + CompareEnumTypes(oldp_, newp_, &type_queue, diff_kind); + return true; } template <> -std::unique_ptr -DiffWrapper::Get() { - std::unique_ptr enum_diff(new EnumDeclDiff()); - assert(oldp_->basic_abi().linker_set_key() == - newp_->basic_abi().linker_set_key()); - google::protobuf::RepeatedPtrField *fdiffs = - enum_diff->mutable_field_diffs(); - assert(fdiffs != nullptr); - enum_diff->set_name(oldp_->basic_abi().name()); - if (GetElementDiffs(fdiffs, oldp_->enum_fields(), newp_->enum_fields()) || - DiffBasicNamedAndTypedDecl( - enum_diff->mutable_type_diff()->mutable_old(), - enum_diff->mutable_type_diff()->mutable_new_(), - oldp_->basic_abi(), newp_->basic_abi())) { - return enum_diff; +bool DiffWrapper::DumpDiff( + abi_util::IRDiffDumper::DiffKind diff_kind) { + std::deque type_queue; + type_queue.push_back(oldp_->GetName()); + DiffStatus type_diff = CompareAndDumpTypeDiff(oldp_->GetReferencedType(), + newp_->GetReferencedType(), + &type_queue, diff_kind); + DiffStatus access_diff = (oldp_->GetAccess() == newp_->GetAccess()) ? + DiffStatus::no_diff : DiffStatus::direct_diff; + if ((type_diff | access_diff) & DiffStatus::direct_diff) { + abi_util::GlobalVarDiffIR global_var_diff_ir(oldp_, newp_); + global_var_diff_ir.SetName(oldp_->GetName()); + return ir_diff_dumper_->AddDiffMessageIR(&global_var_diff_ir, + Unwind(&type_queue), diff_kind); } - return nullptr; + return true; } template <> -std::unique_ptr -DiffWrapper::Get() { - std::unique_ptr func_diff(new FunctionDeclDiff()); - google::protobuf::RepeatedPtrField *pdiffs = - func_diff->mutable_param_diffs(); - assert(func_diff->mutable_return_type_diffs() != nullptr); - func_diff->set_name(oldp_->basic_abi().linker_set_key()); - if (DiffBasicNamedAndTypedDecl( - func_diff->mutable_return_type_diffs()->mutable_old(), - func_diff->mutable_return_type_diffs()->mutable_new_(), - oldp_->basic_abi(), newp_->basic_abi()) || - GetElementDiffs(pdiffs, oldp_->parameters(), newp_->parameters())) { - return func_diff; - } - return nullptr; -} +bool DiffWrapper::DumpDiff( + abi_util::IRDiffDumper::DiffKind diff_kind) { + std::deque type_queue; + type_queue.push_back(oldp_->GetName()); + DiffStatus param_diffs = CompareFunctionParameters(oldp_->GetParameters(), + newp_->GetParameters(), + &type_queue, diff_kind); + DiffStatus return_type_diff = + CompareAndDumpTypeDiff(oldp_->GetReturnType(), + newp_->GetReturnType(), + &type_queue, diff_kind); + CompareTemplateInfo(oldp_->GetTemplateElements(), + newp_->GetTemplateElements(), + &type_queue, diff_kind); -template <> -std::unique_ptr -DiffWrapper::Get() { - std::unique_ptr global_var_diff(new GlobalVarDeclDiff()); - assert(global_var_diff->mutable_type_diff() != nullptr); - if (DiffBasicNamedAndTypedDecl( - global_var_diff->mutable_type_diff()->mutable_old(), - global_var_diff->mutable_type_diff()->mutable_new_(), - oldp_->basic_abi(), newp_->basic_abi())) { - return global_var_diff; + if ((param_diffs == DiffStatus::direct_diff || + return_type_diff == DiffStatus::direct_diff) || + (oldp_->GetAccess() != newp_->GetAccess())) { + abi_util::FunctionDiffIR function_diff_ir(oldp_, newp_); + function_diff_ir.SetName(oldp_->GetName()); + return ir_diff_dumper_->AddDiffMessageIR(&function_diff_ir, + Unwind(&type_queue), diff_kind); } - return nullptr; + return true; } - } // abi_diff_wrappers diff --git a/vndk/tools/header-checker/header-abi-diff/src/abi_diff_wrappers.h b/vndk/tools/header-checker/header-abi-diff/src/abi_diff_wrappers.h index 8cf46991f..9b0a7f57e 100644 --- a/vndk/tools/header-checker/header-abi-diff/src/abi_diff_wrappers.h +++ b/vndk/tools/header-checker/header-abi-diff/src/abi_diff_wrappers.h @@ -22,45 +22,160 @@ #include "proto/abi_diff.pb.h" #pragma clang diagnostic pop +#include + +#include + namespace abi_diff_wrappers { -template +template static bool IgnoreSymbol(const T *element, - const std::set &ignored_symbols) { - return ignored_symbols.find(element->basic_abi().linker_set_key()) != + const std::set &ignored_symbols, + F func) { + return ignored_symbols.find(func(element)) != ignored_symbols.end(); } -template -class DiffWrapperBase { - public: - virtual std::unique_ptr Get() = 0 ; - protected: - DiffWrapperBase(const T *oldp, const T *newp) : oldp_(oldp), newp_(newp) { } - template - bool GetElementDiffs( - google::protobuf::RepeatedPtrField *dst, - const google::protobuf::RepeatedPtrField &old_elements, - const google::protobuf::RepeatedPtrField &new_elements); - - private: - template - void GetExtraElementDiffs( - google::protobuf::RepeatedPtrField *dst, int i, int j, - const google::protobuf::RepeatedPtrField &old_elements, - const google::protobuf::RepeatedPtrField &new_elements); - - protected: - const T *oldp_; - const T *newp_; +enum DiffStatus { + // Previous stages of CompareAndTypeDiff should not dump the diff. + no_diff = 0, + // Previous stages of CompareAndTypeDiff should dump the diff if required. + direct_diff = 1, }; -template -class DiffWrapper : public DiffWrapperBase { +class DiffWrapperBase { public: - DiffWrapper(const T *oldp, const T *newp) - : DiffWrapperBase(oldp, newp) { } - std::unique_ptr Get() override; + virtual bool DumpDiff(abi_util::IRDiffDumper::DiffKind diff_kind) = 0; + virtual ~DiffWrapperBase() { } + protected: + DiffWrapperBase( + abi_util::IRDiffDumper *ir_diff_dumper, + const std::map &old_types, + const std::map &new_types, + std::set *type_cache) + : ir_diff_dumper_(ir_diff_dumper), + old_types_(old_types), new_types_(new_types), + type_cache_(type_cache) { } + + DiffStatus CompareAndDumpTypeDiff(const std::string &old_type_str, + const std::string &new_type_str, + std::deque *type_queue, + abi_util::IRDiffDumper::DiffKind diff_kind); + + DiffStatus CompareAndDumpTypeDiff( + const abi_util::TypeIR *old_type, const abi_util::TypeIR *new_type, + abi_util::LinkableMessageKind kind, std::deque *type_queue, + abi_util::IRDiffDumper::DiffKind diff_kind); + + + DiffStatus CompareRecordTypes(const abi_util::RecordTypeIR *old_type, + const abi_util::RecordTypeIR *new_type, + std::deque *type_queue, + abi_util::IRDiffDumper::DiffKind diff_kind); + + DiffStatus CompareQualifiedTypes(const abi_util::QualifiedTypeIR *old_type, + const abi_util::QualifiedTypeIR *new_type, + std::deque *type_queue, + abi_util::IRDiffDumper::DiffKind diff_kind); + + DiffStatus ComparePointerTypes(const abi_util::PointerTypeIR *old_type, + const abi_util::PointerTypeIR *new_type, + std::deque *type_queue, + abi_util::IRDiffDumper::DiffKind diff_kind); + + DiffStatus CompareLvalueReferenceTypes( + const abi_util::LvalueReferenceTypeIR *old_type, + const abi_util::LvalueReferenceTypeIR *new_type, + std::deque *type_queue, + abi_util::IRDiffDumper::DiffKind diff_kind); + + DiffStatus CompareRvalueReferenceTypes( + const abi_util::RvalueReferenceTypeIR *old_type, + const abi_util::RvalueReferenceTypeIR *new_type, + std::deque *type_queue, + abi_util::IRDiffDumper::DiffKind diff_kind); + + + DiffStatus CompareBuiltinTypes(const abi_util::BuiltinTypeIR *old_type, + const abi_util::BuiltinTypeIR *new_type); + static void CompareEnumFields( + const std::vector &old_fields, + const std::vector &new_fields, + abi_util::EnumTypeDiffIR *enum_type_diff_ir); + + DiffStatus CompareEnumTypes(const abi_util::EnumTypeIR *old_type, + const abi_util::EnumTypeIR *new_type, + std::deque *type_queue, + abi_util::IRDiffDumper::DiffKind diff_kind); + + std::unique_ptr CompareCommonRecordFields( + const abi_util::RecordFieldIR *old_field, + const abi_util::RecordFieldIR *new_field, + std::deque *type_queue, + abi_util::IRDiffDumper::DiffKind diff_kind); + + std::pair, + std::vector> + CompareRecordFields( + const std::vector &old_fields, + const std::vector &new_fields, + std::deque *type_queue, + abi_util::IRDiffDumper::DiffKind diff_kind); + + DiffStatus CompareFunctionParameters( + const std::vector &old_parameters, + const std::vector &new_parameters, + std::deque *type_queue, + abi_util::IRDiffDumper::DiffKind diff_kind); + + bool CompareBaseSpecifiers( + const std::vector &old_base_specifiers, + const std::vector &new_base_specifiers, + std::deque *type_queue, + abi_util::IRDiffDumper::DiffKind diff_kind); + + bool CompareVTables(const abi_util::RecordTypeIR *old_record, + const abi_util::RecordTypeIR *new_record); + + bool CompareVTableComponents( + const abi_util::VTableComponentIR &old_component, + const abi_util::VTableComponentIR &new_component); + + void CompareTemplateInfo( + const std::vector &old_template_elements, + const std::vector &new_template_elements, + std::deque *type_queue, + abi_util::IRDiffDumper::DiffKind diff_kind); + + + bool CompareSizeAndAlignment(const abi_util::TypeIR *old_ti, + const abi_util::TypeIR *new_ti); + + template + bool AddToDiff(DiffType *mutable_diff, const DiffElement *oldp, + const DiffElement *newp, + std::deque *type_queue = nullptr); + protected: + abi_util::IRDiffDumper *ir_diff_dumper_; + const std::map &old_types_; + const std::map &new_types_; + std::set *type_cache_; +}; + +template +class DiffWrapper : public DiffWrapperBase { + public: + DiffWrapper(const T *oldp, const T *newp, + abi_util::IRDiffDumper *ir_diff_dumper, + const std::map &old_types, + const std::map &new_types, + std::set *type_cache) + : DiffWrapperBase(ir_diff_dumper, old_types, new_types, type_cache), + oldp_(oldp), newp_(newp) { } + bool DumpDiff(abi_util::IRDiffDumper::DiffKind diff_kind) override; + private: + const T *oldp_; + const T *newp_; }; } // abi_diff_wrappers diff --git a/vndk/tools/header-checker/header-abi-diff/src/header_abi_diff.cpp b/vndk/tools/header-checker/header-abi-diff/src/header_abi_diff.cpp index 7281c09b6..813ea7505 100644 --- a/vndk/tools/header-checker/header-abi-diff/src/header_abi_diff.cpp +++ b/vndk/tools/header-checker/header-abi-diff/src/header_abi_diff.cpp @@ -14,6 +14,8 @@ #include "abi_diff.h" +#include + #include #include #include @@ -49,6 +51,12 @@ static llvm::cl::opt advice_only( "advice-only", llvm::cl::desc("Advisory mode only"), llvm::cl::Optional, llvm::cl::cat(header_checker_category)); +static llvm::cl::opt check_all_apis( + "check-all-apis", + llvm::cl::desc("All apis, whether referenced or not, by exported symbols in" + " the dynsym table of a shared library are checked"), + llvm::cl::Optional, llvm::cl::cat(header_checker_category)); + static llvm::cl::opt suppress_local_warnings( "suppress_local_warnings", llvm::cl::desc("suppress local warnings"), llvm::cl::Optional, llvm::cl::cat(header_checker_category)); @@ -58,6 +66,13 @@ static llvm::cl::opt allow_extensions( llvm::cl::desc("Do not return a non zero status on extensions"), llvm::cl::Optional, llvm::cl::cat(header_checker_category)); +static llvm::cl::opt allow_unreferenced_changes( + "allow-unreferenced-changes", + llvm::cl::desc("Do not return a non zero status on changes to data" + " structures which are not directly referenced by exported" + " APIs."), + llvm::cl::Optional, llvm::cl::cat(header_checker_category)); + static std::set LoadIgnoredSymbols(std::string &symbol_list_path) { std::ifstream symbol_ifstream(symbol_list_path); std::set ignored_symbols; @@ -80,38 +95,50 @@ int main(int argc, const char **argv) { ignored_symbols = LoadIgnoredSymbols(ignore_symbol_list); } HeaderAbiDiff judge(lib_name, arch, old_dump, new_dump, compatibility_report, - ignored_symbols); + ignored_symbols, check_all_apis); - CompatibilityStatus status = judge.GenerateCompatibilityReport(); + abi_util::CompatibilityStatusIR status = judge.GenerateCompatibilityReport(); std::string status_str = ""; - switch (status) { - case CompatibilityStatus::INCOMPATIBLE: - status_str = "broken"; - break; - case CompatibilityStatus::EXTENSION: - status_str = "extended"; - break; - default: - break; - } + std::string unreferenced_change_str = ""; + std::string error_or_warning_str = "\033[36;1mwarning: \033[0m"; + if (status == abi_util::CompatibilityStatusIR::Incompatible) { + error_or_warning_str = "\033[31;1merror: \033[0m"; + status_str = " INCOMPATIBLE CHANGES"; + } else if (status & abi_util::CompatibilityStatusIR::Extension) { + status_str = "EXTENDING CHANGES"; + } + if (status & abi_util::CompatibilityStatusIR::UnreferencedChanges) { + unreferenced_change_str = ", changes in exported headers, which are"; + unreferenced_change_str += " not directly referenced by exported symbols."; + unreferenced_change_str += " This MIGHT be an ABI breaking change due to"; + unreferenced_change_str += " internal typecasts."; + + } if (!suppress_local_warnings && status) { llvm::errs() << "******************************************************\n" - << "VNDK Abi " + << error_or_warning_str + << "VNDK library: " + << lib_name + << "'s ABI has " << status_str - << ":" + << unreferenced_change_str << " Please check compatiblity report at : " << compatibility_report << "\n" - << "*****************************************************\n"; + << "******************************************************\n"; + } + + if ((allow_extensions && + (status & abi_util::CompatibilityStatusIR::Extension)) || + (allow_unreferenced_changes && + (status & abi_util::CompatibilityStatusIR::UnreferencedChanges))) { + return abi_util::CompatibilityStatusIR::Compatible; } if (advice_only) { - return CompatibilityStatus::COMPATIBLE; + return abi_util::CompatibilityStatusIR::Compatible; } - if (allow_extensions && status == CompatibilityStatus::EXTENSION) { - return CompatibilityStatus::COMPATIBLE; - } return status; } diff --git a/vndk/tools/header-checker/header-abi-dumper/src/abi_wrappers.cpp b/vndk/tools/header-checker/header-abi-dumper/src/abi_wrappers.cpp index 32bb3c736..b0d4e23c6 100644 --- a/vndk/tools/header-checker/header-abi-dumper/src/abi_wrappers.cpp +++ b/vndk/tools/header-checker/header-abi-dumper/src/abi_wrappers.cpp @@ -28,10 +28,26 @@ using namespace abi_wrapper; ABIWrapper::ABIWrapper( clang::MangleContext *mangle_contextp, clang::ASTContext *ast_contextp, - const clang::CompilerInstance *cip) + const clang::CompilerInstance *cip, + std::set *type_cache, + abi_util::IRDumper *ir_dumper, + std::map &decl_to_source_cache) : cip_(cip), mangle_contextp_(mangle_contextp), - ast_contextp_(ast_contextp) { } + ast_contextp_(ast_contextp), + type_cache_(type_cache), + ir_dumper_(ir_dumper), + decl_to_source_file_cache_(decl_to_source_cache) { } + +std::string ABIWrapper::GetCachedDeclSourceFile( + const clang::Decl *decl, const clang::CompilerInstance *cip) { + assert(decl != nullptr); + auto result = decl_to_source_file_cache_.find(decl); + if (result == decl_to_source_file_cache_.end()) { + return GetDeclSourceFile(decl, cip); + } + return result->second; +} std::string ABIWrapper::GetDeclSourceFile(const clang::Decl *decl, const clang::CompilerInstance *cip) { @@ -53,85 +69,174 @@ std::string ABIWrapper::GetDeclSourceFile(const clang::Decl *decl, return file_abs_path; } -abi_dump::AccessSpecifier ABIWrapper::AccessClangToDump( - const clang::AccessSpecifier sp) const { +static abi_util::AccessSpecifierIR AccessClangToIR( + const clang::AccessSpecifier sp) { switch (sp) { case clang::AS_private: { - return abi_dump::AccessSpecifier::private_access; + return abi_util::AccessSpecifierIR::PrivateAccess; break; } case clang::AS_protected: { - return abi_dump::AccessSpecifier::protected_access; + return abi_util::AccessSpecifierIR::ProtectedAccess; break; } default: { - return abi_dump::AccessSpecifier::public_access; + return abi_util::AccessSpecifierIR::PublicAccess; break; } } } -// Dumping the size and alignment is optional. This is since clang can lazily -// instantiate records as incomplete and therefore their sizes 'may' not be -// computable. b/62307940 -bool ABIWrapper::SetupBasicTypeAbi(abi_dump::BasicTypeAbi *type_abi, - const clang::QualType type, - bool dump_size) const { - if (!type_abi) { +// Get type 'referenced' by qual_type. Referenced type implies, in order: +// 1) Strip off all qualifiers if qual_type has CVR qualifiers. +// 2) Strip off a pointer level if qual_type is a pointer. +// 3) Strip off the reference if qual_type is a reference. +// Note: qual_type is expected to be a canonical type. +clang::QualType ABIWrapper::GetReferencedType(const clang::QualType qual_type) { + const clang::Type *type_ptr = qual_type.getTypePtr(); + if (qual_type.hasLocalQualifiers()) { + return qual_type.getLocalUnqualifiedType(); + } + if (type_ptr->isPointerType()) { + return type_ptr->getPointeeType(); + } + if (type_ptr->isArrayType()) { + return + type_ptr->getArrayElementTypeNoTypeQual()->getCanonicalTypeInternal(); + } + return qual_type.getNonReferenceType(); +} + +bool ABIWrapper::CreateExtendedType( + clang::QualType qual_type, + abi_util::TypeIR *typep) { + std::string type_name = QualTypeToString(qual_type); + if (!type_cache_->insert(type_name).second) { + return true; + } + const clang::QualType canonical_type = qual_type.getCanonicalType(); + return CreateBasicNamedAndTypedDecl(canonical_type, typep, ""); +} + +//This overload takes in a qualtype and adds its information to the abi-dump on +//its own. +bool ABIWrapper::CreateBasicNamedAndTypedDecl(clang::QualType qual_type, + const std::string &source_file) { + std::string type_name = QualTypeToString(qual_type); + const clang::QualType canonical_type = qual_type.getCanonicalType(); + const clang::Type *base_type = canonical_type.getTypePtr(); + bool is_ptr = base_type->isPointerType(); + bool is_reference = base_type->isReferenceType(); + bool is_array = base_type->isArrayType(); + bool is_builtin = base_type->isBuiltinType(); + bool has_referenced_type = + is_ptr || is_reference || is_array || + canonical_type.hasLocalQualifiers() || is_builtin; + if (!has_referenced_type || !type_cache_->insert(type_name).second) { + return true; + } + // Do something similar to what is being done right now. Create an object + // extending Type and return a pointer to that and pass it to CreateBasic... + // CreateBasic...(qualtype, Type *) fills in size, alignemnt etc. + std::unique_ptr typep = SetTypeKind(canonical_type, + source_file); + if (!base_type->isVoidType() && !typep) { return false; } - const clang::QualType canonical_type = type.getCanonicalType(); - type_abi->set_name(QualTypeToString(canonical_type)); + return CreateBasicNamedAndTypedDecl(canonical_type, typep.get(), + source_file) && + ir_dumper_->AddLinkableMessageIR(typep.get()); +} + +// CreateBasicNamedAndTypedDecl creates a BasicNamedAndTypedDecl : that'll +// include all the generic information a basic type will have: +// abi_dump::BasicNamedAndTypedDecl. Other methods fill in more specific +// information, eg: RecordDecl, EnumDecl. +bool ABIWrapper::CreateBasicNamedAndTypedDecl( + clang::QualType canonical_type, + abi_util::TypeIR *typep, const std::string &source_file) { // Cannot determine the size and alignment for template parameter dependent // types as well as incomplete types. const clang::Type *base_type = canonical_type.getTypePtr(); + assert(base_type != nullptr); clang::Type::TypeClass type_class = base_type->getTypeClass(); // Temporary Hack for auto type sizes. Not determinable. - if (dump_size && base_type && !(base_type->isDependentType()) && - !(base_type->isIncompleteType()) && (type_class != clang::Type::Auto)) { + if ((type_class != clang::Type::Auto) && !base_type->isIncompleteType() && + !(base_type->isDependentType())) { std::pair size_and_alignment = ast_contextp_->getTypeInfoInChars(canonical_type); - int64_t size = size_and_alignment.first.getQuantity(); - int64_t alignment = size_and_alignment.second.getQuantity(); - type_abi->set_size(size); - type_abi->set_alignment(alignment); + size_t size = size_and_alignment.first.getQuantity(); + size_t alignment = size_and_alignment.second.getQuantity(); + typep->SetSize(size); + typep->SetAlignment(alignment); } - return true; + typep->SetName(QualTypeToString(canonical_type)); + typep->SetLinkerSetKey(QualTypeToString(canonical_type)); + // default values are false, we don't set them since explicitly doing that + // makes the abi dumps more verbose. + // This type has a reference type if its a pointer / reference OR it has CVR + // qualifiers. + clang::QualType referenced_type = GetReferencedType(canonical_type); + typep->SetReferencedType(QualTypeToString(referenced_type)); + // Create the type for referenced type. + return CreateBasicNamedAndTypedDecl(referenced_type, source_file); } -bool ABIWrapper::SetupBasicNamedAndTypedDecl( - abi_dump::BasicNamedAndTypedDecl *basic_named_and_typed_decl, - const clang::QualType type, const std::string &name, - const clang::AccessSpecifier &access, std::string key, - bool dump_size) const { - if (!basic_named_and_typed_decl) { - return false; - } - abi_dump::AccessSpecifier access_dump = AccessClangToDump(access); - basic_named_and_typed_decl->set_name(name); - basic_named_and_typed_decl->set_access(access_dump); - if (key != "") { - basic_named_and_typed_decl->set_linker_set_key(key); - } - return SetupBasicTypeAbi(basic_named_and_typed_decl->mutable_type_abi(), - type, dump_size); -} - -static bool ShouldDumpSize(clang::QualType qt) { - const clang::Type *type_ptr = qt.getTypePtr(); - assert(type_ptr != nullptr); - if (type_ptr->isBuiltinType() || type_ptr->isPointerType()) { - return true; - } - return false; -} - -std::string ABIWrapper::GetTypeLinkageName(const clang::Type *typep) const { +std::string ABIWrapper::GetTypeLinkageName(const clang::Type *typep) { assert(typep != nullptr); clang::QualType qt = typep->getCanonicalTypeInternal(); return QualTypeToString(qt); } +std::unique_ptr ABIWrapper::SetTypeKind( + const clang::QualType canonical_type, const std::string &source_file) { + if (canonical_type.hasLocalQualifiers()) { + auto qual_type_ir = + std::make_unique(); + qual_type_ir->SetConstness(canonical_type.isConstQualified()); + qual_type_ir->SetRestrictedness(canonical_type.isRestrictQualified()); + qual_type_ir->SetVolatility(canonical_type.isVolatileQualified()); + qual_type_ir->SetSourceFile(source_file); + return qual_type_ir; + } + const clang::Type *type_ptr = canonical_type.getTypePtr(); + if (type_ptr->isPointerType()) { + auto pointer_type_ir = std::make_unique(); + pointer_type_ir->SetSourceFile(source_file); + return pointer_type_ir; + } + if (type_ptr->isLValueReferenceType()) { + auto lvalue_reference_type_ir = + std::make_unique(); + lvalue_reference_type_ir->SetSourceFile(source_file); + return lvalue_reference_type_ir; + } + if (type_ptr->isRValueReferenceType()) { + auto rvalue_reference_type_ir = + std::make_unique(); + rvalue_reference_type_ir->SetSourceFile(source_file); + return rvalue_reference_type_ir; + } + if (type_ptr->isArrayType()) { + auto array_type_ir = std::make_unique(); + array_type_ir->SetSourceFile(source_file); + return array_type_ir; + } + if (type_ptr->isEnumeralType()) { + return std::make_unique(); + } + if (type_ptr->isRecordType()) { + return std::make_unique(); + } + if (type_ptr->isBuiltinType()) { + auto builtin_type_ir = std::make_unique(); + builtin_type_ir->SetSignedness(type_ptr->isUnsignedIntegerType()); + builtin_type_ir->SetIntegralType(type_ptr->isIntegralType(*ast_contextp_)); + return builtin_type_ir; + } + return nullptr; +} + std::string ABIWrapper::GetMangledNameDecl( const clang::NamedDecl *decl, clang::MangleContext *mangle_contextp) { if (!mangle_contextp->shouldMangleDeclName(decl)) { @@ -145,36 +250,8 @@ std::string ABIWrapper::GetMangledNameDecl( return mangled_name; } -bool ABIWrapper::SetupTemplateParamNames( - abi_dump::TemplateInfo *tinfo, - clang::TemplateParameterList *pl) const { - if (tinfo->elements_size() > 0) { - return true; - } - - clang::TemplateParameterList::iterator template_it = pl->begin(); - while (template_it != pl->end()) { - abi_dump::TemplateElement *template_parameterp = - tinfo->add_elements(); - if (!template_parameterp) { - return false; - } - abi_dump::TemplateElement::BasicTemplateElementAbi *basic_abi = - template_parameterp->mutable_basic_abi(); - if (!basic_abi) { - return false; - } - std::string name = (*template_it)->getName(); - basic_abi->set_name(name); - // TODO : Default arg ? - basic_abi->set_linker_set_key(name); - template_it++; - } - return true; -} - std::string ABIWrapper::GetTagDeclQualifiedName( - const clang::TagDecl *decl) const { + const clang::TagDecl *decl) { if (decl->getTypedefNameForAnonDecl()) { return decl->getTypedefNameForAnonDecl()->getQualifiedNameAsString(); } @@ -182,8 +259,10 @@ std::string ABIWrapper::GetTagDeclQualifiedName( } bool ABIWrapper::SetupTemplateArguments( - abi_dump::TemplateInfo *tinfo, - const clang::TemplateArgumentList *tl) const { + const clang::TemplateArgumentList *tl, + abi_util::TemplatedArtifactIR *ta, + const std::string &source_file) { + abi_util::TemplateInfoIR template_info; for (int i = 0; i < tl->size(); i++) { const clang::TemplateArgument &arg = (*tl)[i]; //TODO: More comprehensive checking needed. @@ -191,25 +270,33 @@ bool ABIWrapper::SetupTemplateArguments( continue; } clang::QualType type = arg.getAsType(); - abi_dump::TemplateElement *template_parameterp = - tinfo->add_elements(); - if (!template_parameterp) { + template_info.AddTemplateElement( + abi_util::TemplateElementIR(QualTypeToString(type))); + if (!CreateBasicNamedAndTypedDecl(type, source_file)) { return false; } - abi_dump::TemplateElement::BasicTemplateElementAbi *basic_abi = - template_parameterp->mutable_basic_abi(); - if (!basic_abi || !SetupBasicTypeAbi(basic_abi->mutable_type_abi(), type, - false)) { - return false; - } - // TODO : default arg - basic_abi->set_linker_set_key(QualTypeToString(type)); } + ta->SetTemplateInfo(std::move(template_info)); return true; } +static const clang::EnumDecl *GetAnonymousEnum( + const clang::QualType qual_type) { + const clang::Type *type_ptr = qual_type.getTypePtr(); + assert(type_ptr != nullptr); + const clang::TagDecl *tag_decl = type_ptr->getAsTagDecl(); + if (!tag_decl) { + return nullptr; + } + const clang::EnumDecl *enum_decl = llvm::dyn_cast(tag_decl); + if (!enum_decl || enum_decl->hasNameForLinkage()) { + return nullptr; + } + return enum_decl; +} + std::string ABIWrapper::QualTypeToString( - const clang::QualType &sweet_qt) const { + const clang::QualType &sweet_qt) { const clang::QualType salty_qt = sweet_qt.getCanonicalType(); // clang::TypeName::getFullyQualifiedName removes the part of the type related // to it being a template parameter. Don't use it for dependent types. @@ -223,75 +310,84 @@ FunctionDeclWrapper::FunctionDeclWrapper( clang::MangleContext *mangle_contextp, clang::ASTContext *ast_contextp, const clang::CompilerInstance *compiler_instance_p, - const clang::FunctionDecl *decl) - : ABIWrapper(mangle_contextp, ast_contextp, compiler_instance_p), + const clang::FunctionDecl *decl, + std::set *type_cache, + abi_util::IRDumper *ir_dumper, + std::map &decl_to_source_cache) + : ABIWrapper(mangle_contextp, ast_contextp, compiler_instance_p, + type_cache, ir_dumper, decl_to_source_cache), function_decl_(decl) { } +bool FunctionDeclWrapper::SetupThisParameter(abi_util::FunctionIR *functionp, + const std::string &source_file) { + const clang::CXXMethodDecl *cxx_method_decl = + llvm::dyn_cast(function_decl_); + // No this pointer for static methods. + if (!cxx_method_decl || cxx_method_decl->isStatic()) { + return true; + } + clang::QualType this_type = cxx_method_decl->getThisType(*ast_contextp_); + return SetupFunctionParameter(functionp, this_type, false, source_file); +} + +bool FunctionDeclWrapper::SetupFunctionParameter( + abi_util::FunctionIR *functionp, const clang::QualType qual_type, + bool has_default_arg, const std::string &source_file) { + if (!CreateBasicNamedAndTypedDecl(qual_type, source_file)) { + return false; + } + functionp->AddParameter(abi_util::ParamIR(QualTypeToString(qual_type), + has_default_arg)); + return true; +} + bool FunctionDeclWrapper::SetupFunctionParameters( - abi_dump::FunctionDecl *functionp) const { + abi_util::FunctionIR *functionp, + const std::string &source_file) { clang::FunctionDecl::param_const_iterator param_it = function_decl_->param_begin(); + // If this is a CXXMethodDecl, we need to add the "this" pointer. + if (!SetupThisParameter(functionp, source_file)) { + return false; + } + while (param_it != function_decl_->param_end()) { - abi_dump::ParamDecl *function_fieldp = functionp->add_parameters(); - if (!function_fieldp) { - llvm::errs() << "Couldn't add parameter to method. Aborting\n"; - return false; - } // The linker set key is blank since that shows up in the mangled name. bool has_default_arg = (*param_it)->hasDefaultArg(); clang::QualType param_qt = (*param_it)->getType(); - bool should_dump_size = ShouldDumpSize(param_qt); - if (!SetupBasicNamedAndTypedDecl( - function_fieldp->mutable_basic_abi(), - (*param_it)->getType(), (*param_it)->getName(), - (*param_it)->getAccess(), has_default_arg ? "true" : "false", - should_dump_size)) { + if (!SetupFunctionParameter(functionp, param_qt, has_default_arg, + source_file)) { return false; } - function_fieldp->set_default_arg(has_default_arg); param_it++; } return true; } -bool FunctionDeclWrapper::SetupFunction(abi_dump::FunctionDecl *functionp, - const std::string &source_file) const { +bool FunctionDeclWrapper::SetupFunction(abi_util::FunctionIR *functionp, + const std::string &source_file) { // Go through all the parameters in the method and add them to the fields. // Also get the fully qualfied name. - functionp->set_source_file(source_file); - // Combine the function name and return type to form a NamedAndTypedDecl + functionp->SetSourceFile(source_file); + functionp->SetName(function_decl_->getQualifiedNameAsString()); clang::QualType return_type = function_decl_->getReturnType(); - bool should_dump_size = ShouldDumpSize(return_type); - return SetupBasicNamedAndTypedDecl( - functionp->mutable_basic_abi(), - return_type, function_decl_->getQualifiedNameAsString(), - function_decl_->getAccess(), "", should_dump_size) && - SetupTemplateInfo(functionp) && SetupFunctionParameters(functionp); + + functionp->SetReturnType(QualTypeToString(return_type)); + functionp->SetAccess(AccessClangToIR(function_decl_->getAccess())); + return CreateBasicNamedAndTypedDecl(return_type, source_file) && + SetupFunctionParameters(functionp, source_file) && + SetupTemplateInfo(functionp, source_file); } bool FunctionDeclWrapper::SetupTemplateInfo( - abi_dump::FunctionDecl *functionp) const { + abi_util::FunctionIR *functionp, + const std::string &source_file) { switch (function_decl_->getTemplatedKind()) { - case clang::FunctionDecl::TK_FunctionTemplate: { - clang::FunctionTemplateDecl *template_decl = - function_decl_->getDescribedFunctionTemplate(); - if (template_decl) { - clang::TemplateParameterList *template_parameter_list = - template_decl->getTemplateParameters(); - if (template_parameter_list && - !SetupTemplateParamNames(functionp->mutable_template_info(), - template_parameter_list)) { - return false; - } - } - break; - } case clang::FunctionDecl::TK_FunctionTemplateSpecialization: { const clang::TemplateArgumentList *arg_list = function_decl_->getTemplateSpecializationArgs(); - if (arg_list && - !SetupTemplateArguments(functionp->mutable_template_info(), - arg_list)) { + if (arg_list && !SetupTemplateArguments(arg_list, functionp, + source_file)) { return false; } break; @@ -303,11 +399,9 @@ bool FunctionDeclWrapper::SetupTemplateInfo( return true; } -std::unique_ptr -FunctionDeclWrapper::GetFunctionDecl() const { - std::unique_ptr abi_decl( - new abi_dump::FunctionDecl()); - std::string source_file = GetDeclSourceFile(function_decl_, cip_); +std::unique_ptr FunctionDeclWrapper::GetFunctionDecl() { + auto abi_decl = std::make_unique(); + std::string source_file = GetCachedDeclSourceFile(function_decl_, cip_); if (!SetupFunction(abi_decl.get(), source_file)) { return nullptr; } @@ -318,59 +412,110 @@ RecordDeclWrapper::RecordDeclWrapper( clang::MangleContext *mangle_contextp, clang::ASTContext *ast_contextp, const clang::CompilerInstance *compiler_instance_p, - const clang::RecordDecl *decl) - : ABIWrapper(mangle_contextp, ast_contextp, compiler_instance_p), - record_decl_(decl) { } + const clang::RecordDecl *decl, + std::set *type_cache, + abi_util::IRDumper *ir_dumper, + std::map &decl_to_source_cache, + const std::string &previous_record_stages) + : ABIWrapper(mangle_contextp, ast_contextp, compiler_instance_p, + type_cache, ir_dumper, decl_to_source_cache), + record_decl_(decl), previous_record_stages_(previous_record_stages) { } -bool RecordDeclWrapper::SetupRecordFields(abi_dump::RecordDecl *recordp) const { +bool RecordDeclWrapper::CreateAnonymousRecord( + const clang::RecordDecl *record_decl, const std::string &linker_set_key) { + RecordDeclWrapper record_decl_wrapper(mangle_contextp_, ast_contextp_, cip_, + record_decl, type_cache_, ir_dumper_, + decl_to_source_file_cache_, + linker_set_key); + return record_decl_wrapper.GetRecordDecl(); +} + +static const clang::RecordDecl *GetAnonymousRecord(clang::QualType type) { + const clang::Type *type_ptr = type.getTypePtr(); + assert(type_ptr != nullptr); + if (!type_ptr->isRecordType()) { + return nullptr; + } + const clang::TagDecl *tag_decl = type_ptr->getAsTagDecl(); + if (!tag_decl) { + return nullptr; + } + const clang::RecordDecl *record_decl = + llvm::dyn_cast(tag_decl); + + if (record_decl != nullptr && (!record_decl->hasNameForLinkage() || + record_decl->isAnonymousStructOrUnion())) { + return record_decl; + } + return nullptr; +} + +bool RecordDeclWrapper::SetupRecordFields(abi_util::RecordTypeIR *recordp, + const std::string &source_file) { clang::RecordDecl::field_iterator field = record_decl_->field_begin(); + uint32_t field_index = 0; + const clang::ASTRecordLayout &record_layout = + ast_contextp_->getASTRecordLayout(record_decl_); while (field != record_decl_->field_end()) { - abi_dump::RecordFieldDecl *record_fieldp = recordp->add_fields(); - if (!record_fieldp) { - llvm::errs() << " Couldn't add record field: " << field->getName() - << " to reference dump\n"; + clang::QualType field_type = field->getType(); + if (!CreateBasicNamedAndTypedDecl(field_type, source_file)) { + llvm::errs() << "Creation of Type failed\n"; return false; } - if (!SetupBasicNamedAndTypedDecl(record_fieldp->mutable_basic_abi(), - field->getType(), field->getName(), - field->getAccess(), "", true)) { + std::string field_type_str = QualTypeToString(field_type); + std::string field_name = field->getName(); + // Handle anoymous structs / unions as fields. + if (const clang::RecordDecl *anon_record_decl = + GetAnonymousRecord(field_type)) { + // We need to create a unique linker set key for anonymous structs / enums + // since clang just names them with fully_qualified_scope::anonymous. + field_type_str = + previous_record_stages_ + "::(anonymous)" + + std::to_string(field_index); + if (!CreateAnonymousRecord(anon_record_decl, field_type_str)) { return false; + } + } else if (const clang::EnumDecl *enum_decl = + GetAnonymousEnum(field_type)) { + // Handle anonymous enums. + field_type_str = QualTypeToString(enum_decl->getIntegerType()); } + uint64_t field_offset = record_layout.getFieldOffset(field_index); + recordp->AddRecordField(abi_util::RecordFieldIR( + field_name, field_type_str, field_offset, + AccessClangToIR(field->getAccess()))); field++; + field_index++; } return true; } bool RecordDeclWrapper::SetupCXXBases( - abi_dump::RecordDecl *cxxp, - const clang::CXXRecordDecl *cxx_record_decl) const { - assert(cxx_record_decl != nullptr); + abi_util::RecordTypeIR *cxxp, + const clang::CXXRecordDecl *cxx_record_decl) { + if (!cxx_record_decl || !cxxp) { + return false; + } clang::CXXRecordDecl::base_class_const_iterator base_class = cxx_record_decl->bases_begin(); while (base_class != cxx_record_decl->bases_end()) { - abi_dump::CXXBaseSpecifier *base_specifierp = cxxp->add_base_specifiers(); - if (!base_specifierp) { - llvm::errs() << " Couldn't add base specifier to reference dump\n"; - return false; - } std::string name = QualTypeToString(base_class->getType()); bool is_virtual = base_class->isVirtual(); - if (!SetupBasicNamedAndTypedDecl(base_specifierp->mutable_basic_abi(), - base_class->getType(), - "", base_class->getAccessSpecifier(), - "", false)) { - return false; - } - base_specifierp->set_is_virtual(is_virtual); + abi_util::AccessSpecifierIR access = + AccessClangToIR(base_class->getAccessSpecifier()); + cxxp->AddCXXBaseSpecifier(abi_util::CXXBaseSpecifierIR(name, is_virtual, + access)); base_class++; } return true; } bool RecordDeclWrapper::SetupRecordVTable( - abi_dump::RecordDecl *record_declp, - const clang::CXXRecordDecl *cxx_record_decl) const { - assert(cxx_record_decl != nullptr); + abi_util::RecordTypeIR *record_declp, + const clang::CXXRecordDecl *cxx_record_decl) { + if (!cxx_record_decl || !record_declp) { + return false; + } clang::VTableContextBase *base_vtable_contextp = ast_contextp_->getVTableContext(); const clang::Type *typep = cxx_record_decl->getTypeForDecl(); @@ -386,263 +531,254 @@ bool RecordDeclWrapper::SetupRecordVTable( } const clang::VTableLayout &vtable_layout = itanium_vtable_contextp->getVTableLayout(cxx_record_decl); - abi_dump::VTableLayout *vtablep = record_declp->mutable_vtable_layout(); - if (!vtablep) { - return false; - } + abi_util::VTableLayoutIR vtable_ir_layout; for (const auto &vtable_component : vtable_layout.vtable_components()) { - abi_dump::VTableComponent *added_vtable_component = - vtablep->add_vtable_components(); - if (!added_vtable_component || - !SetupRecordVTableComponent(added_vtable_component, vtable_component)) { + abi_util::VTableComponentIR added_component= + SetupRecordVTableComponent(vtable_component); + vtable_ir_layout.AddVTableComponent(std::move(added_component)); + } + record_declp->SetVTableLayout(std::move(vtable_ir_layout)); + return true; +} + +abi_util::VTableComponentIR RecordDeclWrapper::SetupRecordVTableComponent( + const clang::VTableComponent &vtable_component) { + abi_util::VTableComponentIR::Kind kind = + abi_util::VTableComponentIR::Kind::RTTI; + std::string mangled_component_name = ""; + llvm::raw_string_ostream ostream(mangled_component_name); + int64_t value = 0; + clang::VTableComponent::Kind clang_component_kind = + vtable_component.getKind(); + switch (clang_component_kind) { + case clang::VTableComponent::CK_VCallOffset: + kind = abi_util::VTableComponentIR::Kind::VCallOffset; + value = vtable_component.getVCallOffset().getQuantity(); + break; + case clang::VTableComponent::CK_VBaseOffset: + kind = abi_util::VTableComponentIR::Kind::VBaseOffset; + value = vtable_component.getVBaseOffset().getQuantity(); + break; + case clang::VTableComponent::CK_OffsetToTop: + kind = abi_util::VTableComponentIR::Kind::OffsetToTop; + value = vtable_component.getOffsetToTop().getQuantity(); + break; + case clang::VTableComponent::CK_RTTI: + { + kind = abi_util::VTableComponentIR::Kind::RTTI; + const clang::CXXRecordDecl *rtti_decl = + vtable_component.getRTTIDecl(); + assert(rtti_decl != nullptr); + mangled_component_name = + ABIWrapper::GetTypeLinkageName(rtti_decl->getTypeForDecl()); + } + break; + case clang::VTableComponent::CK_FunctionPointer: + case clang::VTableComponent::CK_CompleteDtorPointer: + case clang::VTableComponent::CK_DeletingDtorPointer: + case clang::VTableComponent::CK_UnusedFunctionPointer: + { + const clang::CXXMethodDecl *method_decl = + vtable_component.getFunctionDecl(); + assert(method_decl != nullptr); + switch (clang_component_kind) { + case clang::VTableComponent::CK_FunctionPointer: + kind = abi_util::VTableComponentIR::Kind::FunctionPointer; + mangled_component_name = GetMangledNameDecl(method_decl, + mangle_contextp_); + break; + case clang::VTableComponent::CK_CompleteDtorPointer: + kind = abi_util::VTableComponentIR::Kind::CompleteDtorPointer; + mangle_contextp_->mangleCXXDtor( + vtable_component.getDestructorDecl(), + clang::CXXDtorType::Dtor_Complete, ostream); + ostream.flush(); + break; + case clang::VTableComponent::CK_DeletingDtorPointer: + kind = abi_util::VTableComponentIR::Kind::DeletingDtorPointer; + mangle_contextp_->mangleCXXDtor( + vtable_component.getDestructorDecl(), + clang::CXXDtorType::Dtor_Deleting, ostream); + ostream.flush(); + break; + case clang::VTableComponent::CK_UnusedFunctionPointer: + kind = abi_util::VTableComponentIR::Kind::UnusedFunctionPointer; + break; + default: + break; + } + } + break; + default: + break; + } + return abi_util::VTableComponentIR(mangled_component_name, kind, value); +} + +bool RecordDeclWrapper::SetupTemplateInfo( + abi_util::RecordTypeIR *record_declp, + const clang::CXXRecordDecl *cxx_record_decl, + const std::string &source_file) { + assert(cxx_record_decl != nullptr); + const clang::ClassTemplateSpecializationDecl *specialization_decl = + clang::dyn_cast(cxx_record_decl); + if (specialization_decl) { + const clang::TemplateArgumentList *arg_list = + &specialization_decl->getTemplateArgs(); + if (arg_list && + !SetupTemplateArguments(arg_list, record_declp, source_file)) { return false; } } return true; } -bool RecordDeclWrapper::SetupRecordVTableComponent( - abi_dump::VTableComponent *added_vtable_component, - const clang::VTableComponent &vtable_component) const { - assert(added_vtable_component != nullptr); - abi_dump::VTableComponent_Kind kind = abi_dump::VTableComponent_Kind_RTTI; - std::string mangled_component_name = ""; - llvm::raw_string_ostream ostream(mangled_component_name); - int64_t value = 0; - clang::VTableComponent::Kind clang_component_kind = - vtable_component.getKind(); - switch (clang_component_kind) { - case clang::VTableComponent::CK_VCallOffset: - kind = abi_dump::VTableComponent_Kind_VCallOffset; - value = vtable_component.getVCallOffset().getQuantity(); - break; - case clang::VTableComponent::CK_VBaseOffset: - kind = abi_dump::VTableComponent_Kind_VBaseOffset; - value = vtable_component.getVBaseOffset().getQuantity(); - break; - case clang::VTableComponent::CK_OffsetToTop: - kind = abi_dump::VTableComponent_Kind_OffsetToTop; - value = vtable_component.getOffsetToTop().getQuantity(); - break; - case clang::VTableComponent::CK_RTTI: - { - kind = abi_dump::VTableComponent_Kind_RTTI; - const clang::CXXRecordDecl *rtti_decl = - vtable_component.getRTTIDecl(); - assert(rtti_decl != nullptr); - mangled_component_name = - ABIWrapper::GetTypeLinkageName(rtti_decl->getTypeForDecl()); - } - break; - case clang::VTableComponent::CK_FunctionPointer: - case clang::VTableComponent::CK_CompleteDtorPointer: - case clang::VTableComponent::CK_DeletingDtorPointer: - case clang::VTableComponent::CK_UnusedFunctionPointer: - { - const clang::CXXMethodDecl *method_decl = - vtable_component.getFunctionDecl(); - assert(method_decl != nullptr); - switch (clang_component_kind) { - case clang::VTableComponent::CK_FunctionPointer: - kind = abi_dump::VTableComponent_Kind_FunctionPointer; - mangled_component_name = GetMangledNameDecl(method_decl, - mangle_contextp_); - break; - case clang::VTableComponent::CK_CompleteDtorPointer: - kind = abi_dump::VTableComponent_Kind_CompleteDtorPointer; - mangle_contextp_->mangleCXXDtor( - vtable_component.getDestructorDecl(), - clang::CXXDtorType::Dtor_Complete, ostream); - ostream.flush(); - - break; - case clang::VTableComponent::CK_DeletingDtorPointer: - kind = abi_dump::VTableComponent_Kind_DeletingDtorPointer; - mangle_contextp_->mangleCXXDtor( - vtable_component.getDestructorDecl(), - clang::CXXDtorType::Dtor_Deleting, ostream); - ostream.flush(); - break; - case clang::VTableComponent::CK_UnusedFunctionPointer: - kind = abi_dump::VTableComponent_Kind_UnusedFunctionPointer; - default: - break; - } - } - break; - default: - return false; - } - added_vtable_component->set_kind(kind); - added_vtable_component->set_component_value(value); - added_vtable_component->set_mangled_component_name(mangled_component_name); - return true; -} - -bool RecordDeclWrapper::SetupTemplateInfo( - abi_dump::RecordDecl *record_declp, - const clang::CXXRecordDecl *cxx_record_decl) const { - assert(cxx_record_decl != nullptr); - if (cxx_record_decl->isTemplateDecl()) { - clang::ClassTemplateDecl *template_decl = - cxx_record_decl->getDescribedClassTemplate(); - if (template_decl) { - clang::TemplateParameterList *template_parameter_list = - template_decl->getTemplateParameters(); - if (template_parameter_list && - !SetupTemplateParamNames(record_declp->mutable_template_info(), - template_parameter_list)) { - return false; - } - } - } else { - const clang::ClassTemplateSpecializationDecl *specialization_decl = - clang::dyn_cast( - cxx_record_decl); - if(specialization_decl) { - const clang::TemplateArgumentList *arg_list = - &specialization_decl->getTemplateArgs(); - if (arg_list && - !SetupTemplateArguments(record_declp->mutable_template_info(), - arg_list)) { - return false; - } - } +bool RecordDeclWrapper::SetupRecordInfo(abi_util::RecordTypeIR *record_declp, + const std::string &source_file) { + if (!record_declp) { + return false; + } + + if (record_decl_->isStruct()) { + record_declp->SetRecordKind( + abi_util::RecordTypeIR::RecordKind::struct_kind); + } else if (record_decl_->isClass()) { + record_declp->SetRecordKind( + abi_util::RecordTypeIR::RecordKind::class_kind); + } else { + record_declp->SetRecordKind( + abi_util::RecordTypeIR::RecordKind::union_kind); } - return true; -} -bool RecordDeclWrapper::SetupRecordInfo(abi_dump::RecordDecl *record_declp, - const std::string &source_file) const { - std::string qualified_name = GetTagDeclQualifiedName(record_decl_); const clang::Type *basic_type = nullptr; if (!(basic_type = record_decl_->getTypeForDecl())) { return false; } - std::string mangled_name = ABIWrapper::GetTypeLinkageName(basic_type); - clang::QualType type = basic_type->getCanonicalTypeInternal(); - std::string linker_key = (mangled_name == "") ? qualified_name : mangled_name; - if (!SetupBasicNamedAndTypedDecl(record_declp->mutable_basic_abi(), - type, qualified_name, - record_decl_->getAccess(), linker_key, - true)) { + clang::QualType qual_type = basic_type->getCanonicalTypeInternal(); + if (!CreateExtendedType(qual_type, record_declp)) { return false; } - record_declp->set_mangled_record_name(mangled_name); - record_declp->set_source_file(source_file); - return true; + std::string record_qual_type_str = QualTypeToString(qual_type); + record_declp->SetSourceFile(source_file); + if (!record_decl_->hasNameForLinkage() || + record_decl_->isAnonymousStructOrUnion()) { + record_declp->SetLinkerSetKey(previous_record_stages_); + record_declp->SetAnonymity(true); + } else { + previous_record_stages_ = record_qual_type_str; + record_declp->SetLinkerSetKey(record_qual_type_str); + } + record_declp->SetAccess(AccessClangToIR(record_decl_->getAccess())); + return SetupRecordFields(record_declp, source_file) && + SetupCXXRecordInfo(record_declp, source_file); } bool RecordDeclWrapper::SetupCXXRecordInfo( - abi_dump::RecordDecl *record_declp) const { + abi_util::RecordTypeIR *record_declp, const std::string &source_file) { const clang::CXXRecordDecl *cxx_record_decl = clang::dyn_cast(record_decl_); if (!cxx_record_decl) { return true; } - return SetupTemplateInfo(record_declp, cxx_record_decl) && + return SetupTemplateInfo(record_declp, cxx_record_decl, source_file) && SetupCXXBases(record_declp, cxx_record_decl) && SetupRecordVTable(record_declp, cxx_record_decl); } -std::unique_ptr RecordDeclWrapper::GetRecordDecl() const { - std::unique_ptr abi_decl(new abi_dump::RecordDecl()); - std::string source_file = GetDeclSourceFile(record_decl_, cip_); - abi_dump::RecordDecl *record_declp = abi_decl.get(); - if (!SetupRecordInfo(record_declp, source_file) || - !SetupRecordFields(record_declp) || - !SetupCXXRecordInfo(abi_decl.get())) { +bool RecordDeclWrapper::GetRecordDecl() { + auto abi_decl = std::make_unique(); + std::string source_file = GetCachedDeclSourceFile(record_decl_, cip_); + if (!SetupRecordInfo(abi_decl.get(), source_file)) { llvm::errs() << "Setting up CXX Bases / Template Info failed\n"; - return nullptr; + return false; } - return abi_decl; + return ir_dumper_->AddLinkableMessageIR(abi_decl.get()); } EnumDeclWrapper::EnumDeclWrapper( clang::MangleContext *mangle_contextp, clang::ASTContext *ast_contextp, const clang::CompilerInstance *compiler_instance_p, - const clang::EnumDecl *decl) - : ABIWrapper(mangle_contextp, ast_contextp, compiler_instance_p), + const clang::EnumDecl *decl, + std::set *type_cache, + abi_util::IRDumper *ir_dumper, + std::map &decl_to_source_cache) + : ABIWrapper(mangle_contextp, ast_contextp, compiler_instance_p, + type_cache, ir_dumper, decl_to_source_cache), enum_decl_(decl) { } -bool EnumDeclWrapper::SetupEnumFields(abi_dump::EnumDecl *enump) const { +bool EnumDeclWrapper::SetupEnumFields(abi_util::EnumTypeIR *enump) { + if (!enump) { + return false; + } clang::EnumDecl::enumerator_iterator enum_it = enum_decl_->enumerator_begin(); while (enum_it != enum_decl_->enumerator_end()) { - abi_dump::EnumFieldDecl *enum_fieldp = enump->add_enum_fields(); std::string name = enum_it->getQualifiedNameAsString(); uint64_t field_value = enum_it->getInitVal().getExtValue(); - if (!enum_fieldp || - !SetupBasicNamedAndTypedDecl(enum_fieldp->mutable_basic_abi(), - enum_it->getType(), name, - enum_it->getAccess(), - std::to_string(field_value), true)) { - return false; - } - enum_fieldp->set_enum_field_value(field_value); + enump->AddEnumField(abi_util::EnumFieldIR(name, field_value)); enum_it++; } return true; } -bool EnumDeclWrapper::SetupEnum(abi_dump::EnumDecl *enump, - const std::string &source_file) const { +bool EnumDeclWrapper::SetupEnum(abi_util::EnumTypeIR *enum_type, + const std::string &source_file) { std::string enum_name = GetTagDeclQualifiedName(enum_decl_); - clang::QualType enum_type = enum_decl_->getIntegerType(); - if (!SetupBasicNamedAndTypedDecl(enump->mutable_basic_abi(), enum_type, - enum_name, enum_decl_->getAccess(), - enum_name, true) || - !SetupEnumFields(enump)) { + clang::QualType enum_qual_type = + enum_decl_->getTypeForDecl()->getCanonicalTypeInternal(); + if (!CreateExtendedType(enum_qual_type, enum_type)) { return false; } - enump->set_source_file(source_file); - return true; + enum_type->SetSourceFile(source_file); + enum_type->SetUnderlyingType(QualTypeToString(enum_decl_->getIntegerType())); + enum_type->SetAccess(AccessClangToIR(enum_decl_->getAccess())); + return SetupEnumFields(enum_type) && + CreateBasicNamedAndTypedDecl(enum_decl_->getIntegerType(), ""); } -std::unique_ptr EnumDeclWrapper::GetEnumDecl() const { - std::unique_ptr abi_decl(new abi_dump::EnumDecl()); - std::string source_file = GetDeclSourceFile(enum_decl_, cip_); +bool EnumDeclWrapper::GetEnumDecl() { + auto abi_decl = std::make_unique(); + std::string source_file = GetCachedDeclSourceFile(enum_decl_, cip_); if (!SetupEnum(abi_decl.get(), source_file)) { - llvm::errs() << "Setting up Enum fields failed\n"; - return nullptr; + llvm::errs() << "Setting up Enum failed\n"; + return false; } - return abi_decl; + return ir_dumper_->AddLinkableMessageIR(abi_decl.get()); } GlobalVarDeclWrapper::GlobalVarDeclWrapper( clang::MangleContext *mangle_contextp, clang::ASTContext *ast_contextp, const clang::CompilerInstance *compiler_instance_p, - const clang::VarDecl *decl) - : ABIWrapper(mangle_contextp, ast_contextp, compiler_instance_p), + const clang::VarDecl *decl,std::set *type_cache, + abi_util::IRDumper *ir_dumper, + std::map &decl_to_source_cache) + : ABIWrapper(mangle_contextp, ast_contextp, compiler_instance_p, type_cache, + ir_dumper, decl_to_source_cache), global_var_decl_(decl) { } bool GlobalVarDeclWrapper::SetupGlobalVar( - abi_dump::GlobalVarDecl *global_varp, - const std::string &source_file) const { + abi_util::GlobalVarIR *global_varp, + const std::string &source_file) { // Temporary fix : clang segfaults on trying to mangle global variable which // is a dependent sized array type. - std::string qualified_name = global_var_decl_->getQualifiedNameAsString(); std::string mangled_name = GetMangledNameDecl(global_var_decl_, mangle_contextp_); - if (!SetupBasicNamedAndTypedDecl( - global_varp->mutable_basic_abi(),global_var_decl_->getType(), - qualified_name, global_var_decl_->getAccess(), - mangled_name, true)) { + if (!CreateBasicNamedAndTypedDecl(global_var_decl_->getType(), source_file)) { return false; } - global_varp->set_source_file(source_file); + global_varp->SetSourceFile(source_file); + global_varp->SetName(global_var_decl_->getQualifiedNameAsString()); + global_varp->SetLinkerSetKey(mangled_name); + global_varp->SetReferencedType( + QualTypeToString(global_var_decl_->getType())); return true; } -std::unique_ptr -GlobalVarDeclWrapper::GetGlobalVarDecl() const { - std::unique_ptr - abi_decl(new abi_dump::GlobalVarDecl); - std::string source_file = GetDeclSourceFile(global_var_decl_, cip_); - if (!SetupGlobalVar(abi_decl.get(), source_file)) { - return nullptr; - } - return abi_decl; +bool GlobalVarDeclWrapper::GetGlobalVarDecl() { + auto abi_decl = std::make_unique(); + std::string source_file = GetCachedDeclSourceFile(global_var_decl_, cip_); + return SetupGlobalVar(abi_decl.get(), source_file) && + ir_dumper_->AddLinkableMessageIR(abi_decl.get()); } diff --git a/vndk/tools/header-checker/header-abi-dumper/src/abi_wrappers.h b/vndk/tools/header-checker/header-abi-dumper/src/abi_wrappers.h index 262c9cfdc..af222b08e 100644 --- a/vndk/tools/header-checker/header-abi-dumper/src/abi_wrappers.h +++ b/vndk/tools/header-checker/header-abi-dumper/src/abi_wrappers.h @@ -15,6 +15,8 @@ #ifndef ABI_WRAPPERS_H_ #define ABI_WRAPPERS_H_ +#include + #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-parameter" #pragma clang diagnostic ignored "-Wnested-anon-types" @@ -32,7 +34,10 @@ class ABIWrapper { public: ABIWrapper(clang::MangleContext *mangle_contextp, clang::ASTContext *ast_contextp, - const clang::CompilerInstance *cip); + const clang::CompilerInstance *cip, + std::set *type_cache, + abi_util::IRDumper *ir_dumper, + std::map &decl_to_source_cache); static std::string GetDeclSourceFile(const clang::Decl *decl, const clang::CompilerInstance *cip); @@ -41,123 +46,161 @@ class ABIWrapper { clang::MangleContext *mangle_context); protected: abi_dump::AccessSpecifier AccessClangToDump( - const clang::AccessSpecifier sp) const; + const clang::AccessSpecifier sp); + std::string GetCachedDeclSourceFile(const clang::Decl *decl, + const clang::CompilerInstance *cip); - bool SetupTemplateParamNames(abi_dump::TemplateInfo *tinfo, - clang::TemplateParameterList *pl) const; + bool SetupTemplateArguments(const clang::TemplateArgumentList *tl, + abi_util::TemplatedArtifactIR *ta, + const std::string &source_file); - bool SetupTemplateArguments(abi_dump::TemplateInfo *tinfo, - const clang::TemplateArgumentList *tl) const; + std::string QualTypeToString(const clang::QualType &sweet_qt); - std::string QualTypeToString(const clang::QualType &sweet_qt) const; + std::string GetTagDeclQualifiedName(const clang::TagDecl *decl); - std::string GetTagDeclQualifiedName(const clang::TagDecl *decl) const; + bool CreateBasicNamedAndTypedDecl(clang::QualType, + const std::string &source_file); + bool CreateBasicNamedAndTypedDecl( + clang::QualType canonical_type, + abi_util::TypeIR *typep, + const std::string &source_file); - bool SetupBasicTypeAbi(abi_dump::BasicTypeAbi *type_abi, - const clang::QualType type, bool dump_size) const; + bool CreateExtendedType( + clang::QualType canonical_type, + abi_util::TypeIR *typep); - bool SetupBasicNamedAndTypedDecl( - abi_dump::BasicNamedAndTypedDecl *basic_named_and_typed_decl, - const clang::QualType type, const std::string &name, - const clang::AccessSpecifier &access, std::string key, - bool dump_size) const; + clang::QualType GetReferencedType(const clang::QualType qual_type); - std::string GetTypeLinkageName(const clang::Type *typep) const; + std::string GetTypeLinkageName(const clang::Type *typep); -protected: + std::unique_ptr SetTypeKind(const clang::QualType qtype, + const std::string &source_file); + + + protected: const clang::CompilerInstance *cip_; clang::MangleContext *mangle_contextp_; clang::ASTContext *ast_contextp_; + std::set *type_cache_; + abi_util::IRDumper *ir_dumper_; + std::map &decl_to_source_file_cache_; }; class RecordDeclWrapper : public ABIWrapper { public: - RecordDeclWrapper(clang::MangleContext *mangle_contextp, - clang::ASTContext *ast_contextp, - const clang::CompilerInstance *compiler_instance_p, - const clang::RecordDecl *decl); + RecordDeclWrapper( + clang::MangleContext *mangle_contextp, clang::ASTContext *ast_contextp, + const clang::CompilerInstance *compiler_instance_p, + const clang::RecordDecl *decl, std::set *type_cache, + abi_util::IRDumper *ir_dumper, + std::map &decl_to_source_cache_, + const std::string &previous_record_stages); - std::unique_ptr GetRecordDecl() const; + bool GetRecordDecl(); private: const clang::RecordDecl *record_decl_; + std::string previous_record_stages_; private: - bool SetupRecordInfo(abi_dump::RecordDecl *record_declp, - const std::string &source_file) const; + bool SetupRecordInfo(abi_util::RecordTypeIR *type, + const std::string &source_file); - bool SetupRecordFields(abi_dump::RecordDecl *record_declp) const; + bool SetupRecordFields(abi_util::RecordTypeIR *record_declp, + const std::string &source_file); - bool SetupCXXBases(abi_dump::RecordDecl *cxxp, - const clang::CXXRecordDecl *cxx_record_decl) const; + bool SetupCXXBases(abi_util::RecordTypeIR *cxxp, + const clang::CXXRecordDecl *cxx_record_decl); - bool SetupTemplateInfo(abi_dump::RecordDecl *record_declp, - const clang::CXXRecordDecl *cxx_record_decl) const; + bool SetupTemplateInfo(abi_util::RecordTypeIR *record_declp, + const clang::CXXRecordDecl *cxx_record_decl, + const std::string &source_file); - bool SetupRecordVTable(abi_dump::RecordDecl *record_declp, - const clang::CXXRecordDecl *cxx_record_decl) const; - bool SetupRecordVTableComponent( - abi_dump::VTableComponent *added_vtable_component, - const clang::VTableComponent &vtable_component) const; + bool SetupRecordVTable(abi_util::RecordTypeIR *record_declp, + const clang::CXXRecordDecl *cxx_record_decl); + abi_util::VTableComponentIR SetupRecordVTableComponent( + const clang::VTableComponent &vtable_component); - bool SetupCXXRecordInfo(abi_dump::RecordDecl *record_declp) const; + bool SetupCXXRecordInfo(abi_util::RecordTypeIR *record_declp, + const std::string &source_file); + + bool CreateAnonymousRecord( + const clang::RecordDecl *decl, const std::string &linker_set_key); }; class FunctionDeclWrapper : public ABIWrapper { public: - FunctionDeclWrapper(clang::MangleContext *mangle_contextp, - clang::ASTContext *ast_contextp, - const clang::CompilerInstance *compiler_instance_p, - const clang::FunctionDecl *decl); + FunctionDeclWrapper( + clang::MangleContext *mangle_contextp, clang::ASTContext *ast_contextp, + const clang::CompilerInstance *compiler_instance_p, + const clang::FunctionDecl *decl, std::set *type_cache, + abi_util::IRDumper *ir_dumper, + std::map &decl_to_source_cache_); - std::unique_ptr GetFunctionDecl() const; + std::unique_ptr GetFunctionDecl(); private: const clang::FunctionDecl *function_decl_; private: - bool SetupFunction(abi_dump::FunctionDecl *methodp, - const std::string &source_file) const; + bool SetupFunction(abi_util::FunctionIR *methodp, + const std::string &source_file); - bool SetupTemplateInfo(abi_dump::FunctionDecl *functionp) const; + bool SetupTemplateInfo(abi_util::FunctionIR *functionp, + const std::string &source_file); + + bool SetupFunctionParameters(abi_util::FunctionIR *functionp, + const std::string &source_file); + + bool SetupFunctionParameter(abi_util::FunctionIR *functionp, + const clang::QualType qual_type, + bool has_default_arg, + const std::string &source_file); + + bool SetupThisParameter(abi_util::FunctionIR *functionp, + const std::string &source_file); - bool SetupFunctionParameters(abi_dump::FunctionDecl *functionp) const; }; class EnumDeclWrapper : public ABIWrapper { public: - EnumDeclWrapper(clang::MangleContext *mangle_contextp, - clang::ASTContext *ast_contextp, - const clang::CompilerInstance *compiler_instance_p, - const clang::EnumDecl *decl); + EnumDeclWrapper( + clang::MangleContext *mangle_contextp, clang::ASTContext *ast_contextp, + const clang::CompilerInstance *compiler_instance_p, + const clang::EnumDecl *decl, + std::set *type_cache, abi_util::IRDumper *ir_dumper, + std::map &decl_to_source_cache_); - std::unique_ptr GetEnumDecl() const; + bool GetEnumDecl(); private: const clang::EnumDecl *enum_decl_; private: - bool SetupEnum(abi_dump::EnumDecl *enump, - const std::string &source_file) const; - bool SetupEnumFields(abi_dump::EnumDecl *enump) const; + bool SetupEnum(abi_util::EnumTypeIR *type, + const std::string &source_file); + + bool SetupEnumFields(abi_util::EnumTypeIR *enump); }; class GlobalVarDeclWrapper : public ABIWrapper { public: - GlobalVarDeclWrapper(clang::MangleContext *mangle_contextp, - clang::ASTContext *ast_contextp, - const clang::CompilerInstance *compiler_instance_p, - const clang::VarDecl *decl); + GlobalVarDeclWrapper( + clang::MangleContext *mangle_contextp, clang::ASTContext *ast_contextp, + const clang::CompilerInstance *compiler_instance_p, + const clang::VarDecl *decl, + std::set *type_cache, abi_util::IRDumper *ir_dumper, + std::map &decl_to_source_cache_); - std::unique_ptr GetGlobalVarDecl() const; + bool GetGlobalVarDecl(); private: const clang::VarDecl *global_var_decl_; private: - bool SetupGlobalVar(abi_dump::GlobalVarDecl *global_varp, - const std::string &source_file) const; + bool SetupGlobalVar(abi_util::GlobalVarIR *global_varp, + const std::string &source_file); }; } //end namespace abi_wrapper -#endif // ABI_WRAPPERS_H_ +#endif // ABI_WRAPPERS_H_ diff --git a/vndk/tools/header-checker/header-abi-dumper/src/ast_processing.cpp b/vndk/tools/header-checker/header-abi-dumper/src/ast_processing.cpp index 66b3e9dec..abf58edb6 100644 --- a/vndk/tools/header-checker/header-abi-dumper/src/ast_processing.cpp +++ b/vndk/tools/header-checker/header-abi-dumper/src/ast_processing.cpp @@ -19,9 +19,6 @@ #include #include -#include -#include - #include #include #include @@ -33,83 +30,64 @@ using abi_wrapper::EnumDeclWrapper; using abi_wrapper::GlobalVarDeclWrapper; HeaderASTVisitor::HeaderASTVisitor( - abi_dump::TranslationUnit *tu_ptr, clang::MangleContext *mangle_contextp, clang::ASTContext *ast_contextp, const clang::CompilerInstance *compiler_instance_p, const std::string ¤t_file_name, const std::set &exported_headers, - const clang::Decl *tu_decl) - : tu_ptr_(tu_ptr), - mangle_contextp_(mangle_contextp), + const clang::Decl *tu_decl, + std::set *type_cache, + abi_util::IRDumper *ir_dumper) + : mangle_contextp_(mangle_contextp), ast_contextp_(ast_contextp), cip_(compiler_instance_p), current_file_name_(current_file_name), exported_headers_(exported_headers), - tu_decl_(tu_decl) { } + tu_decl_(tu_decl), + type_cache_(type_cache), + ir_dumper_(ir_dumper) { } bool HeaderASTVisitor::VisitRecordDecl(const clang::RecordDecl *decl) { - // Skip forward declaration. + // Skip forward declarations, dependent records. Also skip anonymous records + // as they will be traversed through record fields. if (!decl->isThisDeclarationADefinition() || - decl->getTypeForDecl()->isDependentType()) { + decl->getTypeForDecl()->isDependentType() || + decl->isAnonymousStructOrUnion() || + !decl->hasNameForLinkage()) { return true; } RecordDeclWrapper record_decl_wrapper( - mangle_contextp_, ast_contextp_, cip_, decl); - std::unique_ptr wrapped_record_decl = - record_decl_wrapper.GetRecordDecl(); - if (!wrapped_record_decl) { - llvm::errs() << "Getting Record Decl failed\n"; - return false; - } - abi_dump::RecordDecl *added_record_declp = tu_ptr_->add_records(); - if (!added_record_declp) { - return false; - } - *added_record_declp = *wrapped_record_decl; - return true; + mangle_contextp_, ast_contextp_, cip_, decl, type_cache_, + ir_dumper_, decl_to_source_file_cache_, ""); + return record_decl_wrapper.GetRecordDecl(); } bool HeaderASTVisitor::VisitEnumDecl(const clang::EnumDecl *decl) { if (!decl->isThisDeclarationADefinition() || - decl->getTypeForDecl()->isDependentType()) { + decl->getTypeForDecl()->isDependentType() || + !decl->hasNameForLinkage()) { return true; } EnumDeclWrapper enum_decl_wrapper( - mangle_contextp_, ast_contextp_, cip_, decl); - std::unique_ptr wrapped_enum_decl = - enum_decl_wrapper.GetEnumDecl(); - if (!wrapped_enum_decl) { - llvm::errs() << "Getting Enum Decl failed\n"; - return false; - } - abi_dump::EnumDecl *added_enum_declp = tu_ptr_->add_enums(); - if (!added_enum_declp) { - return false; - } - *added_enum_declp = *wrapped_enum_decl; - return true; -} + mangle_contextp_, ast_contextp_, cip_, decl, type_cache_, + ir_dumper_, decl_to_source_file_cache_); + return enum_decl_wrapper.GetEnumDecl(); + } -static bool MutateFunctionWithLinkageName(abi_dump::TranslationUnit *tu_ptr, - const abi_dump::FunctionDecl *fd, +static bool MutateFunctionWithLinkageName(const abi_util::FunctionIR *function, + abi_util::IRDumper *ir_dumper, std::string &linkage_name) { - abi_dump::FunctionDecl *added_function_declp = tu_ptr->add_functions(); - if (!added_function_declp) { - return false; - } - *added_function_declp = *fd; - added_function_declp->set_mangled_function_name(linkage_name); - assert(added_function_declp->mutable_basic_abi() != nullptr); - added_function_declp->mutable_basic_abi()->set_linker_set_key(linkage_name); - return true; + auto added_function = std::make_unique(); + *added_function = *function; + added_function->SetLinkerSetKey(linkage_name); + return ir_dumper->AddLinkableMessageIR(added_function.get()); } -static bool AddMangledFunctions(abi_dump::TranslationUnit *tu_ptr, - const abi_dump::FunctionDecl *fd, +static bool AddMangledFunctions(const abi_util::FunctionIR *function, + abi_util:: IRDumper *ir_dumper, std::vector &manglings) { for (auto &&mangling : manglings) { - if (!MutateFunctionWithLinkageName(tu_ptr, fd, mangling)) { + if (!MutateFunctionWithLinkageName(function, ir_dumper, mangling)) { return false; } } @@ -139,23 +117,20 @@ bool HeaderASTVisitor::VisitFunctionDecl(const clang::FunctionDecl *decl) { return true; } FunctionDeclWrapper function_decl_wrapper(mangle_contextp_, ast_contextp_, - cip_, decl); - std::unique_ptr wrapped_function_decl = - function_decl_wrapper.GetFunctionDecl(); - if (!wrapped_function_decl) { - llvm::errs() << "Getting Function Decl failed\n"; - return false; - } + cip_, decl, type_cache_, + ir_dumper_, + decl_to_source_file_cache_); + auto function_wrapper = function_decl_wrapper.GetFunctionDecl(); // Destructors and Constructors can have more than 1 symbol generated from the // same Decl. clang::index::CodegenNameGenerator cg(*ast_contextp_); std::vector manglings = cg.getAllManglings(decl); if (!manglings.empty()) { - return AddMangledFunctions(tu_ptr_, wrapped_function_decl.get(), manglings); + return AddMangledFunctions(function_wrapper.get(), ir_dumper_, manglings); } std::string linkage_name = ABIWrapper::GetMangledNameDecl(decl, mangle_contextp_); - return MutateFunctionWithLinkageName(tu_ptr_, wrapped_function_decl.get(), + return MutateFunctionWithLinkageName(function_wrapper.get(), ir_dumper_, linkage_name); } @@ -166,19 +141,10 @@ bool HeaderASTVisitor::VisitVarDecl(const clang::VarDecl *decl) { return true; } GlobalVarDeclWrapper global_var_decl_wrapper(mangle_contextp_, ast_contextp_, - cip_, decl); - std::unique_ptr wrapped_global_var_decl = - global_var_decl_wrapper.GetGlobalVarDecl(); - if (!wrapped_global_var_decl) { - llvm::errs() << "Getting Global Var Decl failed\n"; - return false; - } - abi_dump::GlobalVarDecl *added_global_var_declp = tu_ptr_->add_global_vars(); - if (!added_global_var_declp) { - return false; - } - *added_global_var_declp = *wrapped_global_var_decl; - return true; + cip_, decl, type_cache_, + ir_dumper_, + decl_to_source_file_cache_); + return global_var_decl_wrapper.GetGlobalVarDecl(); } static bool AreHeadersExported(const std::set &exported_headers) { @@ -191,6 +157,7 @@ bool HeaderASTVisitor::TraverseDecl(clang::Decl *decl) { return true; } std::string source_file = ABIWrapper::GetDeclSourceFile(decl, cip_); + decl_to_source_file_cache_.insert(std::make_pair(decl, source_file)); // If no exported headers are specified we assume the whole AST is exported. if ((decl != tu_decl_) && AreHeadersExported(exported_headers_) && (exported_headers_.find(source_file) == exported_headers_.end())) { @@ -210,18 +177,19 @@ HeaderASTConsumer::HeaderASTConsumer( exported_headers_(exported_headers) { } void HeaderASTConsumer::HandleTranslationUnit(clang::ASTContext &ctx) { - GOOGLE_PROTOBUF_VERIFY_VERSION; - std::ofstream text_output(out_dump_name_); - google::protobuf::io::OstreamOutputStream text_os(&text_output); + clang::PrintingPolicy policy(ctx.getPrintingPolicy()); + policy.SuppressTagKeyword = true; + ctx.setPrintingPolicy(policy); clang::TranslationUnitDecl *translation_unit = ctx.getTranslationUnitDecl(); std::unique_ptr mangle_contextp( ctx.createMangleContext()); - abi_dump::TranslationUnit tu; - std::string str_out; - HeaderASTVisitor v(&tu, mangle_contextp.get(), &ctx, cip_, file_name_, - exported_headers_, translation_unit); - if (!v.TraverseDecl(translation_unit) || - !google::protobuf::TextFormat::Print(tu, &text_os)) { + std::set type_cache; + std::unique_ptr ir_dumper = + abi_util::IRDumper::CreateIRDumper("protobuf", out_dump_name_); + HeaderASTVisitor v(mangle_contextp.get(), &ctx, cip_, file_name_, + exported_headers_, translation_unit, &type_cache, + ir_dumper.get()); + if (!v.TraverseDecl(translation_unit) || !ir_dumper->Dump()) { llvm::errs() << "Serialization to ostream failed\n"; ::exit(1); } diff --git a/vndk/tools/header-checker/header-abi-dumper/src/ast_processing.h b/vndk/tools/header-checker/header-abi-dumper/src/ast_processing.h index f3e6f8c76..569936b67 100644 --- a/vndk/tools/header-checker/header-abi-dumper/src/ast_processing.h +++ b/vndk/tools/header-checker/header-abi-dumper/src/ast_processing.h @@ -21,6 +21,8 @@ #include "proto/abi_dump.pb.h" #pragma clang diagnostic pop +#include + #include #include #include @@ -33,13 +35,14 @@ class HeaderASTVisitor : public clang::RecursiveASTVisitor { public: - HeaderASTVisitor(abi_dump::TranslationUnit *tu_ptr, - clang::MangleContext *mangle_contextp, + HeaderASTVisitor(clang::MangleContext *mangle_contextp, clang::ASTContext *ast_contextp, const clang::CompilerInstance *compiler_instance_p, const std::string ¤t_file_name, const std::set &exported_headers, - const clang::Decl *tu_decl); + const clang::Decl *tu_decl, + std::set *type_cache, + abi_util::IRDumper *ir_dumper); bool VisitRecordDecl(const clang::RecordDecl *decl); @@ -57,7 +60,6 @@ class HeaderASTVisitor } private: - abi_dump::TranslationUnit *tu_ptr_; clang::MangleContext *mangle_contextp_; clang::ASTContext *ast_contextp_; const clang::CompilerInstance *cip_; @@ -65,6 +67,11 @@ class HeaderASTVisitor const std::set &exported_headers_; // To optimize recursion into only exported abi. const clang::Decl *tu_decl_; + std::set *type_cache_; + abi_util::IRDumper *ir_dumper_; + // We cache the source file an AST node corresponds to, to avoid repeated + // calls to "realpath". + std::map decl_to_source_file_cache_; }; class HeaderASTConsumer : public clang::ASTConsumer { @@ -83,4 +90,4 @@ class HeaderASTConsumer : public clang::ASTConsumer { std::set exported_headers_; }; -#endif // AST_PROCESSING_H_ +#endif // AST_PROCESSING_H_ diff --git a/vndk/tools/header-checker/header-abi-linker/src/header_abi_linker.cpp b/vndk/tools/header-checker/header-abi-linker/src/header_abi_linker.cpp index 96616d1ce..bf6a2342b 100644 --- a/vndk/tools/header-checker/header-abi-linker/src/header_abi_linker.cpp +++ b/vndk/tools/header-checker/header-abi-linker/src/header_abi_linker.cpp @@ -65,8 +65,14 @@ static llvm::cl::opt no_filter( "no-filter", llvm::cl::desc("Do not filter any abi"), llvm::cl::Optional, llvm::cl::cat(header_linker_category)); +static llvm::cl::opt use_version_script( + "use-version-script", llvm::cl::desc("Use version script instead of .so" + " file to filter out function" + " and object symbols if available"), + llvm::cl::Optional, llvm::cl::cat(header_linker_category)); + static llvm::cl::opt so_file( - "so", llvm::cl::desc(""), llvm::cl::Optional, + "so", llvm::cl::desc(""), llvm::cl::Required, llvm::cl::cat(header_linker_category)); class HeaderAbiLinker { @@ -85,16 +91,20 @@ class HeaderAbiLinker { bool LinkAndDump(); + template + static std::string GetLinkageName(T &element) { + return element.type_info().linker_set_key(); + } + template + static std::string GetSourceFile(T &element) { + return element.type_info().source_file(); + } private: - bool LinkRecords(const abi_dump::TranslationUnit &dump_tu, - abi_dump::TranslationUnit *linked_tu); - + bool LinkTypes(const abi_dump::TranslationUnit &dump_tu, + abi_dump::TranslationUnit *linked_tu); bool LinkFunctions(const abi_dump::TranslationUnit &dump_tu, abi_dump::TranslationUnit *linked_tu); - bool LinkEnums(const abi_dump::TranslationUnit &dump_tu, - abi_dump::TranslationUnit *linked_tu); - bool LinkGlobalVars(const abi_dump::TranslationUnit &dump_tu, abi_dump::TranslationUnit *linked_tu); @@ -110,6 +120,8 @@ class HeaderAbiLinker { bool ParseSoFile(); + bool AddElfSymbols(abi_dump::TranslationUnit *linked_tu); + private: const std::vector &dump_files_; const std::vector &exported_header_dirs_; @@ -120,9 +132,8 @@ class HeaderAbiLinker { const std::string &api_; // TODO: Add to a map of std::sets instead. std::set exported_headers_; - std::set record_decl_set_; + std::set types_set_; std::set function_decl_set_; - std::set enum_decl_set_; std::set globvar_decl_set_; // Version Script Regex Matching. std::set functions_regex_matched_set; @@ -132,12 +143,32 @@ class HeaderAbiLinker { std::regex globvars_vs_regex_; }; +template +static bool AddElfSymbols(google::protobuf::RepeatedPtrField *dst, + Iterable symbols) { + for (auto &&symbol : symbols) { + auto *added_symbol = dst->Add(); + if (added_symbol == nullptr) { + return false; + } + added_symbol->set_name(symbol); + } + return true; +} + +// To be called right after parsing the .so file / version script. +bool HeaderAbiLinker::AddElfSymbols(abi_dump::TranslationUnit *linked_tu) { + + return ::AddElfSymbols(linked_tu->mutable_elf_functions(), function_decl_set_) + && ::AddElfSymbols(linked_tu->mutable_elf_objects(), globvar_decl_set_); +} + bool HeaderAbiLinker::LinkAndDump() { abi_dump::TranslationUnit linked_tu; std::ofstream text_output(out_dump_name_); google::protobuf::io::OstreamOutputStream text_os(&text_output); - // If a version script is available, we use that as a filter. - if (version_script.empty()) { + // If the user specifies that a version script should be used, use that. + if (!use_version_script) { exported_headers_ = abi_util::CollectAllExportedHeaders(exported_header_dirs_); if (!ParseSoFile()) { @@ -149,20 +180,20 @@ bool HeaderAbiLinker::LinkAndDump() { return false; } + AddElfSymbols(&linked_tu); + for (auto &&i : dump_files_) { abi_dump::TranslationUnit dump_tu; std::ifstream input(i); google::protobuf::io::IstreamInputStream text_is(&input); if (!google::protobuf::TextFormat::Parse(&text_is, &dump_tu) || - !LinkRecords(dump_tu, &linked_tu) || + !LinkTypes(dump_tu, &linked_tu) || !LinkFunctions(dump_tu, &linked_tu) || - !LinkEnums(dump_tu, &linked_tu) || !LinkGlobalVars(dump_tu, &linked_tu)) { llvm::errs() << "Failed to link elements\n"; return false; } } - if (!google::protobuf::TextFormat::Print(linked_tu, &text_os)) { llvm::errs() << "Serialization to ostream failed\n"; return false; @@ -170,22 +201,6 @@ bool HeaderAbiLinker::LinkAndDump() { return true; } -static std::string GetSymbol(const abi_dump::RecordDecl &element) { - return element.mangled_record_name(); -} - -static std::string GetSymbol(const abi_dump::FunctionDecl &element) { - return element.mangled_function_name(); -} - -static std::string GetSymbol(const abi_dump::EnumDecl &element) { - return element.basic_abi().linker_set_key(); -} - -static std::string GetSymbol(const abi_dump::GlobalVarDecl &element) { - return element.basic_abi().linker_set_key(); -} - static bool QueryRegexMatches(std::set *regex_matched_link_set, const std::regex *vs_regex, const std::string &symbol) { @@ -219,10 +234,10 @@ static std::regex CreateRegexMatchExprFromSet( return std::regex(all_regex_match_str); } +//TODO: make linking decls multi-threaded b/63590537. template inline bool HeaderAbiLinker::LinkDecl( - google::protobuf::RepeatedPtrField *dst, - std::set *link_set, + google::protobuf::RepeatedPtrField *dst, std::set *link_set, std::set *regex_matched_link_set, const std::regex *vs_regex, const google::protobuf::RepeatedPtrField &src, bool use_version_script) { assert(dst != nullptr); @@ -230,18 +245,20 @@ inline bool HeaderAbiLinker::LinkDecl( for (auto &&element : src) { // If we are not using a version script and exported headers are available, // filter out unexported abi. - if (!exported_headers_.empty() && - exported_headers_.find(element.source_file()) == + std::string source_file = GetSourceFile(element); + // Builtin types will not have source file information. + if (!exported_headers_.empty() && !source_file.empty() && + exported_headers_.find(source_file) == exported_headers_.end()) { continue; } + std::string element_str = GetLinkageName(element); // Check for the existence of the element in linked dump / symbol file. if (!use_version_script) { - if (!link_set->insert(element.basic_abi().linker_set_key()).second) { + if (!link_set->insert(element_str).second) { continue; - } + } } else { - std::string element_str = GetSymbol(element); std::set::iterator it = link_set->find(element_str); if (it == link_set->end()) { @@ -263,13 +280,52 @@ inline bool HeaderAbiLinker::LinkDecl( return true; } -bool HeaderAbiLinker::LinkRecords(const abi_dump::TranslationUnit &dump_tu, - abi_dump::TranslationUnit *linked_tu) { + +template<> +std::string HeaderAbiLinker::GetLinkageName ( + const abi_dump::FunctionDecl &element) { + return element.linker_set_key(); +} + +template<> +std::string HeaderAbiLinker::GetSourceFile ( + const abi_dump::FunctionDecl &element) { + return element.source_file(); +} + +template<> +std::string HeaderAbiLinker::GetLinkageName ( + const abi_dump::GlobalVarDecl &element) { + return element.linker_set_key(); +} + +template<> +std::string HeaderAbiLinker::GetSourceFile ( + const abi_dump::GlobalVarDecl &element) { + return element.source_file(); +} + +bool HeaderAbiLinker::LinkTypes(const abi_dump::TranslationUnit &dump_tu, + abi_dump::TranslationUnit *linked_tu) { assert(linked_tu != nullptr); - // Even if version scripts are available we take in records, since the symbols - // in the version script might reference a record exposed by the library. - return LinkDecl(linked_tu->mutable_records(), &record_decl_set_, nullptr, - nullptr, dump_tu.records(), false); + // Even if version scripts are available we take in types, since the symbols + // in the version script might reference a type exposed by the library. + return LinkDecl(linked_tu->mutable_record_types(), &types_set_, nullptr, + nullptr, dump_tu.record_types(), false) && + LinkDecl(linked_tu->mutable_enum_types(), &types_set_, nullptr, + nullptr, dump_tu.enum_types(), false) && + LinkDecl(linked_tu->mutable_builtin_types(), &types_set_, nullptr, + nullptr, dump_tu.builtin_types(), false) && + LinkDecl(linked_tu->mutable_pointer_types(), &types_set_, nullptr, + nullptr, dump_tu.pointer_types(), false) && + LinkDecl(linked_tu->mutable_rvalue_reference_types(), &types_set_, nullptr, + nullptr, dump_tu.rvalue_reference_types(), false) && + LinkDecl(linked_tu->mutable_lvalue_reference_types(), &types_set_, nullptr, + nullptr, dump_tu.lvalue_reference_types(), false) && + LinkDecl(linked_tu->mutable_array_types(), &types_set_, nullptr, + nullptr, dump_tu.array_types(), false) && + LinkDecl(linked_tu->mutable_qualified_types(), &types_set_, nullptr, + nullptr, dump_tu.qualified_types(), false); } bool HeaderAbiLinker::LinkFunctions(const abi_dump::TranslationUnit &dump_tu, @@ -281,15 +337,6 @@ bool HeaderAbiLinker::LinkFunctions(const abi_dump::TranslationUnit &dump_tu, (!version_script_.empty() || !so_file_.empty())); } -bool HeaderAbiLinker::LinkEnums(const abi_dump::TranslationUnit &dump_tu, - abi_dump::TranslationUnit *linked_tu) { - assert(linked_tu != nullptr); - // Even if version scripts are available we take in records, since the symbols - // in the version script might reference an enum exposed by the library. - return LinkDecl(linked_tu->mutable_enums(), &enum_decl_set_, nullptr, - nullptr, dump_tu.enums(), false); -} - bool HeaderAbiLinker::LinkGlobalVars(const abi_dump::TranslationUnit &dump_tu, abi_dump::TranslationUnit *linked_tu) { assert(linked_tu != nullptr); diff --git a/vndk/tools/header-checker/header-abi-util/include/header_abi_util.h b/vndk/tools/header-checker/header-abi-util/include/header_abi_util.h index b335cdd6c..5f18ab729 100644 --- a/vndk/tools/header-checker/header-abi-util/include/header_abi_util.h +++ b/vndk/tools/header-checker/header-abi-util/include/header_abi_util.h @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -128,4 +129,60 @@ class ELFSoFileParser : public SoFileParser { bool IsSymbolExported(const Elf_Sym *elf_sym) const; }; +template +std::vector FindRemovedElements( + const std::map &old_elements_map, + const std::map &new_elements_map) { + std::vector removed_elements; + for (auto &&map_element : old_elements_map) { + auto element_key = map_element.first; + auto new_element = new_elements_map.find(element_key); + if (new_element == new_elements_map.end()) { + removed_elements.emplace_back(map_element.second); + } + } + return removed_elements; +} + +template +inline void AddToMap(std::map *dst, Iterable &src, F get_key) { + for (auto &&element : src) { + dst->insert(std::make_pair(get_key(&element), &element)); + } +} + +template +inline void AddToSet(std::set *dst, Iterable &src, F get_key) { + for (auto &&element : src) { + dst->insert(get_key(element)); + } +} + +template +std::vector> FindCommonElements( + const std::map &old_elements_map, + const std::map &new_elements_map) { + std::vector> common_elements; + typename std::map::const_iterator old_element = + old_elements_map.begin(); + typename std::map::const_iterator new_element = + new_elements_map.begin(); + while (old_element != old_elements_map.end() && + new_element != new_elements_map.end()) { + if (old_element->first == new_element->first) { + common_elements.emplace_back(std::make_pair( + old_element->second, new_element->second)); + old_element++; + new_element++; + continue; + } + if (old_element->first < new_element->first) { + old_element++; + } else { + new_element++; + } + } + return common_elements; +} + } // namespace abi_util diff --git a/vndk/tools/header-checker/header-abi-util/include/ir_representation.h b/vndk/tools/header-checker/header-abi-util/include/ir_representation.h new file mode 100644 index 000000000..0567d0e8b --- /dev/null +++ b/vndk/tools/header-checker/header-abi-util/include/ir_representation.h @@ -0,0 +1,1126 @@ +// Copyright (C) 2017 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. +#ifndef IR_ +#define IR_ + +#include +#include +#include +#include +#include +#include + +// Classes which act as middle-men between clang AST parsing routines and +// message format specific dumpers. +namespace abi_util { + +enum CompatibilityStatusIR { + Compatible = 0, + UnreferencedChanges = 1, + Extension = 4, + Incompatible = 8 +}; + +static inline CompatibilityStatusIR operator|(CompatibilityStatusIR f, + CompatibilityStatusIR s) { + return static_cast( + static_cast::type>(f) | + static_cast::type>(s)); +} + +static inline CompatibilityStatusIR operator&( + CompatibilityStatusIR f, CompatibilityStatusIR s) { + return static_cast( + static_cast::type>(f) & + static_cast::type>(s)); +} + +enum AccessSpecifierIR { + PublicAccess = 1, + ProtectedAccess = 2, + PrivateAccess = 3 +}; + +enum LinkableMessageKind { + RecordTypeKind, + EnumTypeKind, + PointerTypeKind, + QualifiedTypeKind, + ArrayTypeKind, + LvalueReferenceTypeKind, + RvalueReferenceTypeKind, + BuiltinTypeKind, + FunctionKind, + GlobalVarKind +}; + +class LinkableMessageIR { + public: + const std::string &GetLinkerSetKey() const { + return linker_set_key_; + } + + void SetSourceFile(const std::string &source_file) { + source_file_ = source_file; + } + + void SetLinkerSetKey(const std::string &linker_set_key) { + linker_set_key_ = linker_set_key; + } + + const std::string &GetSourceFile() const { + return source_file_; + } + + virtual LinkableMessageKind GetKind() const = 0; + + virtual ~LinkableMessageIR() {} + + protected: + // The source file where this message comes from. This will be an empty string + // for built-in types + std::string source_file_; + std::string linker_set_key_; +}; + +class BasicTypeInfoIR { + public: + BasicTypeInfoIR(const std::string &name, const std::string &type, + const std::string linker_set_key, uint64_t size, + uint32_t alignment) + : name_(name), referenced_type_(type), linker_set_key_(linker_set_key) , + size_(size), alignment_(alignment) { } + + BasicTypeInfoIR() { } + + std::string GetLinkerSetKey() { + return linker_set_key_; + } + + protected: + std::string name_; + std::string referenced_type_; + std::string linker_set_key_; + uint64_t size_; + uint32_t alignment_; +}; + +// TODO: Break this up into types with sizes and those without types ? +class TypeIR : public LinkableMessageIR { + public: + void SetName(const std::string &name) { + name_ = name; + } + + const std::string &GetName() const { + return name_; + } + + void SetReferencedType(const std::string &type) { + referenced_type_ = type; + } + + const std::string &GetReferencedType() const { + return referenced_type_; + } + + void SetSize(uint64_t size) { + size_ = size; + } + uint64_t GetSize() const { + return size_; + } + void SetAlignment(uint32_t alignment) { + alignment_ = alignment; + } + uint32_t GetAlignment() const { + return alignment_; + } + ~TypeIR() override { } + + protected: + std::string name_; + std::string referenced_type_; + uint64_t size_; + uint32_t alignment_; +}; + +class VTableComponentIR { + public: + enum Kind { + VCallOffset = 0, + VBaseOffset = 1, + OffsetToTop = 2, + RTTI = 3, + FunctionPointer = 4, + CompleteDtorPointer = 5, + DeletingDtorPointer = 6, + UnusedFunctionPointer = 7 + }; + + VTableComponentIR(const std::string &name, Kind kind, int64_t value) + : component_name_(name), kind_(kind), value_(value) { } + + VTableComponentIR() { } + + Kind GetKind() const { + return kind_; + } + + int64_t GetValue() const { + return value_; + } + + const std::string &GetName() const { + return component_name_; + } + + protected: + std::string component_name_; + Kind kind_; + int64_t value_; +}; + +class VTableLayoutIR { + public: + void AddVTableComponent(VTableComponentIR &&vtable_component) { + vtable_components_.emplace_back(std::move(vtable_component)); + } + + const std::vector &GetVTableComponents() const { + return vtable_components_; + } + + uint64_t GetVTableNumEntries() const { + return vtable_components_.size(); + } + + protected: + std::vector vtable_components_; +}; + +class CXXBaseSpecifierIR { + public: + CXXBaseSpecifierIR(const std::string &type, bool is_virtual, + AccessSpecifierIR access) : + referenced_type_(type), is_virtual_(is_virtual), access_(access) { } + + CXXBaseSpecifierIR() { } + + const std::string &GetReferencedType() const { + return referenced_type_; + } + + bool IsVirtual() const { + return is_virtual_; + } + + AccessSpecifierIR GetAccess() const { + return access_; + } + + protected: + std::string referenced_type_; + bool is_virtual_; + AccessSpecifierIR access_; +}; + +class TemplateElementIR { + public: + TemplateElementIR(std::string &&type) + : referenced_type_(std::move(type)) { } + + TemplateElementIR(const std::string &type) + : referenced_type_(type) { } + + TemplateElementIR() { } + + const std::string &GetReferencedType() const { + return referenced_type_; + } + + protected: + std::string referenced_type_; +}; + +class TemplateInfoIR { + public: + void AddTemplateElement(TemplateElementIR &&element) { + template_elements_.emplace_back(element); + } + + const std::vector &GetTemplateElements() const { + return template_elements_; + } + + protected: + std::vector template_elements_; +}; + +class TemplatedArtifactIR { + public: + void SetTemplateInfo(TemplateInfoIR &&template_info) { + template_info_ = std::move(template_info); + } + + const std::vector &GetTemplateElements() const { + return template_info_.GetTemplateElements(); + } + + protected: + TemplateInfoIR template_info_; +}; + +class RecordFieldIR { + public: + RecordFieldIR(const std::string &name, const std::string &type, + uint64_t offset, AccessSpecifierIR access) + : name_(name), referenced_type_(type), offset_(offset), + access_(access) { } + + RecordFieldIR() { } + + const std::string &GetName() const { + return name_; + } + + const std::string &GetReferencedType() const { + return referenced_type_; + } + + uint64_t GetOffset() const { + return offset_; + } + + AccessSpecifierIR GetAccess() const { + return access_; + } + + protected: + std::string name_; + std::string referenced_type_; + uint64_t offset_; + AccessSpecifierIR access_; +}; + +class RecordTypeIR: public TypeIR, public TemplatedArtifactIR { + public: + enum RecordKind { + struct_kind, + class_kind, + union_kind + }; + + void AddRecordField(RecordFieldIR &&field) { + fields_.emplace_back(std::move(field)); + } + + void SetRecordFields(std::vector &&fields) { + fields_ = std::move(fields); + } + + void SetVTableLayout(VTableLayoutIR &&vtable_layout) { + vtable_layout_ = std::move(vtable_layout); + } + + const VTableLayoutIR &GetVTableLayout() const { + return vtable_layout_; + } + + void AddCXXBaseSpecifier(CXXBaseSpecifierIR &&base_specifier) { + bases_.emplace_back(std::move(base_specifier)); + } + + void SetCXXBaseSpecifiers(std::vector &&bases) { + bases_ = std::move(bases); + } + + const std::vector &GetBases() const { + return bases_; + } + + void SetAccess(AccessSpecifierIR access) { access_ = access;} + + AccessSpecifierIR GetAccess() const { + return access_; + } + + const std::vector &GetFields() const { + return fields_; + } + + LinkableMessageKind GetKind() const override { + return LinkableMessageKind::RecordTypeKind; + } + + uint64_t GetVTableNumEntries() const { + return vtable_layout_.GetVTableNumEntries(); + } + + void SetRecordKind(RecordKind record_kind) { + record_kind_ = record_kind; + } + + RecordKind GetRecordKind() const { + return record_kind_; + } + + void SetAnonymity(bool is_anonymous) { + is_anonymous_ = is_anonymous; + } + + bool IsAnonymous() const { + return is_anonymous_; + } + + protected: + std::vector fields_; + VTableLayoutIR vtable_layout_; + std::vector bases_; + AccessSpecifierIR access_; + bool is_anonymous_; + RecordKind record_kind_; +}; + +class EnumFieldIR { + public: + EnumFieldIR(const std::string &name, int value) + : name_(name), value_(value) { } + const std::string &GetName() const { + return name_; + } + + int GetValue() const { + return value_; + } + + protected: + std::string name_; + int value_; +}; + +class EnumTypeIR : public TypeIR { + public: + // Add Methods to get information from the IR. + void AddEnumField(EnumFieldIR &&field) { + fields_.emplace_back(std::move(field)); + } + + void SetAccess(AccessSpecifierIR access) { access_ = access;} + + LinkableMessageKind GetKind() const override { + return LinkableMessageKind::EnumTypeKind; + } + + AccessSpecifierIR GetAccess() const { + return access_; + } + + void SetUnderlyingType(std::string &&underlying_type) { + underlying_type_ = std::move(underlying_type); + } + + void SetUnderlyingType(const std::string &underlying_type) { + underlying_type_ = underlying_type; + } + + const std::string &GetUnderlyingType() const { + return underlying_type_; + } + + void SetFields(std::vector &&fields) { + fields_ = std::move(fields); + } + + const std::vector &GetFields() const { + return fields_; + } + + protected: + std::vector fields_; + std::string underlying_type_; + AccessSpecifierIR access_; +}; + +class ArrayTypeIR : public TypeIR { + public: + LinkableMessageKind GetKind() const override { + return LinkableMessageKind::ArrayTypeKind; + } +}; + +class PointerTypeIR : public TypeIR { + public: + LinkableMessageKind GetKind() const override { + return LinkableMessageKind::PointerTypeKind; + } +}; + +class BuiltinTypeIR : public TypeIR { + public: + void SetSignedness(bool is_unsigned) { + is_unsigned_ = is_unsigned; + } + + bool IsUnsigned() const { + return is_unsigned_; + } + + void SetIntegralType(bool is_integral_type) { + is_integral_type_ = is_integral_type; + } + + bool IsIntegralType() const { + return is_integral_type_; + } + + public: + LinkableMessageKind GetKind() const override { + return LinkableMessageKind::BuiltinTypeKind; + } + + protected: + bool is_unsigned_; + bool is_integral_type_; +}; + +class LvalueReferenceTypeIR : public TypeIR { + public: + LinkableMessageKind GetKind() const override { + return LinkableMessageKind::LvalueReferenceTypeKind; + } +}; + +class RvalueReferenceTypeIR : public TypeIR { + public: + LinkableMessageKind GetKind() const override { + return LinkableMessageKind::RvalueReferenceTypeKind; + } +}; + +class QualifiedTypeIR : public TypeIR { + public: + void SetConstness(bool is_const) { + is_const_ = is_const; + } + + bool IsConst() const { + return is_const_; + } + + void SetRestrictedness(bool is_restricted) { + is_restricted_ = is_restricted; + } + + bool IsRestricted() const { + return is_restricted_; + } + + void SetVolatility(bool is_volatile) { + is_volatile_ = is_volatile; + } + + bool IsVolatile() const { + return is_volatile_; + } + + public: + LinkableMessageKind GetKind() const override { + return LinkableMessageKind::QualifiedTypeKind; + } + + protected: + bool is_const_; + bool is_restricted_; + bool is_volatile_; +}; + +class GlobalVarIR: public LinkableMessageIR { + public: + // Add Methods to get information from the IR. + void SetReferencedType(const std::string &type) { + referenced_type_ = type; + } + + const std::string &GetReferencedType() const { + return referenced_type_; + } + + void SetName(std::string &&name) { + name_ = std::move(name); + } + + void SetName(const std::string &name) { + name_ = name; + } + + const std::string &GetName() const { + return name_; + } + + AccessSpecifierIR GetAccess() const { + return access_; + } + + LinkableMessageKind GetKind() const override { + return LinkableMessageKind::GlobalVarKind; + } + + protected: + std::string referenced_type_; // underlying type + std::string name_; + AccessSpecifierIR access_; +}; + +class ParamIR { + public: + ParamIR(const std::string &type, bool is_default) : + referenced_type_(type) , is_default_(is_default) {} + + const std::string &GetReferencedType() const { + return referenced_type_; + } + + bool GetIsDefault() const { + return is_default_; + } + + protected: + std::string referenced_type_; + bool is_default_; +}; + +class FunctionIR : public LinkableMessageIR, public TemplatedArtifactIR { + public: + void SetReturnType(const std::string &type) { + return_type_ = type; + } + + const std::string &GetReturnType() const { + return return_type_; + } + + void AddParameter(ParamIR &¶meter) { + parameters_.emplace_back(std::move(parameter)); + } + + void SetAccess(AccessSpecifierIR access) { + access_ = access; + } + + AccessSpecifierIR GetAccess() const { + return access_; + } + + LinkableMessageKind GetKind() const override { + return LinkableMessageKind::FunctionKind; + } + + const std::vector &GetParameters() const { + return parameters_; + } + + void SetName(const std::string &name) { + name_ = name; + } + + const std::string &GetName() const { + return name_; + } + + protected: + std::string return_type_; // return type reference + std::string linkage_name_; + std::string name_; + std::vector parameters_; + AccessSpecifierIR access_; +}; + +class ElfSymbolIR { + public: + enum ElfSymbolKind { + ElfFunctionKind, + ElfObjectKind + }; + + const std::string GetName() const { + return name_; + } + + ElfSymbolIR(const std::string &name) : name_(name) { } + + virtual ElfSymbolKind GetKind() const = 0; + + virtual ~ElfSymbolIR() { } + + protected: + std::string name_; +}; + +class ElfFunctionIR : public ElfSymbolIR{ + public: + ElfSymbolKind GetKind() const override { + return ElfFunctionKind; + } + + ElfFunctionIR(const std::string &name) : ElfSymbolIR(name) { } +}; + +class ElfObjectIR : public ElfSymbolIR { + public: + ElfSymbolKind GetKind() const override { + return ElfObjectKind; + } + + ElfObjectIR(const std::string &name) : ElfSymbolIR(name) { } +}; + +class IRDumper { + public: + IRDumper(const std::string &dump_path) : dump_path_(dump_path) { } + + static std::unique_ptr CreateIRDumper(const std::string &type, + const std::string &dump_path); + + virtual bool AddLinkableMessageIR(const LinkableMessageIR *) = 0; + + virtual bool Dump() = 0; + + virtual ~IRDumper() {} + + protected: + const std::string &dump_path_; +}; + +class TextFormatToIRReader { + public: + TextFormatToIRReader(const std::string &dump_path) : dump_path_(dump_path) { } + + const std::vector &GetFunctions() const { + return functions_; + } + + const std::vector &GetGlobalVariables() const { + return global_variables_; + } + + const std::vector &GetRecordTypes() const { + return record_types_; + } + + const std::vector &GetEnumTypes() const { + return enum_types_; + } + + const std::vector &GetLvalueReferenceTypes() const { + return lvalue_reference_types_; + } + + const std::vector &GetRvalueReferenceTypes() const { + return rvalue_reference_types_; + } + + const std::vector &GetQualifiedTypes() const { + return qualified_types_; + } + + const std::vector &GetArrayTypes() const { + return array_types_; + } + + const std::vector &GetPointerTypes() const { + return pointer_types_; + } + + const std::vector &GetBuiltinTypes() const { + return builtin_types_; + } + + const std::vector &GetElfFunctions() const { + return elf_functions_; + } + + const std::vector &GetElfObjects() const { + return elf_objects_; + } + + virtual bool ReadDump() = 0; + + virtual ~TextFormatToIRReader() { } + + static std::unique_ptr CreateTextFormatToIRReader( + const std::string &text_format, const std::string &dump_path); + + protected: + const std::string &dump_path_; + std::vector functions_; + std::vector global_variables_; + std::vector record_types_; + std::vector enum_types_; + std::vector pointer_types_; + std::vector lvalue_reference_types_; + std::vector rvalue_reference_types_; + std::vector array_types_; + std::vector builtin_types_; + std::vector qualified_types_; + std::vector elf_functions_; + std::vector elf_objects_; +}; + +class DiffMessageIR { + public: + virtual LinkableMessageKind Kind() const = 0; + void SetName(const std::string &name) { + name_ = name; + } + + const std::string &GetName() const { + return name_; + } + + virtual ~DiffMessageIR() { } + + protected: + std::string name_; +}; + +class AccessSpecifierDiffIR { + public: + AccessSpecifierDiffIR(AccessSpecifierIR old_access, + AccessSpecifierIR new_access) + : old_access_(old_access), new_access_(new_access) { } + + protected: + AccessSpecifierIR old_access_; + AccessSpecifierIR new_access_; +}; + +class TypeDiffIR { + public: + TypeDiffIR(std::pair &&sizes, + std::pair &&alignment) + : sizes_(std::move(sizes)), alignments_(std::move(alignment)) { } + + const std::pair &GetSizes() const { + return sizes_; + } + + const std::pair &GetAlignments() const { + return alignments_; + } + + protected: + std::pair sizes_; + std::pair alignments_; +}; + +class VTableLayoutDiffIR { + public: + VTableLayoutDiffIR(const VTableLayoutIR &old_layout, + const VTableLayoutIR &new_layout) + : old_layout_(old_layout), new_layout_(new_layout) { } + + const VTableLayoutIR &GetOldVTable() const { + return old_layout_; + } + + const VTableLayoutIR &GetNewVTable() const { + return new_layout_; + } + + protected: + const VTableLayoutIR &old_layout_; + const VTableLayoutIR &new_layout_; +}; + +class RecordFieldDiffIR { + public: + RecordFieldDiffIR(const RecordFieldIR *old_field, + const RecordFieldIR *new_field) + : old_field_(old_field), new_field_(new_field) { } + const RecordFieldIR *GetOldField() const { + return old_field_; + } + + const RecordFieldIR *GetNewField() const { + return new_field_; + } + + protected: + const RecordFieldIR *old_field_; + const RecordFieldIR *new_field_; +}; + +class CXXBaseSpecifierDiffIR { + public: + CXXBaseSpecifierDiffIR( + const std::vector &old_base_specifiers, + const std::vector &new_base_specifiers) + : old_base_specifiers_(old_base_specifiers), + new_base_specifiers_(new_base_specifiers) { } + const std::vector &GetOldBases() const { + return old_base_specifiers_; + } + + const std::vector &GetNewBases() const { + return new_base_specifiers_; + } + + protected: + const std::vector &old_base_specifiers_; + const std::vector &new_base_specifiers_; +}; + +class RecordTypeDiffIR : public DiffMessageIR { + public: + LinkableMessageKind Kind() const override { + return LinkableMessageKind::RecordTypeKind; + } + + void SetFieldDiffs(std::vector &&field_diffs) { + field_diffs_ = std::move(field_diffs); + } + + const std::vector &GetFieldDiffs() const { + return field_diffs_; + } + + void SetFieldsRemoved(std::vector &&fields_removed) { + fields_removed_ = std::move(fields_removed); + } + + const std::vector &GetFieldsRemoved() const { + return fields_removed_; + } + + void SetVTableLayoutDiff(std::unique_ptr &&vtable_diffs) { + vtable_diffs_ = std::move(vtable_diffs); + } + + void SetTypeDiff(std::unique_ptr &&type_diff) { + type_diff_ = std::move(type_diff); + } + + void SetAccessDiff(std::unique_ptr &&access_diff) { + access_diff_ = std::move(access_diff); + } + + void SetBaseSpecifierDiffs( + std::unique_ptr &&base_diffs) { + base_specifier_diffs_ = std::move(base_diffs); + } + + bool DiffExists() const { + return (type_diff_ != nullptr) || (vtable_diffs_ != nullptr) || + (fields_removed_.size() != 0) || (field_diffs_.size() != 0) || + (access_diff_ != nullptr) || (base_specifier_diffs_ != nullptr); + } + + const TypeDiffIR *GetTypeDiff() const { + return type_diff_.get(); + } + + const VTableLayoutDiffIR *GetVTableLayoutDiff() const { + return vtable_diffs_.get(); + } + + const CXXBaseSpecifierDiffIR *GetBaseSpecifiers() const { + return base_specifier_diffs_.get(); + } + + protected: + // optional implemented with vector / std::unique_ptr. + std::unique_ptr type_diff_; + std::unique_ptr vtable_diffs_; + std::vector field_diffs_; + std::vector fields_removed_; + std::unique_ptr access_diff_; + std::unique_ptr base_specifier_diffs_; + // Template Diffs are not needed since they will show up in the linker set + // key. +}; + +class EnumFieldDiffIR { + public: + EnumFieldDiffIR(const EnumFieldIR *old_field, const EnumFieldIR *new_field) + : old_field_(old_field), new_field_(new_field) { } + + const EnumFieldIR *GetOldField() const { + return old_field_; + } + + const EnumFieldIR *GetNewField() const { + return new_field_; + } + + protected: + const EnumFieldIR *old_field_; + const EnumFieldIR *new_field_; +}; + +class EnumTypeDiffIR : public DiffMessageIR { + public: + void SetFieldsRemoved(std::vector &&fields_removed) { + fields_removed_ = std::move(fields_removed); + } + + const std::vector &GetFieldsRemoved() const { + return fields_removed_; + } + + void SetFieldsAdded(std::vector &&fields_added) { + fields_added_ = std::move(fields_added); + } + + const std::vector &GetFieldsAdded() const { + return fields_added_; + } + + void SetFieldsDiff(std::vector &&fields_diff) { + fields_diff_ = std::move(fields_diff); + } + + const std::vector &GetFieldsDiff() const { + return fields_diff_; + } + + void SetUnderlyingTypeDiff( + std::unique_ptr> &&utype_diff) { + underlying_type_diff_ = std::move(utype_diff); + } + + const std::pair *GetUnderlyingTypeDiff() const { + return underlying_type_diff_.get(); + } + + bool IsExtended() const { + if (fields_removed_.size() == 0 && fields_diff_.size() == 0 && + fields_added_.size() != 0) { + return true; + } + + return false; + } + + bool IsIncompatible() const { + if (fields_removed_.size() != 0 || fields_diff_.size() != 0) { + return true; + } + + return false; + } + + LinkableMessageKind Kind() const override { + return LinkableMessageKind::EnumTypeKind; + } + + protected: + // The underlying type can only be integral, so we just need to check for + // referenced type. + std::unique_ptr> underlying_type_diff_; + std::vector fields_removed_; + std::vector fields_added_; + std::vector fields_diff_; + // Modifiable to allow implicit construction. + std::string name_; +}; + +class GlobalVarDiffIR : public DiffMessageIR { + public: + LinkableMessageKind Kind() const override { + return LinkableMessageKind::GlobalVarKind; + } + + GlobalVarDiffIR(const GlobalVarIR *old_global_var, + const GlobalVarIR *new_global_var) + : old_global_var_(old_global_var), new_global_var_(new_global_var) { } + + const GlobalVarIR *GetOldGlobalVar() const { + return old_global_var_; + } + + const GlobalVarIR *GetNewGlobalVar() const { + return new_global_var_; + } + + protected: + const GlobalVarIR *old_global_var_; + const GlobalVarIR *new_global_var_; +}; + +class FunctionDiffIR : public DiffMessageIR { + public: + LinkableMessageKind Kind() const override { + return LinkableMessageKind::FunctionKind; + } + + FunctionDiffIR(const FunctionIR *old_function, + const FunctionIR *new_function) + : old_function_(old_function), new_function_(new_function) { } + + const FunctionIR *GetOldFunction() const { + return old_function_; + } + + const FunctionIR *GetNewFunction() const { + return new_function_; + } + + protected: + const FunctionIR *old_function_; + const FunctionIR *new_function_; +}; + +class IRDiffDumper { + public: + enum DiffKind { + Extension, // Applicable for enums. + Added, + Removed, + Referenced, + Unreferenced + }; + + IRDiffDumper(const std::string &dump_path) : dump_path_(dump_path) { } + + virtual bool AddDiffMessageIR(const DiffMessageIR *, + const std::string &type_stack, + DiffKind diff_kind) = 0; + + virtual bool AddLinkableMessageIR(const LinkableMessageIR *, + DiffKind diff_kind) = 0; + + virtual bool AddElfSymbolMessageIR(const ElfSymbolIR *, + DiffKind diff_kind) = 0; + + virtual void AddLibNameIR(const std::string &name) = 0; + + virtual void AddArchIR(const std::string &arch) = 0; + + virtual void AddCompatibilityStatusIR(CompatibilityStatusIR status) = 0; + + virtual bool Dump() = 0; + + virtual CompatibilityStatusIR GetCompatibilityStatusIR() = 0; + + virtual ~IRDiffDumper() {} + static std::unique_ptr CreateIRDiffDumper( + const std::string &type, const std::string &dump_path); + protected: + const std::string &dump_path_; +}; + +} // namespace abi_util + +#endif // IR_ diff --git a/vndk/tools/header-checker/header-abi-util/include/ir_representation_protobuf.h b/vndk/tools/header-checker/header-abi-util/include/ir_representation_protobuf.h new file mode 100644 index 000000000..e5cf61e64 --- /dev/null +++ b/vndk/tools/header-checker/header-abi-util/include/ir_representation_protobuf.h @@ -0,0 +1,418 @@ +// Copyright (C) 2017 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. +#ifndef IR_PROTOBUF_ +#define IR_PROTOBUF_ + +#include + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-parameter" +#pragma clang diagnostic ignored "-Wnested-anon-types" +#include "proto/abi_dump.pb.h" +#include "proto/abi_diff.pb.h" +#pragma clang diagnostic pop + +#include +#include + + +// Classes which act as middle-men between clang AST parsing routines and +// message format specific dumpers. +namespace abi_util { + +inline abi_diff::CompatibilityStatus CompatibilityStatusIRToProtobuf( + CompatibilityStatusIR status) { + switch(status) { + case CompatibilityStatusIR::Incompatible: + return abi_diff::CompatibilityStatus::INCOMPATIBLE; + case CompatibilityStatusIR::Extension: + return abi_diff::CompatibilityStatus::EXTENSION; + default: + break; + } + return abi_diff::CompatibilityStatus::COMPATIBLE; +} + +inline abi_dump::AccessSpecifier AccessIRToProtobuf(AccessSpecifierIR access) { + switch (access) { + case AccessSpecifierIR::ProtectedAccess: + return abi_dump::AccessSpecifier::protected_access; + case AccessSpecifierIR::PrivateAccess: + return abi_dump::AccessSpecifier::private_access; + default: + return abi_dump::AccessSpecifier::public_access; + } + return abi_dump::AccessSpecifier::public_access; +} + +inline AccessSpecifierIR AccessProtobufToIR( + abi_dump::AccessSpecifier access) { + switch (access) { + case abi_dump::AccessSpecifier::protected_access: + return AccessSpecifierIR::ProtectedAccess; + case abi_dump::AccessSpecifier::private_access: + return AccessSpecifierIR::PrivateAccess; + default: + return AccessSpecifierIR::PublicAccess; + } + return AccessSpecifierIR::PublicAccess; +} + +inline abi_dump::RecordKind RecordKindIRToProtobuf( + RecordTypeIR::RecordKind kind) { + switch (kind) { + case RecordTypeIR::RecordKind::struct_kind: + return abi_dump::RecordKind::struct_kind; + + case RecordTypeIR::RecordKind::class_kind: + return abi_dump::RecordKind::class_kind; + + case RecordTypeIR::RecordKind::union_kind: + return abi_dump::RecordKind::union_kind; + + default: + return abi_dump::RecordKind::struct_kind; + } + // Should not be reached + assert(false); +} + +inline abi_dump::VTableComponent::Kind VTableComponentKindIRToProtobuf( + VTableComponentIR::Kind kind) { + switch (kind) { + case VTableComponentIR::Kind::VCallOffset: + return abi_dump::VTableComponent_Kind_VCallOffset; + + case VTableComponentIR::Kind::VBaseOffset: + return abi_dump::VTableComponent_Kind_VBaseOffset; + + case VTableComponentIR::Kind::OffsetToTop: + return abi_dump::VTableComponent_Kind_OffsetToTop; + + case VTableComponentIR::Kind::RTTI: + return abi_dump::VTableComponent_Kind_RTTI; + + case VTableComponentIR::Kind::FunctionPointer: + return abi_dump::VTableComponent_Kind_FunctionPointer; + + case VTableComponentIR::Kind::CompleteDtorPointer: + return abi_dump::VTableComponent_Kind_CompleteDtorPointer; + + case VTableComponentIR::Kind::DeletingDtorPointer: + return abi_dump::VTableComponent_Kind_DeletingDtorPointer; + + default: + return abi_dump::VTableComponent_Kind_UnusedFunctionPointer; + } + // Should not be reached + assert(false); +} + +inline VTableComponentIR::Kind VTableComponentKindProtobufToIR( + abi_dump::VTableComponent_Kind kind) { + switch (kind) { + case abi_dump::VTableComponent_Kind_VCallOffset: + return VTableComponentIR::Kind::VCallOffset; + + case abi_dump::VTableComponent_Kind_VBaseOffset: + return VTableComponentIR::Kind::VBaseOffset; + + case abi_dump::VTableComponent_Kind_OffsetToTop: + return VTableComponentIR::Kind::OffsetToTop; + + case abi_dump::VTableComponent_Kind_RTTI: + return VTableComponentIR::Kind::RTTI; + + case abi_dump::VTableComponent_Kind_FunctionPointer: + return VTableComponentIR::Kind::FunctionPointer; + + case abi_dump::VTableComponent_Kind_CompleteDtorPointer: + return VTableComponentIR::Kind::CompleteDtorPointer; + + case abi_dump::VTableComponent_Kind_DeletingDtorPointer: + return VTableComponentIR::Kind::DeletingDtorPointer; + + default: + return VTableComponentIR::Kind::UnusedFunctionPointer; + } + // Should not be reached + assert(false); +} + +class IRToProtobufConverter { + private: + static bool AddTemplateInformation( + abi_dump::TemplateInfo *ti, const abi_util::TemplatedArtifactIR *ta); + + static bool AddTypeInfo( + abi_dump::BasicNamedAndTypedDecl *type_info, const TypeIR *typep); + + static bool AddRecordFields( + abi_dump::RecordType *record_protobuf, const RecordTypeIR *record_ir); + + static bool AddBaseSpecifiers( + abi_dump::RecordType *record_protobuf, const RecordTypeIR *record_ir); + + static bool AddVTableLayout( + abi_dump::RecordType *record_protobuf, const RecordTypeIR *record_ir); + + static bool AddEnumFields(abi_dump::EnumType *enum_protobuf, + const EnumTypeIR *enum_ir); + public: + static abi_dump::EnumType ConvertEnumTypeIR(const EnumTypeIR *enump); + + static abi_dump::RecordType ConvertRecordTypeIR(const RecordTypeIR *recordp); + + static bool AddFunctionParameters(abi_dump::FunctionDecl *function_protobuf, + const FunctionIR *function_ir); + + static abi_dump::FunctionDecl ConvertFunctionIR(const FunctionIR *functionp); + + static abi_dump::GlobalVarDecl ConvertGlobalVarIR( + const GlobalVarIR *global_varp); + + static abi_dump::PointerType ConvertPointerTypeIR( + const PointerTypeIR *pointerp); + + static abi_dump::QualifiedType ConvertQualifiedTypeIR( + const QualifiedTypeIR *qualtypep); + + static abi_dump::BuiltinType ConvertBuiltinTypeIR( + const BuiltinTypeIR *builtin_typep); + + static abi_dump::ArrayType ConvertArrayTypeIR( + const ArrayTypeIR *array_typep); + + static abi_dump::LvalueReferenceType ConvertLvalueReferenceTypeIR( + const LvalueReferenceTypeIR *lvalue_reference_typep); + + static abi_dump::RvalueReferenceType ConvertRvalueReferenceTypeIR( + const RvalueReferenceTypeIR *rvalue_reference_typep); + + static abi_dump::ElfFunction ConvertElfFunctionIR( + const ElfFunctionIR *elf_function_ir); + + static abi_dump::ElfObject ConvertElfObjectIR( + const ElfObjectIR *elf_object_ir); +}; + +class IRDiffToProtobufConverter { + private: + static bool AddTypeInfoDiff(abi_diff::TypeInfoDiff *type_info_diff_protobuf, + const TypeDiffIR *type_diff_ir); + + static bool AddVTableLayoutDiff( + abi_diff::VTableLayoutDiff *vtable_layout_diff_protobuf, + const VTableLayoutDiffIR *vtable_layout_diff_ir); + + static bool AddBaseSpecifierDiffs( + abi_diff::CXXBaseSpecifierDiff *base_specifier_diff_protobuf, + const CXXBaseSpecifierDiffIR *base_specifier_diff_ir); + + static bool AddRecordFieldsRemoved( + abi_diff::RecordTypeDiff *record_diff_protobuf, + const std::vector &record_fields_removed_ir); + + static bool AddRecordFieldDiffs( + abi_diff::RecordTypeDiff *record_diff_protobuf, + const std::vector &record_field_diff_ir); + + static bool AddEnumUnderlyingTypeDiff( + abi_diff::UnderlyingTypeDiff *underlying_type_diff_protobuf, + const std::pair *underlying_type_diff_ir); + + public: + static abi_diff::RecordTypeDiff ConvertRecordTypeDiffIR( + const RecordTypeDiffIR *record_type_diffp); + + static abi_diff::EnumTypeDiff ConvertEnumTypeDiffIR( + const EnumTypeDiffIR *enum_type_diffp); + + static abi_diff::FunctionDeclDiff ConvertFunctionDiffIR( + const FunctionDiffIR *function_diffp); + + static abi_diff::GlobalVarDeclDiff ConvertGlobalVarDiffIR( + const GlobalVarDiffIR *global_var_diffp); +}; + +class ProtobufIRDumper : public IRDumper, public IRToProtobufConverter { + private: + // Types + bool AddRecordTypeIR(const RecordTypeIR *); + + bool AddEnumTypeIR(const EnumTypeIR *); + + bool AddPointerTypeIR(const PointerTypeIR *); + + bool AddQualifiedTypeIR(const QualifiedTypeIR *); + + bool AddLvalueReferenceTypeIR(const LvalueReferenceTypeIR *); + + bool AddRvalueReferenceTypeIR(const RvalueReferenceTypeIR *); + + bool AddArrayTypeIR(const ArrayTypeIR *); + + bool AddBuiltinTypeIR(const BuiltinTypeIR *); + + // Functions and global variables. + bool AddFunctionIR(const FunctionIR *); + + bool AddGlobalVarIR(const GlobalVarIR *); + + public: + ProtobufIRDumper(const std::string &dump_path) + : IRDumper(dump_path), tu_ptr_(new abi_dump::TranslationUnit()) { } + + bool AddLinkableMessageIR(const LinkableMessageIR *); + + bool Dump() override; + + ~ProtobufIRDumper() override { } + + private: + std::unique_ptr tu_ptr_; +}; + + +class ProtobufTextFormatToIRReader : public TextFormatToIRReader { + public: + + virtual bool ReadDump() override; + + ProtobufTextFormatToIRReader(const std::string &dump_path) + : TextFormatToIRReader(dump_path) { } + + private: + std::vector ReadFunctions( + const abi_dump::TranslationUnit &tu); + + std::vector ReadGlobalVariables( + const abi_dump::TranslationUnit &tu); + + std::vector ReadEnumTypes(const abi_dump::TranslationUnit &tu); + + std::vector ReadRecordTypes( + const abi_dump::TranslationUnit &tu); + + std::vector ReadPointerTypes( + const abi_dump::TranslationUnit &tu); + + std::vector ReadBuiltinTypes( + const abi_dump::TranslationUnit &tu); + + std::vector ReadQualifiedTypes( + const abi_dump::TranslationUnit &tu); + + std::vector ReadArrayTypes(const abi_dump::TranslationUnit &tu); + + std::vector ReadLvalueReferenceTypes( + const abi_dump::TranslationUnit &tu); + + std::vector ReadRvalueReferenceTypes( + const abi_dump::TranslationUnit &tu); + + std::vector ReadElfFunctions ( + const abi_dump::TranslationUnit &tu); + + std::vector ReadElfObjects (const abi_dump::TranslationUnit &tu); + + void ReadTypeInfo(const abi_dump::BasicNamedAndTypedDecl &type_info, + TypeIR *typep); + + FunctionIR FunctionProtobufToIR(const abi_dump::FunctionDecl &); + + RecordTypeIR RecordTypeProtobufToIR( + const abi_dump::RecordType &record_type_protobuf); + + std::vector RecordFieldsProtobufToIR( + const google::protobuf::RepeatedPtrField &rfp); + + std::vector RecordCXXBaseSpecifiersProtobufToIR( + const google::protobuf::RepeatedPtrField &rbs); + + std::vector EnumFieldsProtobufToIR( + const google::protobuf::RepeatedPtrField &efp); + + EnumTypeIR EnumTypeProtobufToIR( + const abi_dump::EnumType &enum_type_protobuf); + + VTableLayoutIR VTableLayoutProtobufToIR( + const abi_dump::VTableLayout &vtable_layout_protobuf); + + TemplateInfoIR TemplateInfoProtobufToIR( + const abi_dump::TemplateInfo &template_info_protobuf); +}; + +class ProtobufIRDiffDumper : public IRDiffDumper { + public: + ProtobufIRDiffDumper(const std::string &dump_path) + : IRDiffDumper(dump_path), + diff_tu_(new abi_diff::TranslationUnitDiff()) { } + + bool AddDiffMessageIR(const DiffMessageIR *, const std::string &type_stack, + DiffKind diff_kind) override; + + bool AddLinkableMessageIR(const LinkableMessageIR *, + DiffKind diff_kind) override; + + bool AddElfSymbolMessageIR(const ElfSymbolIR *, DiffKind diff_kind) override; + + void AddLibNameIR(const std::string &name) override; + + void AddArchIR(const std::string &arch) override; + + void AddCompatibilityStatusIR(CompatibilityStatusIR status) override; + + bool Dump() override; + + CompatibilityStatusIR GetCompatibilityStatusIR() override; + + ~ProtobufIRDiffDumper() override { } + + private: + // User defined types. + bool AddRecordTypeDiffIR(const RecordTypeDiffIR *, + const std::string &type_stack, DiffKind diff_kind); + + bool AddEnumTypeDiffIR(const EnumTypeDiffIR *, + const std::string &type_stack, DiffKind diff_kind); + + // Functions and global variables. + bool AddFunctionDiffIR(const FunctionDiffIR *, + const std::string &type_stack, DiffKind diff_kind); + + bool AddGlobalVarDiffIR(const GlobalVarDiffIR *, + const std::string &type_stack, DiffKind diff_kind); + + bool AddLoneRecordTypeDiffIR(const RecordTypeIR *, DiffKind diff_kind); + + bool AddLoneEnumTypeDiffIR(const EnumTypeIR *, DiffKind diff_kind); + + // Functions and global variables. + bool AddLoneFunctionDiffIR(const FunctionIR *, DiffKind diff_kind); + + bool AddLoneGlobalVarDiffIR(const GlobalVarIR *, DiffKind diff_kind); + + bool AddElfObjectIR(const ElfObjectIR *elf_object_ir, DiffKind diff_kind); + + bool AddElfFunctionIR(const ElfFunctionIR *elf_function_ir, + DiffKind diff_kind); + + protected: + std::unique_ptr diff_tu_; +}; + +} // abi_util + +#endif // IR_PROTOBUF_ diff --git a/vndk/tools/header-checker/header-abi-util/src/ir_representation.cpp b/vndk/tools/header-checker/header-abi-util/src/ir_representation.cpp new file mode 100644 index 000000000..0cf58e196 --- /dev/null +++ b/vndk/tools/header-checker/header-abi-util/src/ir_representation.cpp @@ -0,0 +1,67 @@ +// Copyright (C) 2017 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 +#include + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-parameter" +#pragma clang diagnostic ignored "-Wnested-anon-types" +#include "proto/abi_dump.pb.h" +#pragma clang diagnostic pop + +#include +#include + +#include + +#include +#include + + +namespace abi_util { + +std::unique_ptr IRDumper::CreateIRDumper( + const std::string &type, const std::string &dump_path) { + if (type == "protobuf") { + return std::make_unique(dump_path); + } + // Nothing else is supported yet. + llvm::errs() << type << " message format is not supported yet!\n"; + return nullptr; +} + +std::unique_ptr IRDiffDumper::CreateIRDiffDumper( + const std::string &type, const std::string &dump_path) { + if (type == "protobuf") { + return std::make_unique(dump_path); + } + // Nothing else is supported yet. + llvm::errs() << type << " message format is not supported yet!\n"; + return nullptr; +} + +std::unique_ptr +TextFormatToIRReader::CreateTextFormatToIRReader( + const std::string &type, const std::string &dump_path) { + if (type == "protobuf") { + return std::make_unique(dump_path); + } + // Nothing else is supported yet. + llvm::errs() << type << " message format is not supported yet!\n"; + return nullptr; +} + +} // namespace abi_util + diff --git a/vndk/tools/header-checker/header-abi-util/src/ir_representation_protobuf.cpp b/vndk/tools/header-checker/header-abi-util/src/ir_representation_protobuf.cpp new file mode 100644 index 000000000..f3958d8bb --- /dev/null +++ b/vndk/tools/header-checker/header-abi-util/src/ir_representation_protobuf.cpp @@ -0,0 +1,1353 @@ +// Copyright (C) 2017 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 + +#include + +#include +#include +#include +#include + +namespace abi_util { + +void ProtobufTextFormatToIRReader::ReadTypeInfo( + const abi_dump::BasicNamedAndTypedDecl &type_info, + TypeIR *typep) { + typep->SetLinkerSetKey(type_info.linker_set_key()); + typep->SetName(type_info.linker_set_key()); + typep->SetSourceFile(type_info.source_file()); + typep->SetReferencedType(type_info.referenced_type()); + typep->SetSize(type_info.size()); + typep->SetAlignment(type_info.alignment()); +} + +bool ProtobufTextFormatToIRReader::ReadDump() { + abi_dump::TranslationUnit tu; + std::ifstream input(dump_path_); + google::protobuf::io::IstreamInputStream text_is(&input); + + if (!google::protobuf::TextFormat::Parse(&text_is, &tu)) { + llvm::errs() << "Failed to parse protobuf TextFormat file\n"; + return false; + } + + functions_ = ReadFunctions(tu); + global_variables_ = ReadGlobalVariables(tu); + + enum_types_ = ReadEnumTypes(tu); + record_types_ = ReadRecordTypes(tu); + array_types_ = ReadArrayTypes(tu); + pointer_types_ = ReadPointerTypes(tu); + qualified_types_ = ReadQualifiedTypes(tu); + builtin_types_ = ReadBuiltinTypes(tu); + lvalue_reference_types_ = ReadLvalueReferenceTypes(tu); + rvalue_reference_types_ = ReadRvalueReferenceTypes(tu); + + elf_functions_ = ReadElfFunctions(tu); + elf_objects_ = ReadElfObjects(tu); + + return true; +} + +TemplateInfoIR ProtobufTextFormatToIRReader::TemplateInfoProtobufToIR( + const abi_dump::TemplateInfo &template_info_protobuf) { + TemplateInfoIR template_info_ir; + for (auto &&template_element : template_info_protobuf.elements()) { + TemplateElementIR template_element_ir(template_element.referenced_type()); + template_info_ir.AddTemplateElement(std::move(template_element_ir)); + } + return template_info_ir; +} + +FunctionIR ProtobufTextFormatToIRReader::FunctionProtobufToIR( + const abi_dump::FunctionDecl &function_protobuf) { + FunctionIR function_ir; + function_ir.SetReturnType(function_protobuf.return_type()); + function_ir.SetLinkerSetKey(function_protobuf.linker_set_key()); + function_ir.SetName(function_protobuf.function_name()); + function_ir.SetAccess(AccessProtobufToIR(function_protobuf.access())); + function_ir.SetSourceFile(function_protobuf.source_file()); + // Set parameters + for (auto &¶meter: function_protobuf.parameters()) { + ParamIR param_ir(parameter.referenced_type(), parameter.default_arg()); + function_ir.AddParameter(std::move(param_ir)); + } + // Set Template info + function_ir.SetTemplateInfo( + TemplateInfoProtobufToIR(function_protobuf.template_info())); + return function_ir; +} + +VTableLayoutIR ProtobufTextFormatToIRReader::VTableLayoutProtobufToIR( + const abi_dump::VTableLayout &vtable_layout_protobuf) { + VTableLayoutIR vtable_layout_ir; + for (auto &&vtable_component : vtable_layout_protobuf.vtable_components()) { + VTableComponentIR vtable_component_ir( + vtable_component.mangled_component_name(), + VTableComponentKindProtobufToIR(vtable_component.kind()), + vtable_component.component_value()); + vtable_layout_ir.AddVTableComponent(std::move(vtable_component_ir)); + } + return vtable_layout_ir; +} + +std::vector +ProtobufTextFormatToIRReader::RecordFieldsProtobufToIR( + const google::protobuf::RepeatedPtrField &rfp) { + std::vector record_type_fields_ir; + for (auto &&field : rfp) { + RecordFieldIR record_field_ir(field.field_name(), field.referenced_type(), + field.field_offset(), + AccessProtobufToIR(field.access())); + record_type_fields_ir.emplace_back(std::move(record_field_ir)); + } + return record_type_fields_ir; +} + +std::vector +ProtobufTextFormatToIRReader::RecordCXXBaseSpecifiersProtobufToIR( + const google::protobuf::RepeatedPtrField &rbs) { + std::vector record_type_bases_ir; + for (auto &&base : rbs) { + CXXBaseSpecifierIR record_base_ir( + base.referenced_type(), base.is_virtual(), + AccessProtobufToIR(base.access())); + record_type_bases_ir.emplace_back(std::move(record_base_ir)); + } + return record_type_bases_ir; +} + +RecordTypeIR ProtobufTextFormatToIRReader::RecordTypeProtobufToIR( + const abi_dump::RecordType &record_type_protobuf) { + RecordTypeIR record_type_ir; + ReadTypeInfo(record_type_protobuf.type_info(), &record_type_ir); + record_type_ir.SetTemplateInfo( + TemplateInfoProtobufToIR(record_type_protobuf.template_info())); + record_type_ir.SetAccess(AccessProtobufToIR(record_type_protobuf.access())); + record_type_ir.SetVTableLayout( + VTableLayoutProtobufToIR(record_type_protobuf.vtable_layout())); + // Get fields + record_type_ir.SetRecordFields(RecordFieldsProtobufToIR( + record_type_protobuf.fields())); + // Base Specifiers + record_type_ir.SetCXXBaseSpecifiers(RecordCXXBaseSpecifiersProtobufToIR( + record_type_protobuf.base_specifiers())); + + return record_type_ir; +} + +std::vector +ProtobufTextFormatToIRReader::EnumFieldsProtobufToIR( + const google::protobuf::RepeatedPtrField &efp) { + std::vector enum_type_fields_ir; + for (auto &&field : efp) { + EnumFieldIR enum_field_ir(field.name(), field.enum_field_value()); + enum_type_fields_ir.emplace_back(std::move(enum_field_ir)); + } + return enum_type_fields_ir; +} + +EnumTypeIR ProtobufTextFormatToIRReader::EnumTypeProtobufToIR( + const abi_dump::EnumType &enum_type_protobuf) { + EnumTypeIR enum_type_ir; + ReadTypeInfo(enum_type_protobuf.type_info(), &enum_type_ir); + enum_type_ir.SetUnderlyingType(enum_type_protobuf.underlying_type()); + enum_type_ir.SetAccess(AccessProtobufToIR(enum_type_protobuf.access())); + enum_type_ir.SetFields( + EnumFieldsProtobufToIR(enum_type_protobuf.enum_fields())); + return enum_type_ir; +} + +std::vector ProtobufTextFormatToIRReader::ReadGlobalVariables( + const abi_dump::TranslationUnit &tu) { + std::vector global_variables; + for (auto &&global_variable_protobuf : tu.global_vars()) { + GlobalVarIR global_variable_ir; + global_variable_ir.SetName(global_variable_protobuf.name()); + global_variable_ir.SetSourceFile(global_variable_protobuf.source_file()); + global_variable_ir.SetReferencedType( + global_variable_protobuf.referenced_type()); + global_variable_ir.SetLinkerSetKey( + global_variable_protobuf.linker_set_key()); + global_variables.emplace_back(std::move(global_variable_ir)); + } + return global_variables; +} + +std::vector ProtobufTextFormatToIRReader::ReadPointerTypes( + const abi_dump::TranslationUnit &tu) { + std::vector pointer_types; + for (auto &&pointer_type_protobuf : tu.pointer_types()) { + PointerTypeIR pointer_type_ir; + ReadTypeInfo(pointer_type_protobuf.type_info(), &pointer_type_ir); + pointer_types.emplace_back(std::move(pointer_type_ir)); + } + return pointer_types; +} + +std::vector ProtobufTextFormatToIRReader::ReadBuiltinTypes( + const abi_dump::TranslationUnit &tu) { + std::vector builtin_types; + for (auto &&builtin_type_protobuf : tu.builtin_types()) { + BuiltinTypeIR builtin_type_ir; + ReadTypeInfo(builtin_type_protobuf.type_info(), &builtin_type_ir); + builtin_type_ir.SetSignedness(builtin_type_protobuf.is_unsigned()); + builtin_type_ir.SetIntegralType(builtin_type_protobuf.is_integral()); + builtin_types.emplace_back(std::move(builtin_type_ir)); + } + return builtin_types; +} + +std::vector ProtobufTextFormatToIRReader::ReadQualifiedTypes( + const abi_dump::TranslationUnit &tu) { + std::vector qualified_types; + for (auto &&qualified_type_protobuf : tu.qualified_types()) { + QualifiedTypeIR qualified_type_ir; + ReadTypeInfo(qualified_type_protobuf.type_info(), &qualified_type_ir); + qualified_types.emplace_back(std::move(qualified_type_ir)); + } + return qualified_types; +} + +std::vector ProtobufTextFormatToIRReader::ReadArrayTypes( + const abi_dump::TranslationUnit &tu) { + std::vector array_types; + for (auto &&array_type_protobuf : tu.array_types()) { + ArrayTypeIR array_type_ir; + ReadTypeInfo(array_type_protobuf.type_info(), &array_type_ir); + array_types.emplace_back(std::move(array_type_ir)); + } + return array_types; +} + +std::vector +ProtobufTextFormatToIRReader::ReadLvalueReferenceTypes( + const abi_dump::TranslationUnit &tu) { + std::vector lvalue_reference_types; + for (auto &&lvalue_reference_type_protobuf : tu.lvalue_reference_types()) { + LvalueReferenceTypeIR lvalue_reference_type_ir; + ReadTypeInfo(lvalue_reference_type_protobuf.type_info(), + &lvalue_reference_type_ir); + lvalue_reference_types.emplace_back(std::move(lvalue_reference_type_ir)); + } + return lvalue_reference_types; +} + +std::vector +ProtobufTextFormatToIRReader::ReadRvalueReferenceTypes( + const abi_dump::TranslationUnit &tu) { + std::vector rvalue_reference_types; + for (auto &&rvalue_reference_type_protobuf : tu.rvalue_reference_types()) { + RvalueReferenceTypeIR rvalue_reference_type_ir; + ReadTypeInfo(rvalue_reference_type_protobuf.type_info(), + &rvalue_reference_type_ir); + rvalue_reference_types.emplace_back(std::move(rvalue_reference_type_ir)); + } + return rvalue_reference_types; +} + +std::vector ProtobufTextFormatToIRReader::ReadFunctions( + const abi_dump::TranslationUnit &tu) { + std::vector functions; + for (auto &&function_protobuf : tu.functions()) { + FunctionIR function_ir = FunctionProtobufToIR(function_protobuf); + functions.emplace_back(std::move(function_ir)); + } + return functions; +} + +std::vector ProtobufTextFormatToIRReader::ReadRecordTypes( + const abi_dump::TranslationUnit &tu) { + std::vector record_types; + for (auto &&record_type_protobuf : tu.record_types()) { + RecordTypeIR record_type_ir = RecordTypeProtobufToIR(record_type_protobuf); + record_types.emplace_back(std::move(record_type_ir)); + } + return record_types; +} + +std::vector ProtobufTextFormatToIRReader::ReadEnumTypes( + const abi_dump::TranslationUnit &tu) { + std::vector enum_types; + for (auto &&enum_type_protobuf : tu.enum_types()) { + EnumTypeIR enum_type_ir = EnumTypeProtobufToIR(enum_type_protobuf); + enum_types.emplace_back(std::move(enum_type_ir)); + } + return enum_types; +} + +std::vector ProtobufTextFormatToIRReader::ReadElfFunctions( + const abi_dump::TranslationUnit &tu) { + std::vector elf_functions; + for (auto &&elf_function : tu.elf_functions()) { + elf_functions.emplace_back(ElfFunctionIR(elf_function.name())); + } + return elf_functions; +} + +std::vector ProtobufTextFormatToIRReader::ReadElfObjects( + const abi_dump::TranslationUnit &tu) { + std::vector elf_objects; + for (auto &&elf_object : tu.elf_objects()) { + elf_objects.emplace_back(ElfObjectIR(elf_object.name())); + } + return elf_objects; +} + +bool IRToProtobufConverter::AddTemplateInformation( + abi_dump::TemplateInfo *ti, const abi_util::TemplatedArtifactIR *ta) { + for (auto &&template_element : ta->GetTemplateElements()) { + abi_dump::TemplateElement *added_element = ti->add_elements(); + if (!added_element) { + llvm::errs() << "Failed to add template element\n"; + return false; + } + added_element->set_referenced_type(template_element.GetReferencedType()); + } + return true; +} + +bool IRToProtobufConverter::AddTypeInfo( + abi_dump::BasicNamedAndTypedDecl *type_info, + const TypeIR *typep) { + if (!type_info || !typep) { + llvm::errs() << "Typeinfo not valid\n"; + return false; + } + type_info->set_linker_set_key(typep->GetLinkerSetKey()); + type_info->set_source_file(typep->GetSourceFile()); + type_info->set_name(typep->GetName()); + type_info->set_size(typep->GetSize()); + type_info->set_alignment(typep->GetAlignment()); + type_info->set_referenced_type(typep->GetReferencedType()); + return true; +} + +static void SetIRToProtobufRecordField( + abi_dump::RecordFieldDecl *record_field_protobuf, + const RecordFieldIR *record_field_ir) { + record_field_protobuf->set_field_name(record_field_ir->GetName()); + record_field_protobuf->set_referenced_type( + record_field_ir->GetReferencedType()); + record_field_protobuf->set_access( + AccessIRToProtobuf(record_field_ir->GetAccess())); + record_field_protobuf->set_field_offset(record_field_ir->GetOffset()); +} + +bool IRToProtobufConverter::AddRecordFields( + abi_dump::RecordType *record_protobuf, + const RecordTypeIR *record_ir) { + // Iterate through the fields and create corresponding ones for the protobuf + // record + for (auto &&field_ir : record_ir->GetFields()) { + abi_dump::RecordFieldDecl *added_field = record_protobuf->add_fields(); + if (!added_field) { + llvm::errs() << "Couldn't add record field\n"; + } + SetIRToProtobufRecordField(added_field, &field_ir); + } + return true; +} + +static bool SetIRToProtobufBaseSpecifier( + abi_dump::CXXBaseSpecifier *base_specifier_protobuf, + const CXXBaseSpecifierIR &base_specifier_ir) { + if (!base_specifier_protobuf) { + llvm::errs() << "Protobuf base specifier not valid\n"; + return false; + } + base_specifier_protobuf->set_referenced_type( + base_specifier_ir.GetReferencedType()); + base_specifier_protobuf->set_is_virtual( + base_specifier_ir.IsVirtual()); + base_specifier_protobuf->set_access( + AccessIRToProtobuf(base_specifier_ir.GetAccess())); + return true; +} + +bool IRToProtobufConverter::AddBaseSpecifiers( + abi_dump::RecordType *record_protobuf, const RecordTypeIR *record_ir) { + for (auto &&base_ir : record_ir->GetBases()) { + abi_dump::CXXBaseSpecifier *added_base = + record_protobuf->add_base_specifiers(); + if (!SetIRToProtobufBaseSpecifier(added_base, base_ir)) { + return false; + } + } + return true; +} + +static bool SetIRToProtobufVTableLayout( + abi_dump::VTableLayout *vtable_layout_protobuf, + const VTableLayoutIR &vtable_layout_ir) { + if (vtable_layout_protobuf == nullptr) { + llvm::errs() << "vtable layout protobuf not valid\n"; + return false; + } + for (auto &&vtable_component_ir : vtable_layout_ir.GetVTableComponents()) { + abi_dump::VTableComponent *added_vtable_component = + vtable_layout_protobuf->add_vtable_components(); + if (!added_vtable_component) { + llvm::errs() << "Couldn't add vtable component\n"; + return false; + } + added_vtable_component->set_kind( + VTableComponentKindIRToProtobuf(vtable_component_ir.GetKind())); + added_vtable_component->set_component_value(vtable_component_ir.GetValue()); + added_vtable_component->set_mangled_component_name( + vtable_component_ir.GetName()); + } + return true; +} + +bool IRToProtobufConverter::AddVTableLayout( + abi_dump::RecordType *record_protobuf, + const RecordTypeIR *record_ir) { + // If there are no entries in the vtable, just return. + if (record_ir->GetVTableNumEntries() == 0) { + return true; + } + const VTableLayoutIR &vtable_layout_ir = record_ir->GetVTableLayout(); + abi_dump::VTableLayout *vtable_layout_protobuf = + record_protobuf->mutable_vtable_layout(); + if (!SetIRToProtobufVTableLayout(vtable_layout_protobuf, vtable_layout_ir)) { + return false; + } + return true; +} + +abi_dump::RecordType IRToProtobufConverter::ConvertRecordTypeIR( + const RecordTypeIR *recordp) { + abi_dump::RecordType added_record_type; + added_record_type.set_access(AccessIRToProtobuf(recordp->GetAccess())); + added_record_type.set_record_kind( + RecordKindIRToProtobuf(recordp->GetRecordKind())); + if (recordp->IsAnonymous()) { + added_record_type.set_is_anonymous(true); + } + if (!AddTypeInfo(added_record_type.mutable_type_info(), recordp) || + !AddRecordFields(&added_record_type, recordp) || + !AddBaseSpecifiers(&added_record_type, recordp) || + !AddVTableLayout(&added_record_type, recordp) || + !(recordp->GetTemplateElements().size() ? + AddTemplateInformation(added_record_type.mutable_template_info(), + recordp) : true)) { + llvm::errs() << "Template information could not be added\n"; + ::exit(1); + } + return added_record_type; +} + + +abi_dump::ElfObject IRToProtobufConverter::ConvertElfObjectIR( + const ElfObjectIR *elf_object_ir) { + abi_dump::ElfObject elf_object_protobuf; + elf_object_protobuf.set_name(elf_object_ir->GetName()); + return elf_object_protobuf; +} + +abi_dump::ElfFunction IRToProtobufConverter::ConvertElfFunctionIR( + const ElfFunctionIR *elf_function_ir) { + abi_dump::ElfFunction elf_function_protobuf; + elf_function_protobuf.set_name(elf_function_ir->GetName()); + return elf_function_protobuf; +} + +bool IRToProtobufConverter::AddFunctionParameters( + abi_dump::FunctionDecl *function_protobuf, + const FunctionIR *function_ir) { + for (auto &¶meter : function_ir->GetParameters()) { + abi_dump::ParamDecl *added_parameter = function_protobuf->add_parameters(); + if (!added_parameter) { + return false; + } + added_parameter->set_referenced_type( + parameter.GetReferencedType()); + added_parameter->set_default_arg(parameter.GetIsDefault()); + } + return true; +} + +abi_dump::FunctionDecl IRToProtobufConverter::ConvertFunctionIR( + const FunctionIR *functionp) { + abi_dump::FunctionDecl added_function; + added_function.set_access(AccessIRToProtobuf(functionp->GetAccess())); + added_function.set_linker_set_key(functionp->GetLinkerSetKey()); + added_function.set_source_file(functionp->GetSourceFile()); + added_function.set_function_name(functionp->GetName()); + added_function.set_return_type(functionp->GetReturnType()); + if (!AddFunctionParameters(&added_function, functionp) || + !(functionp->GetTemplateElements().size() ? + AddTemplateInformation(added_function.mutable_template_info(), functionp) + : true)) { + llvm::errs() << "Template information could not be added\n"; + ::exit(1); + } + return added_function; +} + +static bool SetIRToProtobufEnumField( + abi_dump::EnumFieldDecl *enum_field_protobuf, + const EnumFieldIR *enum_field_ir) { + if (enum_field_protobuf == nullptr) { + return true; + } + enum_field_protobuf->set_name(enum_field_ir->GetName()); + enum_field_protobuf->set_enum_field_value(enum_field_ir->GetValue()); + return true; +} + +bool IRToProtobufConverter::AddEnumFields(abi_dump::EnumType *enum_protobuf, + const EnumTypeIR *enum_ir) { + for (auto &&field : enum_ir->GetFields()) { + abi_dump::EnumFieldDecl *enum_fieldp = enum_protobuf->add_enum_fields(); + if (!SetIRToProtobufEnumField(enum_fieldp, &field)) { + return false; + } + } + return true; +} + + +abi_dump::EnumType IRToProtobufConverter::ConvertEnumTypeIR( + const EnumTypeIR *enump) { + abi_dump::EnumType added_enum_type; + added_enum_type.set_access(AccessIRToProtobuf(enump->GetAccess())); + added_enum_type.set_underlying_type(enump->GetUnderlyingType()); + if (!AddTypeInfo(added_enum_type.mutable_type_info(), enump) || + !AddEnumFields(&added_enum_type, enump)) { + llvm::errs() << "EnumTypeIR could not be converted\n"; + ::exit(1); + } + return added_enum_type; +} + +abi_dump::GlobalVarDecl IRToProtobufConverter::ConvertGlobalVarIR( + const GlobalVarIR *global_varp) { + abi_dump::GlobalVarDecl added_global_var; + added_global_var.set_referenced_type(global_varp->GetReferencedType()); + added_global_var.set_source_file(global_varp->GetSourceFile()); + added_global_var.set_name(global_varp->GetName()); + added_global_var.set_linker_set_key(global_varp->GetLinkerSetKey()); + added_global_var.set_access( + AccessIRToProtobuf(global_varp->GetAccess())); + return added_global_var; +} + +abi_dump::PointerType IRToProtobufConverter::ConvertPointerTypeIR( + const PointerTypeIR *pointerp) { + abi_dump::PointerType added_pointer_type; + if (!AddTypeInfo(added_pointer_type.mutable_type_info(), pointerp)) { + llvm::errs() << "PointerTypeIR could not be converted\n"; + ::exit(1); + } + return added_pointer_type; +} + +abi_dump::QualifiedType IRToProtobufConverter::ConvertQualifiedTypeIR( + const QualifiedTypeIR *qualtypep) { + abi_dump::QualifiedType added_qualified_type; + if (!AddTypeInfo(added_qualified_type.mutable_type_info(), qualtypep)) { + llvm::errs() << "QualifiedTypeIR could not be converted\n"; + ::exit(1); + } + added_qualified_type.set_is_const(qualtypep->IsConst()); + added_qualified_type.set_is_volatile(qualtypep->IsVolatile()); + added_qualified_type.set_is_restricted(qualtypep->IsRestricted()); + return added_qualified_type; +} + +abi_dump::BuiltinType IRToProtobufConverter::ConvertBuiltinTypeIR( + const BuiltinTypeIR *builtin_typep) { + abi_dump::BuiltinType added_builtin_type; + added_builtin_type.set_is_unsigned(builtin_typep->IsUnsigned()); + added_builtin_type.set_is_integral(builtin_typep->IsIntegralType()); + if (!AddTypeInfo(added_builtin_type.mutable_type_info(), builtin_typep)) { + llvm::errs() << "BuiltinTypeIR could not be converted\n"; + ::exit(1); + } + return added_builtin_type; +} + +abi_dump::ArrayType IRToProtobufConverter::ConvertArrayTypeIR( + const ArrayTypeIR *array_typep) { + abi_dump::ArrayType added_array_type; + if (!AddTypeInfo(added_array_type.mutable_type_info(), array_typep)) { + llvm::errs() << "ArrayTypeIR could not be converted\n"; + ::exit(1); + } + return added_array_type; +} + +abi_dump::LvalueReferenceType +IRToProtobufConverter::ConvertLvalueReferenceTypeIR( + const LvalueReferenceTypeIR *lvalue_reference_typep) { + abi_dump::LvalueReferenceType added_lvalue_reference_type; + if (!AddTypeInfo(added_lvalue_reference_type.mutable_type_info(), + lvalue_reference_typep)) { + llvm::errs() << "LvalueReferenceTypeIR could not be converted\n"; + ::exit(1); + } + return added_lvalue_reference_type; +} + +abi_dump::RvalueReferenceType +IRToProtobufConverter::ConvertRvalueReferenceTypeIR( + const RvalueReferenceTypeIR *rvalue_reference_typep) { + abi_dump::RvalueReferenceType added_rvalue_reference_type; + if (!AddTypeInfo(added_rvalue_reference_type.mutable_type_info(), + rvalue_reference_typep)) { + llvm::errs() << "RvalueReferenceTypeIR could not be converted\n"; + ::exit(1); + } + return added_rvalue_reference_type; +} + + +bool IRDiffToProtobufConverter::AddTypeInfoDiff( + abi_diff::TypeInfoDiff *type_info_diff_protobuf, + const TypeDiffIR *type_diff_ir) { + abi_diff::TypeInfo *old_type_info_protobuf = + type_info_diff_protobuf->mutable_old_type_info(); + abi_diff::TypeInfo *new_type_info_protobuf = + type_info_diff_protobuf->mutable_new_type_info(); + if (old_type_info_protobuf == nullptr || new_type_info_protobuf == nullptr) { + return false; + } + const std::pair &sizes = type_diff_ir->GetSizes(); + const std::pair &alignments = + type_diff_ir->GetAlignments(); + old_type_info_protobuf->set_size(sizes.first); + new_type_info_protobuf->set_size(sizes.second); + + old_type_info_protobuf->set_alignment(alignments.first); + new_type_info_protobuf->set_alignment(alignments.second); + return true; +} + +bool IRDiffToProtobufConverter::AddVTableLayoutDiff( + abi_diff::VTableLayoutDiff *vtable_layout_diff_protobuf, + const VTableLayoutDiffIR *vtable_layout_diff_ir) { + abi_dump:: VTableLayout *old_vtable = + vtable_layout_diff_protobuf->mutable_old_vtable(); + abi_dump:: VTableLayout *new_vtable = + vtable_layout_diff_protobuf->mutable_new_vtable(); + if (old_vtable == nullptr || new_vtable == nullptr || + !SetIRToProtobufVTableLayout(old_vtable, + vtable_layout_diff_ir->GetOldVTable()) || + !SetIRToProtobufVTableLayout(new_vtable, + vtable_layout_diff_ir->GetNewVTable())) { + return false; + } + return true; +} + +template +static bool CopyBaseSpecifiersDiffIRToProtobuf( + google::protobuf::RepeatedPtrField *dst, + const std::vector &bases_ir) { + for (auto &&base_ir : bases_ir) { + T *added_base = dst->Add(); + if (!SetIRToProtobufBaseSpecifier(added_base, base_ir)) { + return false; + } + } + return true; +} + +bool IRDiffToProtobufConverter::AddBaseSpecifierDiffs( + abi_diff::CXXBaseSpecifierDiff *base_specifiers_diff_protobuf, + const CXXBaseSpecifierDiffIR *base_specifiers_diff_ir) { + if (!CopyBaseSpecifiersDiffIRToProtobuf( + base_specifiers_diff_protobuf->mutable_old_bases(), + base_specifiers_diff_ir->GetOldBases()) || + !CopyBaseSpecifiersDiffIRToProtobuf( + base_specifiers_diff_protobuf->mutable_new_bases(), + base_specifiers_diff_ir->GetNewBases())) { + return false; + } + return true; +} + +bool IRDiffToProtobufConverter::AddRecordFieldsRemoved( + abi_diff::RecordTypeDiff *record_diff_protobuf, + const std::vector &record_fields_removed_ir) { + for (auto &&record_field_ir : record_fields_removed_ir) { + abi_dump::RecordFieldDecl *field_removed = + record_diff_protobuf->add_fields_removed(); + if (field_removed == nullptr) { + return false; + } + SetIRToProtobufRecordField(field_removed, record_field_ir); + } + return true; +} + +bool IRDiffToProtobufConverter::AddRecordFieldDiffs( + abi_diff::RecordTypeDiff *record_diff_protobuf, + const std::vector &record_field_diffs_ir) { + for (auto &&record_field_diff_ir : record_field_diffs_ir) { + abi_diff::RecordFieldDeclDiff *record_field_diff = + record_diff_protobuf->add_fields_diff(); + if (record_field_diff == nullptr) { + return false; + } + abi_dump::RecordFieldDecl *old_field = + record_field_diff->mutable_old_field(); + abi_dump::RecordFieldDecl *new_field = + record_field_diff->mutable_new_field(); + if (old_field == nullptr || new_field == nullptr) { + return false; + } + SetIRToProtobufRecordField(old_field, + record_field_diff_ir.GetOldField()); + SetIRToProtobufRecordField(new_field, + record_field_diff_ir.GetNewField()); + } + return true; +} + +abi_diff::RecordTypeDiff IRDiffToProtobufConverter::ConvertRecordTypeDiffIR( + const RecordTypeDiffIR *record_type_diff_ir) { + abi_diff::RecordTypeDiff record_type_diff_protobuf; + record_type_diff_protobuf.set_name(record_type_diff_ir->GetName()); + const TypeDiffIR *type_diff_ir = record_type_diff_ir->GetTypeDiff(); + // If a type_info diff exists + if (type_diff_ir != nullptr) { + abi_diff::TypeInfoDiff *type_info_diff = + record_type_diff_protobuf.mutable_type_info_diff(); + if (!AddTypeInfoDiff(type_info_diff, type_diff_ir)) { + llvm::errs() << "RecordType could not be converted\n"; + ::exit(1); + } + } + // If vtables differ. + const VTableLayoutDiffIR *vtable_layout_diff_ir = + record_type_diff_ir->GetVTableLayoutDiff(); + if (vtable_layout_diff_ir != nullptr) { + abi_diff::VTableLayoutDiff *vtable_layout_diff_protobuf = + record_type_diff_protobuf.mutable_vtable_layout_diff(); + if (!AddVTableLayoutDiff(vtable_layout_diff_protobuf, + vtable_layout_diff_ir)) { + llvm::errs() << "VTable layout diff could not be added\n"; + ::exit(1); + } + } + // If base specifiers differ. + const CXXBaseSpecifierDiffIR *base_specifier_diff_ir = + record_type_diff_ir->GetBaseSpecifiers(); + if ( base_specifier_diff_ir != nullptr) { + abi_diff::CXXBaseSpecifierDiff *base_specifier_diff_protobuf = + record_type_diff_protobuf.mutable_bases_diff(); + if (!AddBaseSpecifierDiffs(base_specifier_diff_protobuf, + base_specifier_diff_ir)) { + llvm::errs() << "Base Specifier diff could not be added\n"; + ::exit(1); + } + } + // Field diffs + if (!AddRecordFieldsRemoved(&record_type_diff_protobuf, + record_type_diff_ir->GetFieldsRemoved()) || + !AddRecordFieldDiffs(&record_type_diff_protobuf, + record_type_diff_ir->GetFieldDiffs())) { + llvm::errs() << "Record Field diff could not be added\n"; + ::exit(1); + } + return record_type_diff_protobuf; +} + +bool IRDiffToProtobufConverter::AddEnumUnderlyingTypeDiff( + abi_diff::UnderlyingTypeDiff *underlying_type_diff_protobuf, + const std::pair *underlying_type_diff_ir) { + if (underlying_type_diff_protobuf == nullptr) { + return false; + } + underlying_type_diff_protobuf->set_old_type(underlying_type_diff_ir->first); + underlying_type_diff_protobuf->set_new_type(underlying_type_diff_ir->second); + return true; +} + +static bool AddEnumFields( + google::protobuf::RepeatedPtrField *dst, + const std::vector &enum_fields) { + for (auto &&enum_field : enum_fields) { + abi_dump::EnumFieldDecl *added_enum_field = dst->Add(); + if (!SetIRToProtobufEnumField(added_enum_field, enum_field)) { + return false; + } + } + return true; +} + +static bool AddEnumFieldDiffs( + google::protobuf::RepeatedPtrField *dst, + const std::vector &fields_diff_ir) { + for (auto &&field_diff_ir : fields_diff_ir) { + abi_diff::EnumFieldDeclDiff *field_diff_protobuf = dst->Add(); + if (field_diff_protobuf == nullptr) { + return false; + } + if (!SetIRToProtobufEnumField(field_diff_protobuf->mutable_old_field(), + field_diff_ir.GetOldField()) || + !SetIRToProtobufEnumField(field_diff_protobuf->mutable_new_field(), + field_diff_ir.GetNewField())) { + return false; + } + } + return true; +} + +abi_diff::EnumTypeDiff IRDiffToProtobufConverter::ConvertEnumTypeDiffIR( + const EnumTypeDiffIR *enum_type_diff_ir) { + abi_diff::EnumTypeDiff enum_type_diff_protobuf; + enum_type_diff_protobuf.set_name(enum_type_diff_ir->GetName()); + const std::pair *underlying_type_diff = + enum_type_diff_ir->GetUnderlyingTypeDiff(); + if ((underlying_type_diff != nullptr && + !AddEnumUnderlyingTypeDiff( + enum_type_diff_protobuf.mutable_underlying_type_diff(), + underlying_type_diff)) || + !AddEnumFields(enum_type_diff_protobuf.mutable_fields_removed(), + enum_type_diff_ir->GetFieldsRemoved()) || + !AddEnumFields(enum_type_diff_protobuf.mutable_fields_added(), + enum_type_diff_ir->GetFieldsAdded()) || + !AddEnumFieldDiffs(enum_type_diff_protobuf.mutable_fields_diff(), + enum_type_diff_ir->GetFieldsDiff())) { + llvm::errs() << "Enum field diff could not be added\n"; + ::exit(1); + } + return enum_type_diff_protobuf; +} + +abi_diff::GlobalVarDeclDiff IRDiffToProtobufConverter::ConvertGlobalVarDiffIR( + const GlobalVarDiffIR *global_var_diff_ir) { + abi_diff::GlobalVarDeclDiff global_var_diff; + global_var_diff.set_name(global_var_diff_ir->GetName()); + abi_dump::GlobalVarDecl *old_global_var = global_var_diff.mutable_old(); + abi_dump::GlobalVarDecl *new_global_var = global_var_diff.mutable_new_(); + if (old_global_var == nullptr || new_global_var == nullptr) { + llvm::errs() << "Globar Var diff could not be added\n"; + ::exit(1); + } + *old_global_var = + IRToProtobufConverter::ConvertGlobalVarIR( + global_var_diff_ir->GetOldGlobalVar()); + *new_global_var = + IRToProtobufConverter::ConvertGlobalVarIR( + global_var_diff_ir->GetNewGlobalVar()); + return global_var_diff; +} + +abi_diff::FunctionDeclDiff IRDiffToProtobufConverter::ConvertFunctionDiffIR( + const FunctionDiffIR *function_diff_ir) { + abi_diff::FunctionDeclDiff function_diff; + function_diff.set_name(function_diff_ir->GetName()); + abi_dump::FunctionDecl *old_function = function_diff.mutable_old(); + abi_dump::FunctionDecl *new_function = function_diff.mutable_new_(); + if (old_function == nullptr || new_function == nullptr) { + llvm::errs() << "Function diff could not be added\n"; + ::exit(1); + } + *old_function = + IRToProtobufConverter::ConvertFunctionIR( + function_diff_ir->GetOldFunction()); + *new_function = + IRToProtobufConverter::ConvertFunctionIR( + function_diff_ir->GetNewFunction()); + return function_diff; +} + +bool ProtobufIRDumper::AddLinkableMessageIR (const LinkableMessageIR *lm) { + // No RTTI + switch (lm->GetKind()) { + case RecordTypeKind: + return AddRecordTypeIR(static_cast(lm)); + case EnumTypeKind: + return AddEnumTypeIR(static_cast(lm)); + case PointerTypeKind: + return AddPointerTypeIR(static_cast(lm)); + case QualifiedTypeKind: + return AddQualifiedTypeIR(static_cast(lm)); + case ArrayTypeKind: + return AddArrayTypeIR(static_cast(lm)); + case LvalueReferenceTypeKind: + return AddLvalueReferenceTypeIR( + static_cast(lm)); + case RvalueReferenceTypeKind: + return AddRvalueReferenceTypeIR( + static_cast(lm)); + case BuiltinTypeKind: + return AddBuiltinTypeIR(static_cast(lm)); + case GlobalVarKind: + return AddGlobalVarIR(static_cast(lm)); + case FunctionKind: + return AddFunctionIR(static_cast(lm)); + } + return false; +} + +bool ProtobufIRDumper::AddRecordTypeIR(const RecordTypeIR *recordp) { + abi_dump::RecordType *added_record_type = tu_ptr_->add_record_types(); + if (!added_record_type) { + return false; + } + *added_record_type = ConvertRecordTypeIR(recordp); + return true; +} + +bool ProtobufIRDumper::AddFunctionIR(const FunctionIR *functionp) { + abi_dump::FunctionDecl *added_function = tu_ptr_->add_functions(); + if (!added_function) { + return false; + } + *added_function = ConvertFunctionIR(functionp); + return true; +} + +bool ProtobufIRDumper::AddEnumTypeIR(const EnumTypeIR *enump) { + abi_dump::EnumType *added_enum_type = tu_ptr_->add_enum_types(); + if (!added_enum_type) { + return false; + } + *added_enum_type = ConvertEnumTypeIR(enump); + return true; +} + +bool ProtobufIRDumper::AddGlobalVarIR(const GlobalVarIR *global_varp) { + abi_dump::GlobalVarDecl *added_global_var = tu_ptr_->add_global_vars(); + if (!added_global_var) { + return false; + } + *added_global_var = ConvertGlobalVarIR(global_varp); + return true; +} + +bool ProtobufIRDumper::AddPointerTypeIR(const PointerTypeIR *pointerp) { + abi_dump::PointerType *added_pointer_type = tu_ptr_->add_pointer_types(); + if (!added_pointer_type) { + return false; + } + *added_pointer_type = ConvertPointerTypeIR(pointerp); + return true; +} + +bool ProtobufIRDumper::AddQualifiedTypeIR(const QualifiedTypeIR *qualtypep) { + abi_dump::QualifiedType *added_qualified_type = + tu_ptr_->add_qualified_types(); + if (!added_qualified_type) { + return false; + } + *added_qualified_type = ConvertQualifiedTypeIR(qualtypep); + return true; +} + +bool ProtobufIRDumper::AddBuiltinTypeIR(const BuiltinTypeIR *builtin_typep) { + abi_dump::BuiltinType *added_builtin_type = + tu_ptr_->add_builtin_types(); + if (!added_builtin_type) { + return false; + } + *added_builtin_type = ConvertBuiltinTypeIR(builtin_typep); + return true; +} + +bool ProtobufIRDumper::AddArrayTypeIR(const ArrayTypeIR *array_typep) { + abi_dump::ArrayType *added_array_type = + tu_ptr_->add_array_types(); + if (!added_array_type) { + return false; + } + *added_array_type = ConvertArrayTypeIR(array_typep); + return true; +} + +bool ProtobufIRDumper::AddLvalueReferenceTypeIR( + const LvalueReferenceTypeIR *lvalue_reference_typep) { + abi_dump::LvalueReferenceType *added_lvalue_reference_type = + tu_ptr_->add_lvalue_reference_types(); + if (!added_lvalue_reference_type) { + return false; + } + *added_lvalue_reference_type = + ConvertLvalueReferenceTypeIR(lvalue_reference_typep); + return true; +} + +bool ProtobufIRDumper::AddRvalueReferenceTypeIR( + const RvalueReferenceTypeIR *rvalue_reference_typep) { + abi_dump::RvalueReferenceType *added_rvalue_reference_type = + tu_ptr_->add_rvalue_reference_types(); + if (!added_rvalue_reference_type) { + return false; + } + *added_rvalue_reference_type = + ConvertRvalueReferenceTypeIR(rvalue_reference_typep); + return true; +} + +bool ProtobufIRDumper::Dump() { + GOOGLE_PROTOBUF_VERIFY_VERSION; + assert( tu_ptr_.get() != nullptr); + std::ofstream text_output(dump_path_); + google::protobuf::io::OstreamOutputStream text_os(&text_output); + return google::protobuf::TextFormat::Print(*tu_ptr_.get(), &text_os); +} + +void ProtobufIRDiffDumper::AddLibNameIR(const std::string &name) { + diff_tu_->set_lib_name(name); +} + +void ProtobufIRDiffDumper::AddArchIR(const std::string &arch) { + diff_tu_->set_arch(arch); +} + +CompatibilityStatusIR ProtobufIRDiffDumper::GetCompatibilityStatusIR() { + if (diff_tu_->functions_removed().size() != 0 || + diff_tu_->global_vars_removed().size() != 0 || + diff_tu_->function_diffs().size() != 0 || + diff_tu_->global_var_diffs().size() != 0 || + diff_tu_->enum_type_diffs().size() != 0 || + diff_tu_->record_type_diffs().size() != 0 || + diff_tu_->removed_elf_functions().size() != 0 || + diff_tu_->removed_elf_objects().size() != 0) { + return CompatibilityStatusIR::Incompatible; + } + + CompatibilityStatusIR combined_status = CompatibilityStatusIR::Compatible; + + if (diff_tu_->enum_type_extension_diffs().size() != 0 || + diff_tu_->functions_added().size() != 0 || + diff_tu_->global_vars_added().size() !=0 || + diff_tu_->added_elf_functions().size() != 0 || + diff_tu_->added_elf_objects().size() != 0) { + combined_status = combined_status | CompatibilityStatusIR::Extension; + } + + if (diff_tu_->unreferenced_enum_type_diffs().size() != 0 || + diff_tu_->unreferenced_enum_types_removed().size() != 0 || + diff_tu_->unreferenced_record_types_removed().size() != 0 || + diff_tu_->unreferenced_record_type_diffs().size() != 0 || + diff_tu_->unreferenced_enum_type_extension_diffs().size() != 0 || + diff_tu_->unreferenced_record_types_added().size() != 0 || + diff_tu_->unreferenced_enum_types_added().size()) { + combined_status = + combined_status | CompatibilityStatusIR::UnreferencedChanges; + } + + return combined_status; +} + +void ProtobufIRDiffDumper::AddCompatibilityStatusIR( + CompatibilityStatusIR status) { + diff_tu_->set_compatibility_status(CompatibilityStatusIRToProtobuf(status)); +} + +bool ProtobufIRDiffDumper::AddDiffMessageIR( + const DiffMessageIR *message, + const std::string &type_stack, + DiffKind diff_kind) { + switch (message->Kind()) { + case RecordTypeKind: + return AddRecordTypeDiffIR( + static_cast(message), + type_stack, diff_kind); + case EnumTypeKind: + return AddEnumTypeDiffIR( + static_cast(message), + type_stack, diff_kind); + case GlobalVarKind: + return AddGlobalVarDiffIR( + static_cast(message), + type_stack, diff_kind); + case FunctionKind: + return AddFunctionDiffIR( + static_cast(message), + type_stack, diff_kind); + default: + break; + } + llvm::errs() << "Dump Diff attempted on something not a user defined type" << + "/ function / global variable\n"; + return false; +} + +bool ProtobufIRDiffDumper::AddLinkableMessageIR( + const LinkableMessageIR *message, + DiffKind diff_kind) { + switch (message->GetKind()) { + case RecordTypeKind: + return AddLoneRecordTypeDiffIR( + static_cast(message), diff_kind); + case EnumTypeKind: + return AddLoneEnumTypeDiffIR( + static_cast(message), diff_kind); + case GlobalVarKind: + return AddLoneGlobalVarDiffIR( + static_cast(message), diff_kind); + case FunctionKind: + return AddLoneFunctionDiffIR( + static_cast(message), diff_kind); + default: + break; + } + llvm::errs() << "Dump Diff attempted on something not a user defined type" << + "/ function / global variable\n"; + return false; +} + +bool ProtobufIRDiffDumper::AddElfSymbolMessageIR (const ElfSymbolIR *elf_symbol, + DiffKind diff_kind) { + switch (elf_symbol->GetKind()) { + case ElfSymbolIR::ElfFunctionKind: + return AddElfFunctionIR(static_cast(elf_symbol), + diff_kind); + break; + case ElfSymbolIR::ElfObjectKind: + return AddElfObjectIR(static_cast(elf_symbol), + diff_kind); + break; + } + // Any other kind is invalid + return false; +} + +bool ProtobufIRDiffDumper::AddElfFunctionIR( + const ElfFunctionIR *elf_function_ir, DiffKind diff_kind) { + abi_dump::ElfFunction *added_elf_function = nullptr; + switch(diff_kind) { + case DiffKind::Removed: + added_elf_function = diff_tu_->add_removed_elf_functions(); + break; + case DiffKind::Added: + added_elf_function = diff_tu_->add_added_elf_functions(); + break; + default: + llvm::errs() << "Invalid call to AddElfFunctionIR\n"; + return false; + } + if (added_elf_function == nullptr) { + return false; + } + *added_elf_function = + IRToProtobufConverter::ConvertElfFunctionIR(elf_function_ir); + return true; +} + +bool ProtobufIRDiffDumper::AddElfObjectIR( + const ElfObjectIR *elf_object_ir, DiffKind diff_kind) { + abi_dump::ElfObject *added_elf_object = nullptr; + switch(diff_kind) { + case DiffKind::Removed: + added_elf_object = diff_tu_->add_removed_elf_objects(); + break; + case DiffKind::Added: + added_elf_object = diff_tu_->add_added_elf_objects(); + break; + default: + llvm::errs() << "Invalid call to AddElfObjectIR\n"; + return false; + } + if (added_elf_object == nullptr) { + return false; + } + *added_elf_object = + IRToProtobufConverter::ConvertElfObjectIR(elf_object_ir); + return true; +} + +bool ProtobufIRDiffDumper::AddLoneRecordTypeDiffIR( + const RecordTypeIR *record_type_ir, + DiffKind diff_kind) { + abi_dump::RecordType *added_record_type = nullptr; + switch (diff_kind) { + case DiffKind::Removed: + // Referenced record types do not get reported as added / removed, + // the diff shows up in the parent type / function/ global variable + // referencing the record. + added_record_type = diff_tu_->add_unreferenced_record_types_removed(); + break; + case DiffKind::Added: + added_record_type = diff_tu_->add_unreferenced_record_types_added(); + break; + default: + llvm::errs() << "Invalid call to AddLoneRecordTypeDiffIR\n"; + return false; + } + if (added_record_type == nullptr) { + return false; + } + *added_record_type = + IRToProtobufConverter::ConvertRecordTypeIR(record_type_ir); + return true; +} + +bool ProtobufIRDiffDumper::AddLoneFunctionDiffIR( + const FunctionIR *function_ir, + DiffKind diff_kind) { + abi_dump::FunctionDecl *added_function = nullptr; + switch (diff_kind) { + case DiffKind::Removed: + added_function = diff_tu_->add_functions_removed(); + break; + case DiffKind::Added: + added_function = diff_tu_->add_functions_added(); + break; + default: + llvm::errs() << "Invalid call to AddLoneFunctionDiffIR\n"; + return false; + } + *added_function = IRToProtobufConverter::ConvertFunctionIR(function_ir); + return true; +} + +bool ProtobufIRDiffDumper::AddLoneEnumTypeDiffIR( + const EnumTypeIR *enum_type_ir, DiffKind diff_kind) { + abi_dump::EnumType *added_enum_type = nullptr; + switch (diff_kind) { + case DiffKind::Removed: + // Referenced enum types do not get reported as added / removed, + // the diff shows up in the parent type / function/ global variable + // referencing the enum. + added_enum_type = diff_tu_->add_unreferenced_enum_types_removed(); + break; + case DiffKind::Added: + added_enum_type = diff_tu_->add_unreferenced_enum_types_added(); + break; + default: + llvm::errs() << "Invalid call to AddLoneRecordTypeDiffIR\n"; + return false; + } + if (added_enum_type == nullptr) { + return false; + } + *added_enum_type = IRToProtobufConverter::ConvertEnumTypeIR(enum_type_ir); + return true; +} + +bool ProtobufIRDiffDumper::AddLoneGlobalVarDiffIR( + const GlobalVarIR *global_var_ir, DiffKind diff_kind) { + abi_dump::GlobalVarDecl *added_global_var = nullptr; + switch (diff_kind) { + case DiffKind::Removed: + added_global_var = diff_tu_->add_global_vars_removed(); + break; + case DiffKind::Added: + added_global_var = diff_tu_->add_global_vars_added(); + break; + default: + llvm::errs() << "Invalid call to AddLoneFunctionDiffIR\n"; + return false; + } + *added_global_var = IRToProtobufConverter::ConvertGlobalVarIR(global_var_ir); + return true; +} + +bool ProtobufIRDiffDumper::AddRecordTypeDiffIR( + const RecordTypeDiffIR *record_diff_ir, + const std::string &type_stack, + DiffKind diff_kind) { + abi_diff::RecordTypeDiff *added_record_type_diff = nullptr; + switch (diff_kind) { + case DiffKind::Unreferenced: + added_record_type_diff = diff_tu_->add_unreferenced_record_type_diffs(); + break; + case DiffKind::Referenced: + added_record_type_diff = diff_tu_->add_record_type_diffs(); + break; + default: + break; + } + if (!added_record_type_diff) { + return false; + } + + *added_record_type_diff = + IRDiffToProtobufConverter::ConvertRecordTypeDiffIR(record_diff_ir); + added_record_type_diff->set_type_stack(type_stack); + return true; +} + +bool ProtobufIRDiffDumper::AddFunctionDiffIR( + const FunctionDiffIR *function_diff_ir, const std::string &type_stack, + DiffKind diff_kind) { + abi_diff::FunctionDeclDiff *added_function_diff = + diff_tu_->add_function_diffs(); + if (!added_function_diff) { + return false; + } + *added_function_diff = + IRDiffToProtobufConverter::ConvertFunctionDiffIR(function_diff_ir); + return true; +} + +bool ProtobufIRDiffDumper::AddEnumTypeDiffIR(const EnumTypeDiffIR *enum_diff_ir, + const std::string &type_stack, + DiffKind diff_kind) { + abi_diff::EnumTypeDiff *added_enum_type_diff = nullptr; + switch (diff_kind) { + case DiffKind::Unreferenced: + if (enum_diff_ir->IsExtended()) { + added_enum_type_diff = + diff_tu_->add_unreferenced_enum_type_extension_diffs(); + } else { + added_enum_type_diff = + diff_tu_->add_unreferenced_enum_type_diffs(); + } + break; + case DiffKind::Referenced: + if (enum_diff_ir->IsExtended()) { + added_enum_type_diff = + diff_tu_->add_enum_type_extension_diffs(); + } else { + added_enum_type_diff = + diff_tu_->add_enum_type_diffs(); + } + break; + default: + break; + } + if (!added_enum_type_diff) { + return false; + } + *added_enum_type_diff = + IRDiffToProtobufConverter::ConvertEnumTypeDiffIR(enum_diff_ir); + added_enum_type_diff->set_type_stack(type_stack); + return true; +} + +bool ProtobufIRDiffDumper::AddGlobalVarDiffIR( + const GlobalVarDiffIR *global_var_diff_ir, + const std::string &type_stack, + DiffKind diff_kind) { + abi_diff::GlobalVarDeclDiff *added_global_var_diff = + diff_tu_->add_global_var_diffs(); + if (!added_global_var_diff) { + return false; + } + *added_global_var_diff = + IRDiffToProtobufConverter::ConvertGlobalVarDiffIR(global_var_diff_ir); + return true; +} + +bool ProtobufIRDiffDumper::Dump() { + GOOGLE_PROTOBUF_VERIFY_VERSION; + assert(diff_tu_.get() != nullptr); + std::ofstream text_output(dump_path_); + google::protobuf::io::OstreamOutputStream text_os(&text_output); + return google::protobuf::TextFormat::Print(*diff_tu_.get(), &text_os); +} + +} //abi_util diff --git a/vndk/tools/header-checker/proto/abi_diff.proto b/vndk/tools/header-checker/proto/abi_diff.proto index 28dbd52b6..17cfc925e 100644 --- a/vndk/tools/header-checker/proto/abi_diff.proto +++ b/vndk/tools/header-checker/proto/abi_diff.proto @@ -4,97 +4,112 @@ import "development/vndk/tools/header-checker/proto/abi_dump.proto"; package abi_diff; -message RecordFieldDeclDiff { - optional abi_dump.RecordFieldDecl old = 1; - optional abi_dump.RecordFieldDecl new = 2; - optional uint32 index = 3; +message TypeInfo { + optional uint64 size = 1; + optional uint32 alignment = 2; } -message EnumFieldDeclDiff { - optional abi_dump.EnumFieldDecl old = 1; - optional abi_dump.EnumFieldDecl new = 2; - optional uint32 index = 3; +message TypeInfoDiff { + optional TypeInfo old_type_info = 1; + optional TypeInfo new_type_info = 2; +} + +message VTableLayoutDiff { + optional abi_dump.VTableLayout old_vtable = 1; + optional abi_dump.VTableLayout new_vtable = 2; +} + +message RecordFieldDeclDiff { + optional abi_dump.RecordFieldDecl old_field = 1; + optional abi_dump.RecordFieldDecl new_field = 2; } message CXXBaseSpecifierDiff { - optional abi_dump.CXXBaseSpecifier old = 1; - optional abi_dump.CXXBaseSpecifier new = 2; - optional uint32 index = 3; + repeated abi_dump.CXXBaseSpecifier old_bases = 1; + repeated abi_dump.CXXBaseSpecifier new_bases = 2; } -message CXXVTableDiff { - optional abi_dump.VTableComponent old = 1; - optional abi_dump.VTableComponent new = 2; - optional uint32 index = 3; +message RecordTypeDiff { + optional string name = 1; + optional string type_stack = 2; + optional TypeInfoDiff type_info_diff = 3; + repeated abi_dump.RecordFieldDecl fields_removed = 4; + repeated RecordFieldDeclDiff fields_diff = 5; + optional CXXBaseSpecifierDiff bases_diff = 6; + optional VTableLayoutDiff vtable_layout_diff = 7; } -message BasicNamedAndTypedDeclDiff { - optional abi_dump.BasicNamedAndTypedDecl old = 1; - optional abi_dump.BasicNamedAndTypedDecl new = 2; +message UnderlyingTypeDiff { + optional string old_type = 1; + optional string new_type = 2; } -message RecordDeclDiff { - repeated RecordFieldDeclDiff field_diffs = 1; - repeated CXXBaseSpecifierDiff base_diffs = 2; - repeated CXXVTableDiff vtable_diffs = 3; - optional BasicNamedAndTypedDeclDiff type_diff = 4; - optional string name = 5; +message EnumFieldDeclDiff { + optional abi_dump.EnumFieldDecl old_field = 1; + optional abi_dump.EnumFieldDecl new_field = 2; } -message EnumDeclDiff { - repeated EnumFieldDeclDiff field_diffs = 1; - optional BasicNamedAndTypedDeclDiff type_diff = 2; - optional string name = 3; -} - -message ReturnTypeDiff { - optional abi_dump.BasicNamedAndTypedDecl old = 1; - optional abi_dump.BasicNamedAndTypedDecl new = 2; -} - -message ParamDeclDiff { - optional abi_dump.ParamDecl old = 1; - optional abi_dump.ParamDecl new = 2; - optional uint32 index = 3; +message EnumTypeDiff { + optional string type_stack = 1; + optional string name = 2; + repeated EnumFieldDeclDiff fields_diff = 3; + optional UnderlyingTypeDiff underlying_type_diff = 4; + repeated abi_dump.EnumFieldDecl fields_added = 5; + repeated abi_dump.EnumFieldDecl fields_removed = 6; } message FunctionDeclDiff { - optional ReturnTypeDiff return_type_diffs = 1; - repeated ParamDeclDiff param_diffs = 2; - optional string name = 3; + // Template diffs are not required since in C++, they will show up in mangled + // names and in C, templates are not supported. + optional string type_stack = 1; + optional string name = 2; + optional abi_dump.FunctionDecl old = 3; + optional abi_dump.FunctionDecl new = 4; } message GlobalVarDeclDiff { - optional BasicNamedAndTypedDeclDiff type_diff = 1; -} - -enum CompatibilityStatus { - COMPATIBLE = 0; - EXTENSION = 1; - INCOMPATIBLE = 4; + optional string type_stack = 1; + optional string name = 2; + optional abi_dump.GlobalVarDecl old = 3; // Old global var + optional abi_dump.GlobalVarDecl new = 4; // New global var } message TranslationUnitDiff { // Library Name optional string lib_name = 1; optional string arch = 2; - // Differing Elements. - repeated RecordDeclDiff records_diff = 3; - repeated EnumDeclDiff enums_diff = 4; - repeated FunctionDeclDiff functions_diff = 5; - repeated GlobalVarDeclDiff global_vars_diff = 6; - // Removed Elements. - repeated abi_dump.RecordDecl records_removed = 7; - repeated abi_dump.FunctionDecl functions_removed = 8; - repeated abi_dump.EnumDecl enums_removed = 9; - repeated abi_dump.GlobalVarDecl global_vars_removed = 10; - // Added Elements. - repeated abi_dump.RecordDecl records_added = 11; - repeated abi_dump.FunctionDecl functions_added = 12; - repeated abi_dump.EnumDecl enums_added = 13; - repeated abi_dump.GlobalVarDecl global_vars_added = 14; + // Records. + repeated RecordTypeDiff record_type_diffs = 3; + repeated RecordTypeDiff unreferenced_record_type_diffs = 4; + repeated abi_dump.RecordType unreferenced_record_types_removed = 5; + repeated abi_dump.RecordType unreferenced_record_types_added = 6; + + // Enums + repeated EnumTypeDiff enum_type_diffs = 7; + repeated EnumTypeDiff enum_type_extension_diffs = 8; + repeated EnumTypeDiff unreferenced_enum_type_diffs = 9; + repeated EnumTypeDiff unreferenced_enum_type_extension_diffs = 10; + repeated abi_dump.EnumType unreferenced_enum_types_removed = 11; + repeated abi_dump.EnumType unreferenced_enum_types_added = 12; + + // Functions and Global variables. + repeated FunctionDeclDiff function_diffs = 13; + repeated GlobalVarDeclDiff global_var_diffs = 14; + + repeated abi_dump.FunctionDecl functions_removed = 15; + repeated abi_dump.GlobalVarDecl global_vars_removed = 16; + + repeated abi_dump.FunctionDecl functions_added = 17; + repeated abi_dump.GlobalVarDecl global_vars_added = 18; + + repeated abi_dump.ElfFunction added_elf_functions = 19; + repeated abi_dump.ElfFunction removed_elf_functions = 20; + + repeated abi_dump.ElfObject added_elf_objects = 21; + repeated abi_dump.ElfObject removed_elf_objects = 22; + // Compatiblity Status - optional CompatibilityStatus compatibility_status = 15; + optional CompatibilityStatus compatibility_status = 23; } // Not merged with TranslationUnitDiff to allow future extensions. @@ -108,3 +123,9 @@ message ConciseDiffReportInformation { message MergedTranslationUnitDiff { repeated ConciseDiffReportInformation diff_reports = 1; } + +enum CompatibilityStatus { + COMPATIBLE = 0; + EXTENSION = 1; + INCOMPATIBLE = 4; +} diff --git a/vndk/tools/header-checker/proto/abi_dump.proto b/vndk/tools/header-checker/proto/abi_dump.proto index 3633d208b..870329cc9 100644 --- a/vndk/tools/header-checker/proto/abi_dump.proto +++ b/vndk/tools/header-checker/proto/abi_dump.proto @@ -2,50 +2,84 @@ syntax = "proto2"; package abi_dump; -message BasicTypeAbi { - // The type's name. for eg : a record field's type. - optional string name = 1; - // Optional since templated types will not have this information. - optional uint64 size = 2 [default = 0]; - optional uint32 alignment = 3 [default = 0]; -} - enum AccessSpecifier { public_access = 1; private_access = 2; protected_access = 3; } +enum RecordKind { + struct_kind = 1; + class_kind = 2; + union_kind = 3; +} + message BasicNamedAndTypedDecl { - optional BasicTypeAbi type_abi = 1; // The TypedDecl's name. - optional string name = 2; - optional AccessSpecifier access = 3; - optional string linker_set_key = 4; + optional string name = 1; + optional uint64 size = 2 [default = 0]; + optional uint32 alignment = 3 [default = 0]; + optional string referenced_type = 4; + optional string source_file = 5; + optional string linker_set_key = 6; +} + +message ArrayType { + optional BasicNamedAndTypedDecl type_info = 1; +} + +message PointerType { + optional BasicNamedAndTypedDecl type_info = 1; +} + +message QualifiedType { + optional BasicNamedAndTypedDecl type_info = 1; + optional bool is_const = 6; + optional bool is_volatile = 7; + optional bool is_restricted = 8; +} + +message BuiltinType { + optional BasicNamedAndTypedDecl type_info = 1; + optional bool is_unsigned = 2; + optional bool is_integral = 3; +} + +message LvalueReferenceType { + optional BasicNamedAndTypedDecl type_info = 1; +} + +message RvalueReferenceType { + optional BasicNamedAndTypedDecl type_info = 1; } message FunctionDecl { - optional BasicNamedAndTypedDecl basic_abi = 1; - // Mangled name. - optional string mangled_function_name = 2; + // Return type reference + optional string return_type = 1; + optional string function_name = 2; optional string source_file = 3; repeated ParamDecl parameters = 4; optional TemplateInfo template_info = 5; + optional string linker_set_key = 6; + optional AccessSpecifier access = 7 [default = public_access]; } message ParamDecl { - optional BasicNamedAndTypedDecl basic_abi = 1; + optional string referenced_type = 1; optional bool default_arg = 2; } message RecordFieldDecl { // For future additions. - optional BasicNamedAndTypedDecl basic_abi = 1; + optional string referenced_type = 1; + optional uint64 field_offset = 2; + optional string field_name = 3; + optional AccessSpecifier access = 4 [default = public_access]; } message EnumFieldDecl { - optional BasicNamedAndTypedDecl basic_abi = 1; - optional int64 enum_field_value = 2; // assumption: fits int64 + optional int64 enum_field_value = 1; // assumption: fits int64 + optional string name = 3; } message TemplateInfo { @@ -53,17 +87,13 @@ message TemplateInfo { } message TemplateElement { - optional BasicTemplateElementAbi basic_abi = 1; - message BasicTemplateElementAbi { - optional BasicTypeAbi type_abi = 1; - optional string name = 2; - optional string linker_set_key = 3; - } + optional string referenced_type = 1; } message CXXBaseSpecifier { - optional BasicNamedAndTypedDecl basic_abi = 1; + optional string referenced_type = 1; optional bool is_virtual = 2; + optional AccessSpecifier access = 3; } message VTableComponent { @@ -89,30 +119,51 @@ message VTableLayout { repeated VTableComponent vtable_components = 1; } -message RecordDecl { - optional BasicNamedAndTypedDecl basic_abi = 1; +message RecordType { + optional BasicNamedAndTypedDecl type_info = 1; repeated RecordFieldDecl fields = 2; repeated CXXBaseSpecifier base_specifiers = 3; - optional string source_file = 4; optional TemplateInfo template_info = 5; - optional string mangled_record_name = 6; optional VTableLayout vtable_layout = 7; + optional AccessSpecifier access = 8 [default = public_access]; + optional bool is_anonymous = 9; + optional RecordKind record_kind = 10 [default = struct_kind]; } -message EnumDecl { - optional BasicNamedAndTypedDecl basic_abi = 1; - repeated EnumFieldDecl enum_fields = 2; - optional string source_file = 3; +message EnumType { + optional BasicNamedAndTypedDecl type_info = 1; + optional string underlying_type = 2; + repeated EnumFieldDecl enum_fields = 3; + optional AccessSpecifier access = 4 [default = public_access]; } message GlobalVarDecl { - optional BasicNamedAndTypedDecl basic_abi = 1; + optional string name = 1; optional string source_file = 2; + optional string linker_set_key = 3; + optional string referenced_type = 4; + optional AccessSpecifier access = 5 [default = public_access]; +} + +message ElfFunction { + optional string name = 1; +} + +message ElfObject { + optional string name = 1; } message TranslationUnit { - repeated RecordDecl records = 1; - repeated FunctionDecl functions = 2; - repeated EnumDecl enums = 3; - repeated GlobalVarDecl global_vars = 4; + repeated RecordType record_types = 1; + repeated EnumType enum_types = 2; + repeated PointerType pointer_types = 3; + repeated LvalueReferenceType lvalue_reference_types = 4; + repeated RvalueReferenceType rvalue_reference_types = 5; + repeated BuiltinType builtin_types = 6; + repeated QualifiedType qualified_types = 7; + repeated ArrayType array_types = 8; + repeated FunctionDecl functions = 9; + repeated GlobalVarDecl global_vars = 10; + repeated ElfFunction elf_functions = 11; + repeated ElfObject elf_objects = 12; } diff --git a/vndk/tools/header-checker/tests/input/example1.h b/vndk/tools/header-checker/tests/input/example1.h index c00cf06fb..1ff0c1cc4 100644 --- a/vndk/tools/header-checker/tests/input/example1.h +++ b/vndk/tools/header-checker/tests/input/example1.h @@ -7,9 +7,21 @@ extern "C" { #endif +struct ForwardDeclaration; +int uses_forward_decl(struct ForwardDeclaration *); + struct Hello { int foo; int bar; + enum {A, B} enum_field; + enum {C, D} enum_field2; + struct { + int a; + int b; + struct { + int c; + }; + }; }; #if defined(__cplusplus) @@ -24,8 +36,17 @@ struct CPPHello : private HelloAgain, public ByeAgain { cfloat_type cpp_bar; virtual int again() { return 0; } CPPHello() : cpp_foo(20), cpp_bar(1.234) { } + enum Bla{BLA = 1}; + int test_enum() {return CPPHello::BLA;} }; + +void fooVariadic (int &, int *, ...); + +int boo (const CPPHello, int *, float *) { + return CPPHello::BLA; +} + template struct StackNode { public: @@ -63,7 +84,7 @@ public: template class List { -protected: +public: /* * One element in the list. */ @@ -74,6 +95,7 @@ protected: inline T& getRef() { return mVal; } inline const T& getRef() const { return mVal; } private: + void PrivateNode(); friend class List; friend class _ListIterator; T mVal; @@ -83,11 +105,20 @@ protected: _Node *middle; }; + typedef List float_list; float_list float_list_test; - typedef List int_list; int_list int_list_test; +List::_Node node(2); +int ListMangle(int_list *, StackNode *); + +template +List castInterface(List parent, const char *childIndicator, bool emitError) {return List();} + +void format() { +castInterface(List(), "foo", true); +} #endif // EXAMPLE1_H_ diff --git a/vndk/tools/header-checker/tests/input/example2.h b/vndk/tools/header-checker/tests/input/example2.h index acf0f4208..bc225eb6b 100644 --- a/vndk/tools/header-checker/tests/input/example2.h +++ b/vndk/tools/header-checker/tests/input/example2.h @@ -22,6 +22,7 @@ struct HelloAgain { int bar_again; static int hello_forever; virtual int again(); + virtual ~HelloAgain() {} }; struct NowWeCrash; } // namespace test2