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
This commit is contained in:
Jayant Chowdhary
2017-07-05 17:56:33 -07:00
parent c57aaaf57c
commit 83fc92bb30
21 changed files with 5077 additions and 1167 deletions

View File

@@ -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",

View File

@@ -7,6 +7,7 @@
## Usage
header-abi-dumper -o <dump-file> <source_file> -I <export-include-dir-1> -I
<export-include-dir-2>.. -- <cflags>
For options : header-abi-dumper --help
# VNDK Header Abi Linker
@@ -16,6 +17,7 @@
## Usage
header-abi-linker -o <linked-abi-dump> <abi-dump1> <abi-dump2> <abi-dump3> ...
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 <old-abi-dump> -new <new-abi-dump> -o <report>
For options : header-abi-diff --help

View File

@@ -14,169 +14,262 @@
#include "abi_diff.h"
#include <header_abi_util.h>
#include <llvm/Support/raw_ostream.h>
#include <google/protobuf/text_format.h>
#include <google/protobuf/io/zero_copy_stream_impl.h>
#include <memory>
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
#include <stdlib.h>
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<abi_util::TextFormatToIRReader> old_reader =
TextFormatToIRReader::CreateTextFormatToIRReader("protobuf", old_dump_);
std::unique_ptr<abi_util::TextFormatToIRReader> 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<abi_util::IRDiffDumper> 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<abi_diff::TranslationUnitDiff> diff_tu(
new abi_diff::TranslationUnitDiff);
CompatibilityStatus record_status = Collect<abi_dump::RecordDecl>(
diff_tu->mutable_records_added(), diff_tu->mutable_records_removed(),
diff_tu->mutable_records_diff(), old_tu.records(), new_tu.records(),
ignored_symbols_);
template <typename F>
static void AddTypesToMap(std::map<std::string, const abi_util::TypeIR *> *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<abi_dump::FunctionDecl>(
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<std::string, const abi_util::TypeIR *> old_types;
std::map<std::string, const abi_util::TypeIR *> 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<abi_dump::EnumDecl>(
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<abi_dump::GlobalVarDecl>(
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 <typename T, typename TDiff>
abi_diff::CompatibilityStatus HeaderAbiDiff::Collect(
google::protobuf::RepeatedPtrField<T> *elements_added,
google::protobuf::RepeatedPtrField<T> *elements_removed,
google::protobuf::RepeatedPtrField<TDiff> *elements_diff,
const google::protobuf::RepeatedPtrField<T> &old_srcs,
const google::protobuf::RepeatedPtrField<T> &new_srcs,
const std::set<std::string> &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<std::string, const abi_util::TypeIR *> &old_types_map,
const std::map<std::string, const abi_util::TypeIR *> &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<std::string, const T*> old_elements_map;
std::map<std::string, const T*> new_elements_map;
AddToMap(&old_elements_map, old_srcs);
AddToMap(&new_elements_map, new_srcs);
template <typename T>
bool HeaderAbiDiff::CollectUserDefinedTypesInternal(
const std::vector<T> &old_ud_types,
const std::vector<T> &new_ud_types,
const std::map<std::string, const abi_util::TypeIR *> &old_types_map,
const std::map<std::string, const abi_util::TypeIR *> &new_types_map,
abi_util::IRDiffDumper *ir_diff_dumper) {
// No elf information for records and enums.
std::map<std::string, const T *> old_ud_types_map;
std::map<std::string, const T *> 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 <typename T, typename ElfSymbolType>
bool HeaderAbiDiff::CollectDynsymExportables(
const std::vector<T> &old_exportables,
const std::vector<T> &new_exportables,
const std::vector<ElfSymbolType> &old_elf_symbols,
const std::vector<ElfSymbolType> &new_elf_symbols,
const std::map<std::string, const abi_util::TypeIR *> &old_types_map,
const std::map<std::string, const abi_util::TypeIR *> &new_types_map,
abi_util::IRDiffDumper *ir_diff_dumper) {
std::map<std::string, const T *> old_exportables_map;
std::map<std::string, const T *> new_exportables_map;
std::map<std::string, const abi_util::ElfSymbolIR *> old_elf_symbol_map;
std::map<std::string, const abi_util::ElfSymbolIR *> 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 <typename T>
bool HeaderAbiDiff::Collect(
const std::map<std::string, const T*> &old_elements_map,
const std::map<std::string, const T*> &new_elements_map,
const std::map<std::string, const abi_util::ElfSymbolIR *> *old_elf_map,
const std::map<std::string, const abi_util::ElfSymbolIR *> *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<std::string, const abi_util::ElfSymbolIR *> &old_symbols,
const std::map<std::string, const abi_util::ElfSymbolIR *> &new_symbols,
abi_util::IRDiffDumper *ir_diff_dumper) {
std::vector<const abi_util::ElfSymbolIR *> removed_elements =
abi_util::FindRemovedElements(old_symbols, new_symbols);
std::vector<const abi_util::ElfSymbolIR *> 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<const abi_util::ElfSymbolIR *> &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 <typename T>
bool HeaderAbiDiff::PopulateRemovedElements(
google::protobuf::RepeatedPtrField<T> *dst,
const std::map<std::string, const T*> &old_elements_map,
const std::map<std::string, const T*> &new_elements_map,
const std::set<std::string> &ignored_symbols) {
std::vector<const T *> 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<std::string, const abi_util::ElfSymbolIR *> *elf_map,
abi_util::IRDiffDumper *ir_diff_dumper,
abi_util::IRDiffDumper::DiffKind diff_kind) {
std::vector<const T *> 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 <typename T, typename TDiff>
template <typename T>
bool HeaderAbiDiff::PopulateCommonElements(
google::protobuf::RepeatedPtrField<TDiff> *dst,
const std::map<std::string, const T *> &old_elements_map,
const std::map<std::string, const T *> &new_elements_map,
const std::set<std::string> &ignored_symbols) {
std::vector<std::pair<const T *, const T *>> common_elements;
typename std::map<std::string, const T *>::const_iterator old_element =
old_elements_map.begin();
typename std::map<std::string, const T *>::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<std::string, const abi_util::TypeIR *> &old_types,
const std::map<std::string, const abi_util::TypeIR *> &new_types,
abi_util::IRDiffDumper *ir_diff_dumper,
abi_util::IRDiffDumper::DiffKind diff_kind) {
std::vector<std::pair<const T *, const T *>> 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 <typename T>
bool HeaderAbiDiff::DumpLoneElements(
google::protobuf::RepeatedPtrField<T> *dst,
std::vector<const T *> &elements,
const std::set<std::string> &ignored_symbols) {
const std::map<std::string, const abi_util::ElfSymbolIR *> *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<T>(element, ignored_symbols)) {
if (abi_diff_wrappers::IgnoreSymbol<T>(
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 <typename T, typename TDiff>
template <typename T>
bool HeaderAbiDiff::DumpDiffElements(
google::protobuf::RepeatedPtrField<TDiff> *dst,
std::vector<std::pair<const T *,const T *>> &pairs,
const std::set<std::string> &ignored_symbols) {
const std::map<std::string, const abi_util::TypeIR *> &old_types,
const std::map<std::string, const abi_util::TypeIR *> &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<T>(old_element, ignored_symbols)) {
if (abi_diff_wrappers::IgnoreSymbol<T>(
old_element, ignored_symbols_,
[](const T *e) {return e->GetLinkerSetKey();})) {
continue;
}
abi_diff_wrappers::DiffWrapper<T, TDiff> diff_wrapper(old_element,
new_element);
std::unique_ptr<TDiff> 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<T> 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;
}

View File

@@ -14,6 +14,8 @@
#include "abi_diff_wrappers.h"
#include <ir_representation.h>
#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 <memory>
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
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<std::string> &ignored_symbols)
const std::set<std::string> &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 <typename T, typename TDiff>
static CompatibilityStatus Collect(
google::protobuf::RepeatedPtrField<T> *elements_added,
google::protobuf::RepeatedPtrField<T> *elements_removed,
google::protobuf::RepeatedPtrField<TDiff> *elements_diff,
const google::protobuf::RepeatedPtrField<T> &old_srcs,
const google::protobuf::RepeatedPtrField<T> &new_srcs,
const std::set<std::string> &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 <typename T, typename ElfSymbolType>
bool CollectDynsymExportables(
const std::vector<T> &old_exportables,
const std::vector<T> &new_exportables,
const std::vector<ElfSymbolType> &old_elf_symbols,
const std::vector<ElfSymbolType> &new_elf_symbols,
const std::map<std::string, const abi_util::TypeIR *> &old_types_map,
const std::map<std::string, const abi_util::TypeIR *> &new_types_map,
abi_util::IRDiffDumper *ir_diff_dumper);
template <typename T>
static inline void AddToMap(std::map<std::string, const T *> *dst,
const google::protobuf::RepeatedPtrField<T> &src);
bool Collect(
const std::map<std::string, const T *> &old_elements_map,
const std::map<std::string, const T *> &new_elements_map,
const std::map<std::string, const abi_util::ElfSymbolIR *> *old_elf_map,
const std::map<std::string, const abi_util::ElfSymbolIR *> *new_elf_map,
abi_util::IRDiffDumper *ir_diff_dumper);
bool CollectElfSymbols(
const std::map<std::string, const abi_util::ElfSymbolIR *> &old_symbols,
const std::map<std::string, const abi_util::ElfSymbolIR *> &new_symbols,
abi_util::IRDiffDumper *ir_diff_dumper);
bool PopulateElfElements(
std::vector<const abi_util::ElfSymbolIR *> &elf_elements,
abi_util::IRDiffDumper *ir_diff_dumper,
abi_util::IRDiffDumper::DiffKind diff_kind);
template <typename T>
static bool PopulateRemovedElements(
google::protobuf::RepeatedPtrField<T> *dst,
bool PopulateRemovedElements(
const std::map<std::string, const T *> &old_elements_map,
const std::map<std::string, const T *> &new_elements_map,
const std::set<std::string> &ignored_symbols);
const std::map<std::string, const abi_util::ElfSymbolIR *> *elf_map,
abi_util::IRDiffDumper *ir_diff_dumper,
abi_util::IRDiffDumper::DiffKind diff_kind);
template <typename T, typename TDiff>
static bool PopulateCommonElements(
google::protobuf::RepeatedPtrField<TDiff> *dst,
template <typename T>
bool PopulateCommonElements(
const std::map<std::string, const T *> &old_elements_map,
const std::map<std::string, const T *> &new_elements_map,
const std::set<std::string> &ignored_symbols);
const std::map<std::string, const abi_util::TypeIR *> &old_types,
const std::map<std::string, const abi_util::TypeIR *> &new_types,
abi_util::IRDiffDumper *ir_diff_dumper,
abi_util::IRDiffDumper::DiffKind diff_kind);
template <typename T, typename TDiff>
static bool DumpDiffElements(
google::protobuf::RepeatedPtrField<TDiff> *dst,
template <typename T>
bool DumpDiffElements(
std::vector<std::pair<const T *, const T *>> &pairs,
const std::set<std::string> &ignored_symbols);
const std::map<std::string, const abi_util::TypeIR *> &old_types,
const std::map<std::string, const abi_util::TypeIR *> &new_types,
abi_util::IRDiffDumper *ir_diff_dumper,
abi_util::IRDiffDumper::DiffKind diff_kind);
template <typename T>
static bool DumpLoneElements(google::protobuf::RepeatedPtrField<T> *dst,
std::vector<const T *> &elements,
const std::set<std::string> &ignored_symbols);
bool DumpLoneElements(
std::vector<const T *> &elements,
const std::map<std::string, const abi_util::ElfSymbolIR *> *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<std::string, const abi_util::TypeIR *> &old_types_map,
const std::map<std::string, const abi_util::TypeIR *> &new_types_map,
abi_util::IRDiffDumper *ir_diff_dumper);
template <typename T>
bool CollectUserDefinedTypesInternal(
const std::vector<T> &old_ud_types,
const std::vector<T> &new_ud_types,
const std::map<std::string, const abi_util::TypeIR *> &old_types_map,
const std::map<std::string, const abi_util::TypeIR *> &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<std::string> &ignored_symbols_;
bool check_all_apis_;
std::set<std::string> type_cache_;
};
template <typename T>
inline void HeaderAbiDiff::AddToMap(
std::map<std::string, const T *> *dst,
const google::protobuf::RepeatedPtrField<T> &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<CompatibilityStatus>(
static_cast<std::underlying_type<CompatibilityStatus>::type>(f) |
static_cast<std::underlying_type<CompatibilityStatus>::type>(s));
}
static inline CompatibilityStatus operator&(
CompatibilityStatus f, CompatibilityStatus s) {
return static_cast<CompatibilityStatus>(
static_cast<std::underlying_type<CompatibilityStatus>::type>(f) &
static_cast<std::underlying_type<CompatibilityStatus>::type>(s));
}

View File

@@ -14,52 +14,23 @@
#include "abi_diff_wrappers.h"
#include<header_abi_util.h>
#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 <header_abi_util.h>
#include <llvm/Support/raw_ostream.h>
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<std::string> *type_queue) {
if (!type_queue) {
return "";
}
std::string stack_str;
std::deque<std::string> 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<abi_util::EnumFieldIR> &old_fields,
const std::vector<abi_util::EnumFieldIR> &new_fields,
abi_util::EnumTypeDiffIR *enum_type_diff_ir) {
std::map<std::string, const abi_util::EnumFieldIR *> old_fields_map;
std::map<std::string, const abi_util::EnumFieldIR *> 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<const abi_util::EnumFieldIR *> removed_fields =
abi_util::FindRemovedElements(old_fields_map, new_fields_map);
std::vector<const abi_util::EnumFieldIR *> 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<std::pair<
const abi_util::EnumFieldIR *, const abi_util::EnumFieldIR *>> cf =
abi_util::FindCommonElements(old_fields_map, new_fields_map);
std::vector<abi_util::EnumFieldDiffIR> 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<std::string> *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<abi_util::EnumTypeDiffIR>();
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<std::pair<std::string, std::string>>(
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 <typename T>
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<EnumFieldDecl>(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<abi_util::VTableComponentIR> &old_components =
old_record->GetVTableLayout().GetVTableComponents();
const std::vector<abi_util::VTableComponentIR> &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<ParamDecl>(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<CXXBaseSpecifier>(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<abi_util::RecordFieldDiffIR>
DiffWrapperBase::CompareCommonRecordFields(
const abi_util::RecordFieldIR *old_field,
const abi_util::RecordFieldIR *new_field,
std::deque<std::string> *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<abi_util::RecordFieldDiffIR>(old_field, new_field);
}
return nullptr;
}
template <>
bool Diff<VTableComponent>(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<abi_util::RecordFieldDiffIR>,
std::vector<const abi_util::RecordFieldIR *>>
DiffWrapperBase::CompareRecordFields(
const std::vector<abi_util::RecordFieldIR> &old_fields,
const std::vector<abi_util::RecordFieldIR> &new_fields,
std::deque<std::string> *type_queue,
abi_util::IRDiffDumper::DiffKind diff_kind) {
std::pair<std::vector<abi_util::RecordFieldDiffIR>,
std::vector<const abi_util::RecordFieldIR *>> diffed_and_removed_fields;
std::map<std::string, const abi_util::RecordFieldIR *> old_fields_map;
std::map<std::string, const abi_util::RecordFieldIR *> new_fields_map;
std::map<uint64_t, const abi_util::RecordFieldIR *> old_fields_offset_map;
std::map<uint64_t, const abi_util::RecordFieldIR *> 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<const abi_util::RecordFieldIR *> 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<std::pair<
const abi_util::RecordFieldIR *, const abi_util::RecordFieldIR *>> cf =
abi_util::FindCommonElements(old_fields_map, new_fields_map);
for (auto &&common_fields : cf) {
std::unique_ptr<abi_util::RecordFieldDiffIR> 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 <typename T, typename TDiff>
template <typename Element, typename ElementDiff>
bool DiffWrapperBase<T, TDiff>::GetElementDiffs(
google::protobuf::RepeatedPtrField<ElementDiff> *dst,
const google::protobuf::RepeatedPtrField<Element> &old_elements,
const google::protobuf::RepeatedPtrField<Element> &new_elements) {
bool differs = false;
assert(dst != nullptr);
bool DiffWrapperBase::CompareBaseSpecifiers(
const std::vector<abi_util::CXXBaseSpecifierIR> &old_base_specifiers,
const std::vector<abi_util::CXXBaseSpecifierIR> &new_base_specifiers,
std::deque<std::string> *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 <typename T, typename TDiff>
template <typename Element, typename ElementDiff>
void DiffWrapperBase<T, TDiff>::GetExtraElementDiffs(
google::protobuf::RepeatedPtrField<ElementDiff> *dst, int i, int j,
const google::protobuf::RepeatedPtrField<Element> &old_elements,
const google::protobuf::RepeatedPtrField<Element> &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<abi_util::TemplateElementIR> &old_template_elements,
const std::vector<abi_util::TemplateElementIR> &new_template_elements,
std::deque<std::string> *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<std::string> *type_queue,
abi_util::IRDiffDumper::DiffKind diff_kind) {
auto record_type_diff_ir = std::make_unique<abi_util::RecordTypeDiffIR>();
// 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<abi_util::AccessSpecifierDiffIR>(
old_type->GetAccess(), new_type->GetAccess()));
}
if (!CompareSizeAndAlignment(old_type, new_type)) {
record_type_diff_ir->SetTypeDiff(
std::make_unique<abi_util::TypeDiffIR>(
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<abi_util::VTableLayoutDiffIR>(
old_type->GetVTableLayout(), new_type->GetVTableLayout()));
}
std::pair<std::vector<abi_util::RecordFieldDiffIR>,
std::vector<const abi_util::RecordFieldIR *>> 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<abi_util::CXXBaseSpecifierIR> &old_bases =
old_type->GetBases();
const std::vector<abi_util::CXXBaseSpecifierIR> &new_bases =
new_type->GetBases();
if (!CompareBaseSpecifiers(old_bases, new_bases, type_queue, diff_kind)) {
record_type_diff_ir->SetBaseSpecifierDiffs(
std::make_unique<abi_util::CXXBaseSpecifierDiffIR>(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<std::string> *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<std::string> *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<std::string> *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<std::string> *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<abi_util::ParamIR> &old_parameters,
const std::vector<abi_util::ParamIR> &new_parameters,
std::deque<std::string> *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<std::string> *type_queue,
abi_util::IRDiffDumper::DiffKind diff_kind) {
if (kind == abi_util::LinkableMessageKind::BuiltinTypeKind) {
return CompareBuiltinTypes(
static_cast<const abi_util::BuiltinTypeIR *>(old_type),
static_cast<const abi_util::BuiltinTypeIR *>(new_type));
}
if (kind == abi_util::LinkableMessageKind::QualifiedTypeKind) {
return CompareQualifiedTypes(
static_cast<const abi_util::QualifiedTypeIR *>(old_type),
static_cast<const abi_util::QualifiedTypeIR *>(new_type),
type_queue, diff_kind);
}
if (kind == abi_util::LinkableMessageKind::EnumTypeKind) {
return CompareEnumTypes(
static_cast<const abi_util::EnumTypeIR *>(old_type),
static_cast<const abi_util::EnumTypeIR *>(new_type),
type_queue, diff_kind);
}
if (kind == abi_util::LinkableMessageKind::LvalueReferenceTypeKind) {
return CompareLvalueReferenceTypes(
static_cast<const abi_util::LvalueReferenceTypeIR *>(old_type),
static_cast<const abi_util::LvalueReferenceTypeIR *>(new_type),
type_queue, diff_kind);
}
if (kind == abi_util::LinkableMessageKind::RvalueReferenceTypeKind) {
return CompareRvalueReferenceTypes(
static_cast<const abi_util::RvalueReferenceTypeIR *>(old_type),
static_cast<const abi_util::RvalueReferenceTypeIR *>(new_type),
type_queue, diff_kind);
}
if (kind == abi_util::LinkableMessageKind::PointerTypeKind) {
return ComparePointerTypes(
static_cast<const abi_util::PointerTypeIR *>(old_type),
static_cast<const abi_util::PointerTypeIR *>(new_type),
type_queue, diff_kind);
}
if (kind == abi_util::LinkableMessageKind::RecordTypeKind) {
return CompareRecordTypes(
static_cast<const abi_util::RecordTypeIR *>(old_type),
static_cast<const abi_util::RecordTypeIR *>(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<std::string> *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<std::string, const abi_util::TypeIR *>::const_iterator old_it =
old_types_.find(old_type_str);
std::map<std::string, const abi_util::TypeIR *>::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<abi_util::RecordTypeIR>::DumpDiff(
abi_util::IRDiffDumper::DiffKind diff_kind) {
std::deque<std::string> 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<RecordDeclDiff>
DiffWrapper<RecordDecl, RecordDeclDiff>::Get() {
std::unique_ptr<RecordDeclDiff> 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<RecordFieldDeclDiff> *fdiffs =
record_diff->mutable_field_diffs();
google::protobuf::RepeatedPtrField<CXXBaseSpecifierDiff> *bdiffs =
record_diff->mutable_base_diffs();
google::protobuf::RepeatedPtrField<CXXVTableDiff> *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<abi_util::EnumTypeIR>::DumpDiff(
abi_util::IRDiffDumper::DiffKind diff_kind) {
std::deque<std::string> 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<EnumDeclDiff>
DiffWrapper<EnumDecl, EnumDeclDiff>::Get() {
std::unique_ptr<EnumDeclDiff> enum_diff(new EnumDeclDiff());
assert(oldp_->basic_abi().linker_set_key() ==
newp_->basic_abi().linker_set_key());
google::protobuf::RepeatedPtrField<EnumFieldDeclDiff> *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<abi_util::GlobalVarIR>::DumpDiff(
abi_util::IRDiffDumper::DiffKind diff_kind) {
std::deque<std::string> 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<FunctionDeclDiff>
DiffWrapper<FunctionDecl, FunctionDeclDiff>::Get() {
std::unique_ptr<FunctionDeclDiff> func_diff(new FunctionDeclDiff());
google::protobuf::RepeatedPtrField<ParamDeclDiff> *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<abi_util::FunctionIR>::DumpDiff(
abi_util::IRDiffDumper::DiffKind diff_kind) {
std::deque<std::string> 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<GlobalVarDeclDiff>
DiffWrapper<GlobalVarDecl, GlobalVarDeclDiff>::Get() {
std::unique_ptr<GlobalVarDeclDiff> 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

View File

@@ -22,45 +22,160 @@
#include "proto/abi_diff.pb.h"
#pragma clang diagnostic pop
#include <ir_representation.h>
#include <deque>
namespace abi_diff_wrappers {
template <typename T>
template <typename T, typename F>
static bool IgnoreSymbol(const T *element,
const std::set<std::string> &ignored_symbols) {
return ignored_symbols.find(element->basic_abi().linker_set_key()) !=
const std::set<std::string> &ignored_symbols,
F func) {
return ignored_symbols.find(func(element)) !=
ignored_symbols.end();
}
template <typename T, typename TDiff>
class DiffWrapperBase {
public:
virtual std::unique_ptr<TDiff> Get() = 0 ;
protected:
DiffWrapperBase(const T *oldp, const T *newp) : oldp_(oldp), newp_(newp) { }
template <typename Element, typename ElementDiff>
bool GetElementDiffs(
google::protobuf::RepeatedPtrField<ElementDiff> *dst,
const google::protobuf::RepeatedPtrField<Element> &old_elements,
const google::protobuf::RepeatedPtrField<Element> &new_elements);
private:
template <typename Element, typename ElementDiff>
void GetExtraElementDiffs(
google::protobuf::RepeatedPtrField<ElementDiff> *dst, int i, int j,
const google::protobuf::RepeatedPtrField<Element> &old_elements,
const google::protobuf::RepeatedPtrField<Element> &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 <typename T, typename TDiff>
class DiffWrapper : public DiffWrapperBase<T, TDiff> {
class DiffWrapperBase {
public:
DiffWrapper(const T *oldp, const T *newp)
: DiffWrapperBase<T, TDiff>(oldp, newp) { }
std::unique_ptr<TDiff> 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<std::string, const abi_util::TypeIR *> &old_types,
const std::map<std::string, const abi_util::TypeIR *> &new_types,
std::set<std::string> *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<std::string> *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<std::string> *type_queue,
abi_util::IRDiffDumper::DiffKind diff_kind);
DiffStatus CompareRecordTypes(const abi_util::RecordTypeIR *old_type,
const abi_util::RecordTypeIR *new_type,
std::deque<std::string> *type_queue,
abi_util::IRDiffDumper::DiffKind diff_kind);
DiffStatus CompareQualifiedTypes(const abi_util::QualifiedTypeIR *old_type,
const abi_util::QualifiedTypeIR *new_type,
std::deque<std::string> *type_queue,
abi_util::IRDiffDumper::DiffKind diff_kind);
DiffStatus ComparePointerTypes(const abi_util::PointerTypeIR *old_type,
const abi_util::PointerTypeIR *new_type,
std::deque<std::string> *type_queue,
abi_util::IRDiffDumper::DiffKind diff_kind);
DiffStatus CompareLvalueReferenceTypes(
const abi_util::LvalueReferenceTypeIR *old_type,
const abi_util::LvalueReferenceTypeIR *new_type,
std::deque<std::string> *type_queue,
abi_util::IRDiffDumper::DiffKind diff_kind);
DiffStatus CompareRvalueReferenceTypes(
const abi_util::RvalueReferenceTypeIR *old_type,
const abi_util::RvalueReferenceTypeIR *new_type,
std::deque<std::string> *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<abi_util::EnumFieldIR> &old_fields,
const std::vector<abi_util::EnumFieldIR> &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<std::string> *type_queue,
abi_util::IRDiffDumper::DiffKind diff_kind);
std::unique_ptr<abi_util::RecordFieldDiffIR> CompareCommonRecordFields(
const abi_util::RecordFieldIR *old_field,
const abi_util::RecordFieldIR *new_field,
std::deque<std::string> *type_queue,
abi_util::IRDiffDumper::DiffKind diff_kind);
std::pair<std::vector<abi_util::RecordFieldDiffIR>,
std::vector<const abi_util::RecordFieldIR *>>
CompareRecordFields(
const std::vector<abi_util::RecordFieldIR> &old_fields,
const std::vector<abi_util::RecordFieldIR> &new_fields,
std::deque<std::string> *type_queue,
abi_util::IRDiffDumper::DiffKind diff_kind);
DiffStatus CompareFunctionParameters(
const std::vector<abi_util::ParamIR> &old_parameters,
const std::vector<abi_util::ParamIR> &new_parameters,
std::deque<std::string> *type_queue,
abi_util::IRDiffDumper::DiffKind diff_kind);
bool CompareBaseSpecifiers(
const std::vector<abi_util::CXXBaseSpecifierIR> &old_base_specifiers,
const std::vector<abi_util::CXXBaseSpecifierIR> &new_base_specifiers,
std::deque<std::string> *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<abi_util::TemplateElementIR> &old_template_elements,
const std::vector<abi_util::TemplateElementIR> &new_template_elements,
std::deque<std::string> *type_queue,
abi_util::IRDiffDumper::DiffKind diff_kind);
bool CompareSizeAndAlignment(const abi_util::TypeIR *old_ti,
const abi_util::TypeIR *new_ti);
template <typename DiffType, typename DiffElement>
bool AddToDiff(DiffType *mutable_diff, const DiffElement *oldp,
const DiffElement *newp,
std::deque<std::string> *type_queue = nullptr);
protected:
abi_util::IRDiffDumper *ir_diff_dumper_;
const std::map<std::string, const abi_util::TypeIR *> &old_types_;
const std::map<std::string, const abi_util::TypeIR *> &new_types_;
std::set<std::string> *type_cache_;
};
template <typename T>
class DiffWrapper : public DiffWrapperBase {
public:
DiffWrapper(const T *oldp, const T *newp,
abi_util::IRDiffDumper *ir_diff_dumper,
const std::map<std::string, const abi_util::TypeIR *> &old_types,
const std::map<std::string, const abi_util::TypeIR *> &new_types,
std::set<std::string> *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

View File

@@ -14,6 +14,8 @@
#include "abi_diff.h"
#include <fstream>
#include <llvm/Support/CommandLine.h>
#include <llvm/Support/FileSystem.h>
#include <llvm/Support/raw_ostream.h>
@@ -49,6 +51,12 @@ static llvm::cl::opt<bool> advice_only(
"advice-only", llvm::cl::desc("Advisory mode only"), llvm::cl::Optional,
llvm::cl::cat(header_checker_category));
static llvm::cl::opt<bool> 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<bool> 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<bool> 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<bool> 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<std::string> LoadIgnoredSymbols(std::string &symbol_list_path) {
std::ifstream symbol_ifstream(symbol_list_path);
std::set<std::string> 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;
}

View File

@@ -15,6 +15,8 @@
#ifndef ABI_WRAPPERS_H_
#define ABI_WRAPPERS_H_
#include <ir_representation.h>
#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<std::string> *type_cache,
abi_util::IRDumper *ir_dumper,
std::map<const clang::Decl *, std::string> &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<abi_util::TypeIR> 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<std::string> *type_cache_;
abi_util::IRDumper *ir_dumper_;
std::map<const clang::Decl *, std::string> &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<std::string> *type_cache,
abi_util::IRDumper *ir_dumper,
std::map<const clang::Decl *, std::string> &decl_to_source_cache_,
const std::string &previous_record_stages);
std::unique_ptr<abi_dump::RecordDecl> 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<std::string> *type_cache,
abi_util::IRDumper *ir_dumper,
std::map<const clang::Decl *, std::string> &decl_to_source_cache_);
std::unique_ptr<abi_dump::FunctionDecl> GetFunctionDecl() const;
std::unique_ptr<abi_util::FunctionIR> 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<std::string> *type_cache, abi_util::IRDumper *ir_dumper,
std::map<const clang::Decl *, std::string> &decl_to_source_cache_);
std::unique_ptr<abi_dump::EnumDecl> 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<std::string> *type_cache, abi_util::IRDumper *ir_dumper,
std::map<const clang::Decl *, std::string> &decl_to_source_cache_);
std::unique_ptr<abi_dump::GlobalVarDecl> 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_

View File

@@ -19,9 +19,6 @@
#include <clang/Tooling/Core/QualTypeNames.h>
#include <clang/Index/CodegenNameGenerator.h>
#include <google/protobuf/text_format.h>
#include <google/protobuf/io/zero_copy_stream_impl.h>
#include <fstream>
#include <iostream>
#include <string>
@@ -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 &current_file_name,
const std::set<std::string> &exported_headers,
const clang::Decl *tu_decl)
: tu_ptr_(tu_ptr),
mangle_contextp_(mangle_contextp),
const clang::Decl *tu_decl,
std::set<std::string> *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<abi_dump::RecordDecl> 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<abi_dump::EnumDecl> 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<abi_util::FunctionIR>();
*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<std::string> &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<abi_dump::FunctionDecl> 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<std::string> 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<abi_dump::GlobalVarDecl> 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<std::string> &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<clang::MangleContext> 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<std::string> type_cache;
std::unique_ptr<abi_util::IRDumper> 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);
}

View File

@@ -21,6 +21,8 @@
#include "proto/abi_dump.pb.h"
#pragma clang diagnostic pop
#include <ir_representation.h>
#include <clang/AST/AST.h>
#include <clang/AST/ASTConsumer.h>
#include <clang/AST/Mangle.h>
@@ -33,13 +35,14 @@
class HeaderASTVisitor
: public clang::RecursiveASTVisitor<HeaderASTVisitor> {
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 &current_file_name,
const std::set<std::string> &exported_headers,
const clang::Decl *tu_decl);
const clang::Decl *tu_decl,
std::set<std::string> *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<std::string> &exported_headers_;
// To optimize recursion into only exported abi.
const clang::Decl *tu_decl_;
std::set<std::string> *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<const clang::Decl *, std::string> decl_to_source_file_cache_;
};
class HeaderASTConsumer : public clang::ASTConsumer {
@@ -83,4 +90,4 @@ class HeaderASTConsumer : public clang::ASTConsumer {
std::set<std::string> exported_headers_;
};
#endif // AST_PROCESSING_H_
#endif // AST_PROCESSING_H_

View File

@@ -65,8 +65,14 @@ static llvm::cl::opt<bool> 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<bool> 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<std::string> so_file(
"so", llvm::cl::desc("<path to so file>"), llvm::cl::Optional,
"so", llvm::cl::desc("<path to so file>"), llvm::cl::Required,
llvm::cl::cat(header_linker_category));
class HeaderAbiLinker {
@@ -85,16 +91,20 @@ class HeaderAbiLinker {
bool LinkAndDump();
template <typename T>
static std::string GetLinkageName(T &element) {
return element.type_info().linker_set_key();
}
template <typename T>
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<std::string> &dump_files_;
const std::vector<std::string> &exported_header_dirs_;
@@ -120,9 +132,8 @@ class HeaderAbiLinker {
const std::string &api_;
// TODO: Add to a map of std::sets instead.
std::set<std::string> exported_headers_;
std::set<std::string> record_decl_set_;
std::set<std::string> types_set_;
std::set<std::string> function_decl_set_;
std::set<std::string> enum_decl_set_;
std::set<std::string> globvar_decl_set_;
// Version Script Regex Matching.
std::set<std::string> functions_regex_matched_set;
@@ -132,12 +143,32 @@ class HeaderAbiLinker {
std::regex globvars_vs_regex_;
};
template <typename T, typename Iterable>
static bool AddElfSymbols(google::protobuf::RepeatedPtrField<T> *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<std::string> *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 <typename T>
inline bool HeaderAbiLinker::LinkDecl(
google::protobuf::RepeatedPtrField<T> *dst,
std::set<std::string> *link_set,
google::protobuf::RepeatedPtrField<T> *dst, std::set<std::string> *link_set,
std::set<std::string> *regex_matched_link_set, const std::regex *vs_regex,
const google::protobuf::RepeatedPtrField<T> &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<std::string>::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> (
const abi_dump::FunctionDecl &element) {
return element.linker_set_key();
}
template<>
std::string HeaderAbiLinker::GetSourceFile<const abi_dump::FunctionDecl> (
const abi_dump::FunctionDecl &element) {
return element.source_file();
}
template<>
std::string HeaderAbiLinker::GetLinkageName<const abi_dump::GlobalVarDecl> (
const abi_dump::GlobalVarDecl &element) {
return element.linker_set_key();
}
template<>
std::string HeaderAbiLinker::GetSourceFile<const abi_dump::GlobalVarDecl> (
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);

View File

@@ -18,6 +18,7 @@
#include <llvm/Support/Endian.h>
#include <llvm/Support/raw_ostream.h>
#include <map>
#include <regex>
#include <set>
#include <string>
@@ -128,4 +129,60 @@ class ELFSoFileParser : public SoFileParser {
bool IsSymbolExported(const Elf_Sym *elf_sym) const;
};
template <typename T, typename K>
std::vector<T> FindRemovedElements(
const std::map<K, T> &old_elements_map,
const std::map<K, T> &new_elements_map) {
std::vector<T> 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 <typename T, typename F, typename K, typename Iterable>
inline void AddToMap(std::map<K, T> *dst, Iterable &src, F get_key) {
for (auto &&element : src) {
dst->insert(std::make_pair(get_key(&element), &element));
}
}
template <typename F, typename K, typename Iterable>
inline void AddToSet(std::set<K> *dst, Iterable &src, F get_key) {
for (auto &&element : src) {
dst->insert(get_key(element));
}
}
template <typename K, typename T>
std::vector<std::pair<T, T>> FindCommonElements(
const std::map<K, T> &old_elements_map,
const std::map<K, T> &new_elements_map) {
std::vector<std::pair<T, T>> common_elements;
typename std::map<K, T>::const_iterator old_element =
old_elements_map.begin();
typename std::map<K, T>::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

File diff suppressed because it is too large Load Diff

View File

@@ -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 <ir_representation.h>
#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 <google/protobuf/text_format.h>
#include <google/protobuf/io/zero_copy_stream_impl.h>
// 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<const RecordFieldIR *> &record_fields_removed_ir);
static bool AddRecordFieldDiffs(
abi_diff::RecordTypeDiff *record_diff_protobuf,
const std::vector<RecordFieldDiffIR> &record_field_diff_ir);
static bool AddEnumUnderlyingTypeDiff(
abi_diff::UnderlyingTypeDiff *underlying_type_diff_protobuf,
const std::pair<std::string, std::string> *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<abi_dump::TranslationUnit> tu_ptr_;
};
class ProtobufTextFormatToIRReader : public TextFormatToIRReader {
public:
virtual bool ReadDump() override;
ProtobufTextFormatToIRReader(const std::string &dump_path)
: TextFormatToIRReader(dump_path) { }
private:
std::vector<FunctionIR> ReadFunctions(
const abi_dump::TranslationUnit &tu);
std::vector<GlobalVarIR> ReadGlobalVariables(
const abi_dump::TranslationUnit &tu);
std::vector<EnumTypeIR> ReadEnumTypes(const abi_dump::TranslationUnit &tu);
std::vector<RecordTypeIR> ReadRecordTypes(
const abi_dump::TranslationUnit &tu);
std::vector<PointerTypeIR> ReadPointerTypes(
const abi_dump::TranslationUnit &tu);
std::vector<BuiltinTypeIR> ReadBuiltinTypes(
const abi_dump::TranslationUnit &tu);
std::vector<QualifiedTypeIR> ReadQualifiedTypes(
const abi_dump::TranslationUnit &tu);
std::vector<ArrayTypeIR> ReadArrayTypes(const abi_dump::TranslationUnit &tu);
std::vector<LvalueReferenceTypeIR> ReadLvalueReferenceTypes(
const abi_dump::TranslationUnit &tu);
std::vector<RvalueReferenceTypeIR> ReadRvalueReferenceTypes(
const abi_dump::TranslationUnit &tu);
std::vector<ElfFunctionIR> ReadElfFunctions (
const abi_dump::TranslationUnit &tu);
std::vector<ElfObjectIR> 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<RecordFieldIR> RecordFieldsProtobufToIR(
const google::protobuf::RepeatedPtrField<abi_dump::RecordFieldDecl> &rfp);
std::vector<CXXBaseSpecifierIR> RecordCXXBaseSpecifiersProtobufToIR(
const google::protobuf::RepeatedPtrField<abi_dump::CXXBaseSpecifier> &rbs);
std::vector<EnumFieldIR> EnumFieldsProtobufToIR(
const google::protobuf::RepeatedPtrField<abi_dump::EnumFieldDecl> &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<abi_diff::TranslationUnitDiff> diff_tu_;
};
} // abi_util
#endif // IR_PROTOBUF_

View File

@@ -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 <ir_representation.h>
#include <ir_representation_protobuf.h>
#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 <google/protobuf/text_format.h>
#include <google/protobuf/io/zero_copy_stream_impl.h>
#include <llvm/Support/raw_ostream.h>
#include <string>
#include <memory>
namespace abi_util {
std::unique_ptr<IRDumper> IRDumper::CreateIRDumper(
const std::string &type, const std::string &dump_path) {
if (type == "protobuf") {
return std::make_unique<ProtobufIRDumper>(dump_path);
}
// Nothing else is supported yet.
llvm::errs() << type << " message format is not supported yet!\n";
return nullptr;
}
std::unique_ptr<IRDiffDumper> IRDiffDumper::CreateIRDiffDumper(
const std::string &type, const std::string &dump_path) {
if (type == "protobuf") {
return std::make_unique<ProtobufIRDiffDumper>(dump_path);
}
// Nothing else is supported yet.
llvm::errs() << type << " message format is not supported yet!\n";
return nullptr;
}
std::unique_ptr<TextFormatToIRReader>
TextFormatToIRReader::CreateTextFormatToIRReader(
const std::string &type, const std::string &dump_path) {
if (type == "protobuf") {
return std::make_unique<ProtobufTextFormatToIRReader>(dump_path);
}
// Nothing else is supported yet.
llvm::errs() << type << " message format is not supported yet!\n";
return nullptr;
}
} // namespace abi_util

File diff suppressed because it is too large Load Diff

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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<float_type> {
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<typename T>
struct StackNode {
public:
@@ -63,7 +84,7 @@ public:
template<typename T>
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> float_list;
float_list float_list_test;
typedef List<int> int_list;
int_list int_list_test;
List<float>::_Node node(2);
int ListMangle(int_list *, StackNode<int> *);
template<typename IChild, typename IParent, typename BpChild, typename BpParent>
List<IChild> castInterface(List<IParent> parent, const char *childIndicator, bool emitError) {return List<IChild>();}
void format() {
castInterface<float, float, float , float>(List<float>(), "foo", true);
}
#endif // EXAMPLE1_H_

View File

@@ -22,6 +22,7 @@ struct HelloAgain {
int bar_again;
static int hello_forever;
virtual int again();
virtual ~HelloAgain() {}
};
struct NowWeCrash;
} // namespace test2