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: [ cflags: [
"-Wall", "-Wall",
"-Werror", "-Werror",
"-std=c++11",
"-DGOOGLE_PROTOBUF_NO_RTTI", "-DGOOGLE_PROTOBUF_NO_RTTI",
"-UNDEBUG" "-UNDEBUG"
], ],
cppflags: [
"-std=c++14",
],
target: { target: {
windows: { windows: {
enabled: false enabled: false
@@ -40,8 +43,8 @@ cc_defaults {
name: "header-checker-lib-defaults", name: "header-checker-lib-defaults",
static_libs: [ static_libs: [
"libclangTooling",
"libclangToolingCore", "libclangToolingCore",
"libclangTooling",
"libclangFrontendTool", "libclangFrontendTool",
"libclangFrontend", "libclangFrontend",
"libclangDriver", "libclangDriver",
@@ -134,8 +137,8 @@ cc_binary_host {
], ],
static_libs: [ static_libs: [
"libheader-checker-proto",
"libheader-abi-util", "libheader-abi-util",
"libheader-checker-proto",
], ],
target: { target: {
@@ -269,8 +272,14 @@ cc_library_static {
"libLLVMMCParser", "libLLVMMCParser",
"libLLVMCore", "libLLVMCore",
"libLLVMSupport", "libLLVMSupport",
"libheader-checker-proto",
], ],
shared_libs: [
"libprotobuf-cpp-full",
],
cflags: [ cflags: [
"-Wcast-qual", "-Wcast-qual",
"-Wno-long-long", "-Wno-long-long",

View File

@@ -7,6 +7,7 @@
## Usage ## Usage
header-abi-dumper -o <dump-file> <source_file> -I <export-include-dir-1> -I header-abi-dumper -o <dump-file> <source_file> -I <export-include-dir-1> -I
<export-include-dir-2>.. -- <cflags> <export-include-dir-2>.. -- <cflags>
For options : header-abi-dumper --help
# VNDK Header Abi Linker # VNDK Header Abi Linker
@@ -16,6 +17,7 @@
## Usage ## Usage
header-abi-linker -o <linked-abi-dump> <abi-dump1> <abi-dump2> <abi-dump3> ... header-abi-linker -o <linked-abi-dump> <abi-dump1> <abi-dump2> <abi-dump3> ...
For options : header-abi-linker --help
# VNDK Header Abi Diff # VNDK Header Abi Diff
@@ -24,10 +26,13 @@
abi's exposed by the two dumps. abi's exposed by the two dumps.
# Return Value # Return Value
1: InCompatible 0: Compatible
0: Compatible or Compatible Extension. 1: Changes to APIs unreferenced by symbols in the .dynsym table
4: Compatible Extension
8: Incompatible
## Usage ## Usage
header-abi-diff -old <old-abi-dump> -new <new-abi-dump> -o <report> 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 "abi_diff.h"
#include <header_abi_util.h>
#include <llvm/Support/raw_ostream.h> #include <llvm/Support/raw_ostream.h>
#include <google/protobuf/text_format.h> #include <google/protobuf/text_format.h>
#include <google/protobuf/io/zero_copy_stream_impl.h> #include <google/protobuf/io/zero_copy_stream_impl.h>
#include <memory> #include <memory>
#include <fstream>
#include <iostream>
#include <string> #include <string>
#include <vector> #include <vector>
#include <stdlib.h> #include <stdlib.h>
CompatibilityStatus HeaderAbiDiff::GenerateCompatibilityReport() { abi_util::CompatibilityStatusIR HeaderAbiDiff::GenerateCompatibilityReport() {
abi_dump::TranslationUnit old_tu; using abi_util::TextFormatToIRReader;
abi_dump::TranslationUnit new_tu; std::unique_ptr<abi_util::TextFormatToIRReader> old_reader =
std::ifstream old_input(old_dump_); TextFormatToIRReader::CreateTextFormatToIRReader("protobuf", old_dump_);
std::ifstream new_input(new_dump_); std::unique_ptr<abi_util::TextFormatToIRReader> new_reader =
google::protobuf::io::IstreamInputStream text_iso(&old_input); TextFormatToIRReader::CreateTextFormatToIRReader("protobuf", new_dump_);
google::protobuf::io::IstreamInputStream text_isn(&new_input); if (!old_reader || !new_reader || !old_reader->ReadDump() ||
!new_reader->ReadDump()) {
if (!google::protobuf::TextFormat::Parse(&text_iso, &old_tu) || llvm::errs() << "Could not create Text Format readers\n";
!google::protobuf::TextFormat::Parse(&text_isn, &new_tu)) {
llvm::errs() << "Failed to generate compatibility report\n";
::exit(1); ::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 =
CompatibilityStatus HeaderAbiDiff::CompareTUs( CompareTUs(old_reader.get(), new_reader.get(), ir_diff_dumper.get());
const abi_dump::TranslationUnit &old_tu, if (!ir_diff_dumper->Dump()) {
const abi_dump::TranslationUnit &new_tu) { llvm::errs() << "Could not dump diff report\n";
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_);
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_);
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";
::exit(1); ::exit(1);
} }
return status;
}
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);
}
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();});
// 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; return combined_status;
} }
template <typename T, typename TDiff> bool HeaderAbiDiff::CollectUserDefinedTypes(
abi_diff::CompatibilityStatus HeaderAbiDiff::Collect( const abi_util::TextFormatToIRReader *old_tu,
google::protobuf::RepeatedPtrField<T> *elements_added, const abi_util::TextFormatToIRReader *new_tu,
google::protobuf::RepeatedPtrField<T> *elements_removed, const std::map<std::string, const abi_util::TypeIR *> &old_types_map,
google::protobuf::RepeatedPtrField<TDiff> *elements_diff, const std::map<std::string, const abi_util::TypeIR *> &new_types_map,
const google::protobuf::RepeatedPtrField<T> &old_srcs, abi_util::IRDiffDumper *ir_diff_dumper) {
const google::protobuf::RepeatedPtrField<T> &new_srcs, return CollectUserDefinedTypesInternal(
const std::set<std::string> &ignored_symbols) { old_tu->GetRecordTypes(), new_tu->GetRecordTypes(), old_types_map,
assert(elements_added != nullptr); new_types_map, ir_diff_dumper) &&
assert(elements_removed != nullptr); CollectUserDefinedTypesInternal(old_tu->GetEnumTypes(),
assert(elements_diff != nullptr); new_tu->GetEnumTypes(), old_types_map,
new_types_map, ir_diff_dumper);
}
std::map<std::string, const T*> old_elements_map; template <typename T>
std::map<std::string, const T*> new_elements_map; bool HeaderAbiDiff::CollectUserDefinedTypesInternal(
AddToMap(&old_elements_map, old_srcs); const std::vector<T> &old_ud_types,
AddToMap(&new_elements_map, new_srcs); 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, abi_util::AddToMap(&old_ud_types_map, old_ud_types,
new_elements_map, ignored_symbols) || [](const T *e)
!PopulateRemovedElements(elements_added, new_elements_map, { return e->GetLinkerSetKey();});
old_elements_map, ignored_symbols) ||
!PopulateCommonElements(elements_diff, old_elements_map, abi_util::AddToMap(&new_ud_types_map, new_ud_types,
new_elements_map, ignored_symbols)) { [](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"; llvm::errs() << "Populating functions in report failed\n";
::exit(1); return false;
} }
if (elements_diff->size() || elements_removed->size()) { return true;
return CompatibilityStatus::INCOMPATIBLE;
} }
if (elements_added->size()) {
return CompatibilityStatus::EXTENSION; 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);
} }
return CompatibilityStatus::COMPATIBLE;
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;
}
}
return true;
} }
template <typename T> template <typename T>
bool HeaderAbiDiff::PopulateRemovedElements( 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*> &old_elements_map,
const std::map<std::string, const T*> &new_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,
std::vector<const T *> removed_elements; abi_util::IRDiffDumper::DiffKind diff_kind) {
for (auto &&map_element : old_elements_map) { std::vector<const T *> removed_elements =
const T *element = map_element.second; abi_util::FindRemovedElements(old_elements_map, new_elements_map);
auto new_element = if (!DumpLoneElements(removed_elements, elf_map, ir_diff_dumper, diff_kind)) {
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)) {
llvm::errs() << "Dumping added / removed element to report failed\n"; llvm::errs() << "Dumping added / removed element to report failed\n";
return false; return false;
} }
return true; return true;
} }
template <typename T, typename TDiff> template <typename T>
bool HeaderAbiDiff::PopulateCommonElements( 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 *> &old_elements_map,
const std::map<std::string, const T *> &new_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,
std::vector<std::pair<const T *, const T *>> common_elements; const std::map<std::string, const abi_util::TypeIR *> &new_types,
typename std::map<std::string, const T *>::const_iterator old_element = abi_util::IRDiffDumper *ir_diff_dumper,
old_elements_map.begin(); abi_util::IRDiffDumper::DiffKind diff_kind) {
typename std::map<std::string, const T *>::const_iterator new_element = std::vector<std::pair<const T *, const T *>> common_elements =
new_elements_map.begin(); abi_util::FindCommonElements(old_elements_map, new_elements_map);
while (old_element != old_elements_map.end() && if (!DumpDiffElements(common_elements, old_types, new_types,
new_element != new_elements_map.end()) { ir_diff_dumper, diff_kind)) {
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)) {
llvm::errs() << "Dumping difference in common element to report failed\n"; llvm::errs() << "Dumping difference in common element to report failed\n";
return false; return false;
} }
@@ -185,48 +278,62 @@ bool HeaderAbiDiff::PopulateCommonElements(
template <typename T> template <typename T>
bool HeaderAbiDiff::DumpLoneElements( bool HeaderAbiDiff::DumpLoneElements(
google::protobuf::RepeatedPtrField<T> *dst,
std::vector<const T *> &elements, 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) { 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; continue;
} }
T *added_element = dst->Add(); // The element does exist in the .dynsym table, we do not have meta-data
if (!added_element) { // surrounding the element.
llvm::errs() << "Adding element diff failed\n"; 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; return false;
} }
*added_element = *element;
} }
return true; return true;
} }
template <typename T, typename TDiff>
template <typename T>
bool HeaderAbiDiff::DumpDiffElements( bool HeaderAbiDiff::DumpDiffElements(
google::protobuf::RepeatedPtrField<TDiff> *dst,
std::vector<std::pair<const T *,const T *>> &pairs, 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) { for (auto &&pair : pairs) {
const T *old_element = pair.first; const T *old_element = pair.first;
const T *new_element = pair.second; const T *new_element = pair.second;
// Not having inheritance from protobuf messages makes this
// restrictive code. if (abi_diff_wrappers::IgnoreSymbol<T>(
if (abi_diff_wrappers::IgnoreSymbol<T>(old_element, ignored_symbols)) { old_element, ignored_symbols_,
[](const T *e) {return e->GetLinkerSetKey();})) {
continue; continue;
} }
abi_diff_wrappers::DiffWrapper<T, TDiff> diff_wrapper(old_element, abi_diff_wrappers::DiffWrapper<T> diff_wrapper(old_element, new_element,
new_element); ir_diff_dumper, old_types,
std::unique_ptr<TDiff> decl_diff_ptr = diff_wrapper.Get(); new_types, &type_cache_);
if (!decl_diff_ptr) { if (!diff_wrapper.DumpDiff(diff_kind)) {
continue; llvm::errs() << "Failed to diff elements\n";
}
TDiff *added_element_diff = dst->Add();
if (!added_element_diff) {
llvm::errs() << "Adding element diff failed\n";
return false; return false;
} }
*added_element_diff = *decl_diff_ptr;
} }
return true; return true;
} }

View File

@@ -14,6 +14,8 @@
#include "abi_diff_wrappers.h" #include "abi_diff_wrappers.h"
#include <ir_representation.h>
#pragma clang diagnostic push #pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-parameter" #pragma clang diagnostic ignored "-Wunused-parameter"
#pragma clang diagnostic ignored "-Wnested-anon-types" #pragma clang diagnostic ignored "-Wnested-anon-types"
@@ -21,68 +23,102 @@
#include "proto/abi_diff.pb.h" #include "proto/abi_diff.pb.h"
#pragma clang diagnostic pop #pragma clang diagnostic pop
#include <memory>
#include <fstream>
#include <iostream>
#include <string> #include <string>
#include <vector> #include <vector>
typedef abi_diff::CompatibilityStatus CompatibilityStatus;
class HeaderAbiDiff { class HeaderAbiDiff {
public: public:
HeaderAbiDiff(const std::string &lib_name, const std::string &arch, HeaderAbiDiff(const std::string &lib_name, const std::string &arch,
const std::string &old_dump, const std::string &new_dump, const std::string &old_dump, const std::string &new_dump,
const std::string &compatibility_report, 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), : lib_name_(lib_name), arch_(arch), old_dump_(old_dump),
new_dump_(new_dump), cr_(compatibility_report), 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: private:
CompatibilityStatus CompareTUs(const abi_dump::TranslationUnit &old_tu, abi_util::CompatibilityStatusIR CompareTUs(
const abi_dump::TranslationUnit &new_tu); const abi_util::TextFormatToIRReader *old_tu,
// Collect* methods fill in the diff_tu. const abi_util::TextFormatToIRReader *new_tu,
template <typename T, typename TDiff> abi_util::IRDiffDumper *ir_diff_dumper);
static CompatibilityStatus Collect(
google::protobuf::RepeatedPtrField<T> *elements_added, template <typename T, typename ElfSymbolType>
google::protobuf::RepeatedPtrField<T> *elements_removed, bool CollectDynsymExportables(
google::protobuf::RepeatedPtrField<TDiff> *elements_diff, const std::vector<T> &old_exportables,
const google::protobuf::RepeatedPtrField<T> &old_srcs, const std::vector<T> &new_exportables,
const google::protobuf::RepeatedPtrField<T> &new_srcs, const std::vector<ElfSymbolType> &old_elf_symbols,
const std::set<std::string> &ignored_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> template <typename T>
static inline void AddToMap(std::map<std::string, const T *> *dst, bool Collect(
const google::protobuf::RepeatedPtrField<T> &src); 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> template <typename T>
static bool PopulateRemovedElements( bool PopulateRemovedElements(
google::protobuf::RepeatedPtrField<T> *dst,
const std::map<std::string, const T *> &old_elements_map, 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 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> template <typename T>
static bool PopulateCommonElements( bool PopulateCommonElements(
google::protobuf::RepeatedPtrField<TDiff> *dst,
const std::map<std::string, const T *> &old_elements_map, 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 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> template <typename T>
static bool DumpDiffElements( bool DumpDiffElements(
google::protobuf::RepeatedPtrField<TDiff> *dst,
std::vector<std::pair<const T *, const T *>> &pairs, 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> template <typename T>
static bool DumpLoneElements(google::protobuf::RepeatedPtrField<T> *dst, bool DumpLoneElements(
std::vector<const T *> &elements, 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);
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: private:
const std::string &lib_name_; const std::string &lib_name_;
@@ -91,27 +127,6 @@ class HeaderAbiDiff {
const std::string &new_dump_; const std::string &new_dump_;
const std::string &cr_; const std::string &cr_;
const std::set<std::string> &ignored_symbols_; 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

@@ -16,50 +16,21 @@
#include <header_abi_util.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 <llvm/Support/raw_ostream.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 { namespace abi_diff_wrappers {
static bool IsAccessDownGraded(abi_dump::AccessSpecifier old_access, static bool IsAccessDownGraded(abi_util::AccessSpecifierIR old_access,
abi_dump::AccessSpecifier new_access) { abi_util::AccessSpecifierIR new_access) {
bool access_downgraded = false; bool access_downgraded = false;
switch (old_access) { switch (old_access) {
case abi_dump::AccessSpecifier::protected_access: case abi_util::AccessSpecifierIR::ProtectedAccess:
if (new_access == abi_dump::AccessSpecifier::private_access) { if (new_access == abi_util::AccessSpecifierIR::PrivateAccess) {
access_downgraded = true; access_downgraded = true;
} }
break; break;
case abi_dump::AccessSpecifier::public_access: case abi_util::AccessSpecifierIR::PublicAccess:
if (new_access != abi_dump::AccessSpecifier::public_access) { if (new_access != abi_util::AccessSpecifierIR::PublicAccess) {
access_downgraded = true; access_downgraded = true;
} }
break; break;
@@ -69,255 +40,590 @@ static bool IsAccessDownGraded(abi_dump::AccessSpecifier old_access,
return access_downgraded; return access_downgraded;
} }
static std::string CpptoCAdjustment(const std::string &type) { static std::string Unwind(const std::deque<std::string> *type_queue) {
std::string adjusted_type_name = if (!type_queue) {
abi_util::FindAndReplace(type, "\\bstruct ", ""); return "";
}
return adjusted_type_name; 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, void DiffWrapperBase::CompareEnumFields(
const abi_dump::BasicTypeAbi &new_abi) { const std::vector<abi_util::EnumFieldIR> &old_fields,
// Strip of leading 'struct' keyword from type names const std::vector<abi_util::EnumFieldIR> &new_fields,
std::string old_type = old_abi.name(); abi_util::EnumTypeDiffIR *enum_type_diff_ir) {
std::string new_type = new_abi.name(); std::map<std::string, const abi_util::EnumFieldIR *> old_fields_map;
old_type = CpptoCAdjustment(old_type); std::map<std::string, const abi_util::EnumFieldIR *> new_fields_map;
new_type = CpptoCAdjustment(new_type); abi_util::AddToMap(&old_fields_map, old_fields,
// TODO: Add checks for C++ built-in types vs C corresponding types. [](const abi_util::EnumFieldIR *f)
return old_type != new_type; {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, DiffStatus DiffWrapperBase::CompareEnumTypes(
const abi_dump::BasicTypeAbi &new_abi) { const abi_util::EnumTypeIR *old_type, const abi_util::EnumTypeIR *new_type,
// We need to add a layer of indirection to account for issues when C and C++ std::deque<std::string> *type_queue,
// are mixed. For example some types like wchar_t are in-built types for C++ abi_util::IRDiffDumper::DiffKind diff_kind) {
// but not for C. Another example would be clang reporting C structures if (old_type->GetName() != new_type->GetName()) {
// without the leading "struct" keyword when headers defining them are return DiffStatus::direct_diff;
// 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;
} }
auto enum_type_diff_ir = std::make_unique<abi_util::EnumTypeDiffIR>();
template <typename T> enum_type_diff_ir->SetName(old_type->GetName());
static bool Diff(const T &old_element, const T &new_element) { const std::string &old_underlying_type = old_type->GetUnderlyingType();
// Can be specialized for future changes in the format. const std::string &new_underlying_type = new_type->GetUnderlyingType();
return DiffBasicTypeAbi(old_element.basic_abi().type_abi(), if (old_underlying_type != new_underlying_type) {
new_element.basic_abi().type_abi()) || enum_type_diff_ir->SetUnderlyingTypeDiff(
(old_element.basic_abi().name() != new_element.basic_abi().name()) || std::make_unique<std::pair<std::string, std::string>>(
IsAccessDownGraded(old_element.basic_abi().access(), old_underlying_type, new_underlying_type));
new_element.basic_abi().access());
} }
CompareEnumFields(old_type->GetFields(), new_type->GetFields(),
template <> enum_type_diff_ir.get());
bool Diff<EnumFieldDecl>(const EnumFieldDecl &old_element, if ((enum_type_diff_ir->IsExtended() ||
const EnumFieldDecl &new_element) { enum_type_diff_ir->IsIncompatible()) &&
// Can be specialized for future changes in the format. !ir_diff_dumper_->AddDiffMessageIR(enum_type_diff_ir.get(),
return DiffBasicTypeAbi(old_element.basic_abi().type_abi(), Unwind(type_queue), diff_kind)) {
new_element.basic_abi().type_abi()) || llvm::errs() << "AddDiffMessage on EnumTypeDiffIR failed\n";
(old_element.enum_field_value() != new_element.enum_field_value()) ||
(old_element.basic_abi().name() != new_element.basic_abi().name());
}
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());
}
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());
}
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;
}
// 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);
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); ::exit(1);
} }
*old_elementp = old_element; return DiffStatus::no_diff;
*new_elementp = new_element;
diff->set_index(i);
differs = true;
}
i++;
j++;
}
if (old_elements.size() != new_elements.size()) {
GetExtraElementDiffs(dst, i, j, old_elements, new_elements);
differs = true;
}
return differs;
} }
template <typename T, typename TDiff> bool DiffWrapperBase::CompareVTableComponents(
template <typename Element, typename ElementDiff> const abi_util::VTableComponentIR &old_component,
void DiffWrapperBase<T, TDiff>::GetExtraElementDiffs( const abi_util::VTableComponentIR &new_component) {
google::protobuf::RepeatedPtrField<ElementDiff> *dst, int i, int j, return old_component.GetName() == new_component.GetName() &&
const google::protobuf::RepeatedPtrField<Element> &old_elements, old_component.GetValue() == new_component.GetValue() &&
const google::protobuf::RepeatedPtrField<Element> &new_elements) { old_component.GetKind() == new_component.GetKind();
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);
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, bool DiffWrapperBase::CompareVTables(
BasicNamedAndTypedDecl *type_diff_new, const abi_util::RecordTypeIR *old_record,
const BasicNamedAndTypedDecl &old, const abi_util::RecordTypeIR *new_record) {
const BasicNamedAndTypedDecl &new_) {
assert(type_diff_old != nullptr); const std::vector<abi_util::VTableComponentIR> &old_components =
assert(type_diff_new != nullptr); old_record->GetVTableLayout().GetVTableComponents();
if (DiffBasicTypeAbi(old.type_abi(), new_.type_abi()) || const std::vector<abi_util::VTableComponentIR> &new_components =
IsAccessDownGraded(old.access(), new_.access())) { new_record->GetVTableLayout().GetVTableComponents();
*(type_diff_old) = old; if (old_components.size() > new_components.size()) {
*(type_diff_new) = new_; // Something in the vtable got deleted.
return true;
}
return false; 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 DiffWrapperBase::CompareSizeAndAlignment(
std::unique_ptr<RecordDeclDiff> const abi_util::TypeIR *old_type,
DiffWrapper<RecordDecl, RecordDeclDiff>::Get() { const abi_util::TypeIR *new_type) {
std::unique_ptr<RecordDeclDiff> record_diff(new RecordDeclDiff()); return old_type->GetSize() == new_type->GetSize() &&
assert(oldp_->basic_abi().linker_set_key() == old_type->GetAlignment() == new_type->GetAlignment();
newp_->basic_abi().linker_set_key()); }
record_diff->set_name(oldp_->basic_abi().name());
google::protobuf::RepeatedPtrField<RecordFieldDeclDiff> *fdiffs = std::unique_ptr<abi_util::RecordFieldDiffIR>
record_diff->mutable_field_diffs(); DiffWrapperBase::CompareCommonRecordFields(
google::protobuf::RepeatedPtrField<CXXBaseSpecifierDiff> *bdiffs = const abi_util::RecordFieldIR *old_field,
record_diff->mutable_base_diffs(); const abi_util::RecordFieldIR *new_field,
google::protobuf::RepeatedPtrField<CXXVTableDiff> *vtdiffs = std::deque<std::string> *type_queue,
record_diff->mutable_vtable_diffs(); abi_util::IRDiffDumper::DiffKind diff_kind) {
assert(fdiffs != nullptr && bdiffs != nullptr); if (old_field->GetOffset() != new_field->GetOffset() ||
// Template Information isn't diffed since the linker_set_key includes the // TODO: Should this be an inquality check instead ? Some compilers can
// mangled name which includes template information. // make signatures dependant on absolute values of access specifiers.
if (GetElementDiffs(fdiffs, oldp_->fields(), newp_->fields()) || IsAccessDownGraded(old_field->GetAccess(), new_field->GetAccess()) ||
GetElementDiffs(bdiffs, oldp_->base_specifiers(), CompareAndDumpTypeDiff(old_field->GetReferencedType(),
newp_->base_specifiers()) || new_field->GetReferencedType(),
GetElementDiffs(vtdiffs, oldp_->vtable_layout().vtable_components(), type_queue, diff_kind) ==
newp_->vtable_layout().vtable_components()) || DiffStatus::direct_diff) {
DiffBasicNamedAndTypedDecl( return std::make_unique<abi_util::RecordFieldDiffIR>(old_field, new_field);
record_diff->mutable_type_diff()->mutable_old(),
record_diff->mutable_type_diff()->mutable_new_(),
oldp_->basic_abi(), newp_->basic_abi())) {
return record_diff;
} }
return nullptr; return nullptr;
} }
template <> std::pair<std::vector<abi_util::RecordFieldDiffIR>,
std::unique_ptr<EnumDeclDiff> std::vector<const abi_util::RecordFieldIR *>>
DiffWrapper<EnumDecl, EnumDeclDiff>::Get() { DiffWrapperBase::CompareRecordFields(
std::unique_ptr<EnumDeclDiff> enum_diff(new EnumDeclDiff()); const std::vector<abi_util::RecordFieldIR> &old_fields,
assert(oldp_->basic_abi().linker_set_key() == const std::vector<abi_util::RecordFieldIR> &new_fields,
newp_->basic_abi().linker_set_key()); std::deque<std::string> *type_queue,
google::protobuf::RepeatedPtrField<EnumFieldDeclDiff> *fdiffs = abi_util::IRDiffDumper::DiffKind diff_kind) {
enum_diff->mutable_field_diffs(); std::pair<std::vector<abi_util::RecordFieldDiffIR>,
assert(fdiffs != nullptr); std::vector<const abi_util::RecordFieldIR *>> diffed_and_removed_fields;
enum_diff->set_name(oldp_->basic_abi().name()); std::map<std::string, const abi_util::RecordFieldIR *> old_fields_map;
if (GetElementDiffs(fdiffs, oldp_->enum_fields(), newp_->enum_fields()) || std::map<std::string, const abi_util::RecordFieldIR *> new_fields_map;
DiffBasicNamedAndTypedDecl( std::map<uint64_t, const abi_util::RecordFieldIR *> old_fields_offset_map;
enum_diff->mutable_type_diff()->mutable_old(), std::map<uint64_t, const abi_util::RecordFieldIR *> new_fields_offset_map;
enum_diff->mutable_type_diff()->mutable_new_(),
oldp_->basic_abi(), newp_->basic_abi())) { abi_util::AddToMap(&old_fields_map, old_fields,
return enum_diff; [](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;
} }
return nullptr; 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;
}
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;
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++;
}
return true;
}
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++;
}
}
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 <> template <>
std::unique_ptr<FunctionDeclDiff> bool DiffWrapper<abi_util::RecordTypeIR>::DumpDiff(
DiffWrapper<FunctionDecl, FunctionDeclDiff>::Get() { abi_util::IRDiffDumper::DiffKind diff_kind) {
std::unique_ptr<FunctionDeclDiff> func_diff(new FunctionDeclDiff()); std::deque<std::string> type_queue;
google::protobuf::RepeatedPtrField<ParamDeclDiff> *pdiffs = if (oldp_->GetName() != newp_->GetName()) {
func_diff->mutable_param_diffs(); llvm::errs() << "Comparing two different unreferenced records\n";
assert(func_diff->mutable_return_type_diffs() != nullptr); return false;
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; if (!type_cache_->insert(oldp_->GetName()).second) {
return true;
}
CompareRecordTypes(oldp_, newp_, &type_queue, diff_kind);
return true;
} }
template <> template <>
std::unique_ptr<GlobalVarDeclDiff> bool DiffWrapper<abi_util::EnumTypeIR>::DumpDiff(
DiffWrapper<GlobalVarDecl, GlobalVarDeclDiff>::Get() { abi_util::IRDiffDumper::DiffKind diff_kind) {
std::unique_ptr<GlobalVarDeclDiff> global_var_diff(new GlobalVarDeclDiff()); std::deque<std::string> type_queue;
assert(global_var_diff->mutable_type_diff() != nullptr); if (oldp_->GetName() != newp_->GetName()) {
if (DiffBasicNamedAndTypedDecl( llvm::errs() << "Comparing two different unreferenced enums\n";
global_var_diff->mutable_type_diff()->mutable_old(), return false;
global_var_diff->mutable_type_diff()->mutable_new_(),
oldp_->basic_abi(), newp_->basic_abi())) {
return global_var_diff;
} }
return nullptr; if (!type_cache_->insert(oldp_->GetName()).second) {
return true;
}
CompareEnumTypes(oldp_, newp_, &type_queue, diff_kind);
return true;
} }
template <>
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 true;
}
template <>
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);
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 true;
}
} // abi_diff_wrappers } // abi_diff_wrappers

View File

@@ -22,45 +22,160 @@
#include "proto/abi_diff.pb.h" #include "proto/abi_diff.pb.h"
#pragma clang diagnostic pop #pragma clang diagnostic pop
#include <ir_representation.h>
#include <deque>
namespace abi_diff_wrappers { namespace abi_diff_wrappers {
template <typename T> template <typename T, typename F>
static bool IgnoreSymbol(const T *element, static bool IgnoreSymbol(const T *element,
const std::set<std::string> &ignored_symbols) { const std::set<std::string> &ignored_symbols,
return ignored_symbols.find(element->basic_abi().linker_set_key()) != F func) {
return ignored_symbols.find(func(element)) !=
ignored_symbols.end(); ignored_symbols.end();
} }
template <typename T, typename TDiff> enum DiffStatus {
class DiffWrapperBase { // Previous stages of CompareAndTypeDiff should not dump the diff.
public: no_diff = 0,
virtual std::unique_ptr<TDiff> Get() = 0 ; // Previous stages of CompareAndTypeDiff should dump the diff if required.
protected: direct_diff = 1,
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_;
}; };
template <typename T, typename TDiff> class DiffWrapperBase {
class DiffWrapper : public DiffWrapperBase<T, TDiff> {
public: public:
DiffWrapper(const T *oldp, const T *newp) virtual bool DumpDiff(abi_util::IRDiffDumper::DiffKind diff_kind) = 0;
: DiffWrapperBase<T, TDiff>(oldp, newp) { } virtual ~DiffWrapperBase() { }
std::unique_ptr<TDiff> Get() override; 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 } // abi_diff_wrappers

View File

@@ -14,6 +14,8 @@
#include "abi_diff.h" #include "abi_diff.h"
#include <fstream>
#include <llvm/Support/CommandLine.h> #include <llvm/Support/CommandLine.h>
#include <llvm/Support/FileSystem.h> #include <llvm/Support/FileSystem.h>
#include <llvm/Support/raw_ostream.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, "advice-only", llvm::cl::desc("Advisory mode only"), llvm::cl::Optional,
llvm::cl::cat(header_checker_category)); 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( static llvm::cl::opt<bool> suppress_local_warnings(
"suppress_local_warnings", llvm::cl::desc("suppress local warnings"), "suppress_local_warnings", llvm::cl::desc("suppress local warnings"),
llvm::cl::Optional, llvm::cl::cat(header_checker_category)); 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::desc("Do not return a non zero status on extensions"),
llvm::cl::Optional, llvm::cl::cat(header_checker_category)); 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) { static std::set<std::string> LoadIgnoredSymbols(std::string &symbol_list_path) {
std::ifstream symbol_ifstream(symbol_list_path); std::ifstream symbol_ifstream(symbol_list_path);
std::set<std::string> ignored_symbols; std::set<std::string> ignored_symbols;
@@ -80,38 +95,50 @@ int main(int argc, const char **argv) {
ignored_symbols = LoadIgnoredSymbols(ignore_symbol_list); ignored_symbols = LoadIgnoredSymbols(ignore_symbol_list);
} }
HeaderAbiDiff judge(lib_name, arch, old_dump, new_dump, compatibility_report, 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 = ""; std::string status_str = "";
switch (status) { std::string unreferenced_change_str = "";
case CompatibilityStatus::INCOMPATIBLE: std::string error_or_warning_str = "\033[36;1mwarning: \033[0m";
status_str = "broken";
break;
case CompatibilityStatus::EXTENSION:
status_str = "extended";
break;
default:
break;
}
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) { if (!suppress_local_warnings && status) {
llvm::errs() << "******************************************************\n" llvm::errs() << "******************************************************\n"
<< "VNDK Abi " << error_or_warning_str
<< "VNDK library: "
<< lib_name
<< "'s ABI has "
<< status_str << status_str
<< ":" << unreferenced_change_str
<< " Please check compatiblity report at : " << " Please check compatiblity report at : "
<< compatibility_report << "\n" << 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) { if (advice_only) {
return CompatibilityStatus::COMPATIBLE; return abi_util::CompatibilityStatusIR::Compatible;
} }
if (allow_extensions && status == CompatibilityStatus::EXTENSION) {
return CompatibilityStatus::COMPATIBLE;
}
return status; return status;
} }

View File

@@ -28,10 +28,26 @@ using namespace abi_wrapper;
ABIWrapper::ABIWrapper( ABIWrapper::ABIWrapper(
clang::MangleContext *mangle_contextp, clang::MangleContext *mangle_contextp,
clang::ASTContext *ast_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)
: cip_(cip), : cip_(cip),
mangle_contextp_(mangle_contextp), mangle_contextp_(mangle_contextp),
ast_contextp_(ast_contextp) { } ast_contextp_(ast_contextp),
type_cache_(type_cache),
ir_dumper_(ir_dumper),
decl_to_source_file_cache_(decl_to_source_cache) { }
std::string ABIWrapper::GetCachedDeclSourceFile(
const clang::Decl *decl, const clang::CompilerInstance *cip) {
assert(decl != nullptr);
auto result = decl_to_source_file_cache_.find(decl);
if (result == decl_to_source_file_cache_.end()) {
return GetDeclSourceFile(decl, cip);
}
return result->second;
}
std::string ABIWrapper::GetDeclSourceFile(const clang::Decl *decl, std::string ABIWrapper::GetDeclSourceFile(const clang::Decl *decl,
const clang::CompilerInstance *cip) { const clang::CompilerInstance *cip) {
@@ -53,85 +69,174 @@ std::string ABIWrapper::GetDeclSourceFile(const clang::Decl *decl,
return file_abs_path; return file_abs_path;
} }
abi_dump::AccessSpecifier ABIWrapper::AccessClangToDump( static abi_util::AccessSpecifierIR AccessClangToIR(
const clang::AccessSpecifier sp) const { const clang::AccessSpecifier sp) {
switch (sp) { switch (sp) {
case clang::AS_private: { case clang::AS_private: {
return abi_dump::AccessSpecifier::private_access; return abi_util::AccessSpecifierIR::PrivateAccess;
break; break;
} }
case clang::AS_protected: { case clang::AS_protected: {
return abi_dump::AccessSpecifier::protected_access; return abi_util::AccessSpecifierIR::ProtectedAccess;
break; break;
} }
default: { default: {
return abi_dump::AccessSpecifier::public_access; return abi_util::AccessSpecifierIR::PublicAccess;
break; break;
} }
} }
} }
// Dumping the size and alignment is optional. This is since clang can lazily // Get type 'referenced' by qual_type. Referenced type implies, in order:
// instantiate records as incomplete and therefore their sizes 'may' not be // 1) Strip off all qualifiers if qual_type has CVR qualifiers.
// computable. b/62307940 // 2) Strip off a pointer level if qual_type is a pointer.
bool ABIWrapper::SetupBasicTypeAbi(abi_dump::BasicTypeAbi *type_abi, // 3) Strip off the reference if qual_type is a reference.
const clang::QualType type, // Note: qual_type is expected to be a canonical type.
bool dump_size) const { clang::QualType ABIWrapper::GetReferencedType(const clang::QualType qual_type) {
if (!type_abi) { const clang::Type *type_ptr = qual_type.getTypePtr();
if (qual_type.hasLocalQualifiers()) {
return qual_type.getLocalUnqualifiedType();
}
if (type_ptr->isPointerType()) {
return type_ptr->getPointeeType();
}
if (type_ptr->isArrayType()) {
return
type_ptr->getArrayElementTypeNoTypeQual()->getCanonicalTypeInternal();
}
return qual_type.getNonReferenceType();
}
bool ABIWrapper::CreateExtendedType(
clang::QualType qual_type,
abi_util::TypeIR *typep) {
std::string type_name = QualTypeToString(qual_type);
if (!type_cache_->insert(type_name).second) {
return true;
}
const clang::QualType canonical_type = qual_type.getCanonicalType();
return CreateBasicNamedAndTypedDecl(canonical_type, typep, "");
}
//This overload takes in a qualtype and adds its information to the abi-dump on
//its own.
bool ABIWrapper::CreateBasicNamedAndTypedDecl(clang::QualType qual_type,
const std::string &source_file) {
std::string type_name = QualTypeToString(qual_type);
const clang::QualType canonical_type = qual_type.getCanonicalType();
const clang::Type *base_type = canonical_type.getTypePtr();
bool is_ptr = base_type->isPointerType();
bool is_reference = base_type->isReferenceType();
bool is_array = base_type->isArrayType();
bool is_builtin = base_type->isBuiltinType();
bool has_referenced_type =
is_ptr || is_reference || is_array ||
canonical_type.hasLocalQualifiers() || is_builtin;
if (!has_referenced_type || !type_cache_->insert(type_name).second) {
return true;
}
// Do something similar to what is being done right now. Create an object
// extending Type and return a pointer to that and pass it to CreateBasic...
// CreateBasic...(qualtype, Type *) fills in size, alignemnt etc.
std::unique_ptr<abi_util::TypeIR> typep = SetTypeKind(canonical_type,
source_file);
if (!base_type->isVoidType() && !typep) {
return false; return false;
} }
const clang::QualType canonical_type = type.getCanonicalType(); return CreateBasicNamedAndTypedDecl(canonical_type, typep.get(),
type_abi->set_name(QualTypeToString(canonical_type)); source_file) &&
ir_dumper_->AddLinkableMessageIR(typep.get());
}
// CreateBasicNamedAndTypedDecl creates a BasicNamedAndTypedDecl : that'll
// include all the generic information a basic type will have:
// abi_dump::BasicNamedAndTypedDecl. Other methods fill in more specific
// information, eg: RecordDecl, EnumDecl.
bool ABIWrapper::CreateBasicNamedAndTypedDecl(
clang::QualType canonical_type,
abi_util::TypeIR *typep, const std::string &source_file) {
// Cannot determine the size and alignment for template parameter dependent // Cannot determine the size and alignment for template parameter dependent
// types as well as incomplete types. // types as well as incomplete types.
const clang::Type *base_type = canonical_type.getTypePtr(); const clang::Type *base_type = canonical_type.getTypePtr();
assert(base_type != nullptr);
clang::Type::TypeClass type_class = base_type->getTypeClass(); clang::Type::TypeClass type_class = base_type->getTypeClass();
// Temporary Hack for auto type sizes. Not determinable. // Temporary Hack for auto type sizes. Not determinable.
if (dump_size && base_type && !(base_type->isDependentType()) && if ((type_class != clang::Type::Auto) && !base_type->isIncompleteType() &&
!(base_type->isIncompleteType()) && (type_class != clang::Type::Auto)) { !(base_type->isDependentType())) {
std::pair<clang::CharUnits, clang::CharUnits> size_and_alignment = std::pair<clang::CharUnits, clang::CharUnits> size_and_alignment =
ast_contextp_->getTypeInfoInChars(canonical_type); ast_contextp_->getTypeInfoInChars(canonical_type);
int64_t size = size_and_alignment.first.getQuantity(); size_t size = size_and_alignment.first.getQuantity();
int64_t alignment = size_and_alignment.second.getQuantity(); size_t alignment = size_and_alignment.second.getQuantity();
type_abi->set_size(size); typep->SetSize(size);
type_abi->set_alignment(alignment); typep->SetAlignment(alignment);
} }
return true; typep->SetName(QualTypeToString(canonical_type));
typep->SetLinkerSetKey(QualTypeToString(canonical_type));
// default values are false, we don't set them since explicitly doing that
// makes the abi dumps more verbose.
// This type has a reference type if its a pointer / reference OR it has CVR
// qualifiers.
clang::QualType referenced_type = GetReferencedType(canonical_type);
typep->SetReferencedType(QualTypeToString(referenced_type));
// Create the type for referenced type.
return CreateBasicNamedAndTypedDecl(referenced_type, source_file);
} }
bool ABIWrapper::SetupBasicNamedAndTypedDecl( std::string ABIWrapper::GetTypeLinkageName(const clang::Type *typep) {
abi_dump::BasicNamedAndTypedDecl *basic_named_and_typed_decl,
const clang::QualType type, const std::string &name,
const clang::AccessSpecifier &access, std::string key,
bool dump_size) const {
if (!basic_named_and_typed_decl) {
return false;
}
abi_dump::AccessSpecifier access_dump = AccessClangToDump(access);
basic_named_and_typed_decl->set_name(name);
basic_named_and_typed_decl->set_access(access_dump);
if (key != "") {
basic_named_and_typed_decl->set_linker_set_key(key);
}
return SetupBasicTypeAbi(basic_named_and_typed_decl->mutable_type_abi(),
type, dump_size);
}
static bool ShouldDumpSize(clang::QualType qt) {
const clang::Type *type_ptr = qt.getTypePtr();
assert(type_ptr != nullptr);
if (type_ptr->isBuiltinType() || type_ptr->isPointerType()) {
return true;
}
return false;
}
std::string ABIWrapper::GetTypeLinkageName(const clang::Type *typep) const {
assert(typep != nullptr); assert(typep != nullptr);
clang::QualType qt = typep->getCanonicalTypeInternal(); clang::QualType qt = typep->getCanonicalTypeInternal();
return QualTypeToString(qt); return QualTypeToString(qt);
} }
std::unique_ptr<abi_util::TypeIR> ABIWrapper::SetTypeKind(
const clang::QualType canonical_type, const std::string &source_file) {
if (canonical_type.hasLocalQualifiers()) {
auto qual_type_ir =
std::make_unique<abi_util::QualifiedTypeIR>();
qual_type_ir->SetConstness(canonical_type.isConstQualified());
qual_type_ir->SetRestrictedness(canonical_type.isRestrictQualified());
qual_type_ir->SetVolatility(canonical_type.isVolatileQualified());
qual_type_ir->SetSourceFile(source_file);
return qual_type_ir;
}
const clang::Type *type_ptr = canonical_type.getTypePtr();
if (type_ptr->isPointerType()) {
auto pointer_type_ir = std::make_unique<abi_util::PointerTypeIR>();
pointer_type_ir->SetSourceFile(source_file);
return pointer_type_ir;
}
if (type_ptr->isLValueReferenceType()) {
auto lvalue_reference_type_ir =
std::make_unique<abi_util::LvalueReferenceTypeIR>();
lvalue_reference_type_ir->SetSourceFile(source_file);
return lvalue_reference_type_ir;
}
if (type_ptr->isRValueReferenceType()) {
auto rvalue_reference_type_ir =
std::make_unique<abi_util::RvalueReferenceTypeIR>();
rvalue_reference_type_ir->SetSourceFile(source_file);
return rvalue_reference_type_ir;
}
if (type_ptr->isArrayType()) {
auto array_type_ir = std::make_unique<abi_util::ArrayTypeIR>();
array_type_ir->SetSourceFile(source_file);
return array_type_ir;
}
if (type_ptr->isEnumeralType()) {
return std::make_unique<abi_util::EnumTypeIR>();
}
if (type_ptr->isRecordType()) {
return std::make_unique<abi_util::RecordTypeIR>();
}
if (type_ptr->isBuiltinType()) {
auto builtin_type_ir = std::make_unique<abi_util::BuiltinTypeIR>();
builtin_type_ir->SetSignedness(type_ptr->isUnsignedIntegerType());
builtin_type_ir->SetIntegralType(type_ptr->isIntegralType(*ast_contextp_));
return builtin_type_ir;
}
return nullptr;
}
std::string ABIWrapper::GetMangledNameDecl( std::string ABIWrapper::GetMangledNameDecl(
const clang::NamedDecl *decl, clang::MangleContext *mangle_contextp) { const clang::NamedDecl *decl, clang::MangleContext *mangle_contextp) {
if (!mangle_contextp->shouldMangleDeclName(decl)) { if (!mangle_contextp->shouldMangleDeclName(decl)) {
@@ -145,36 +250,8 @@ std::string ABIWrapper::GetMangledNameDecl(
return mangled_name; return mangled_name;
} }
bool ABIWrapper::SetupTemplateParamNames(
abi_dump::TemplateInfo *tinfo,
clang::TemplateParameterList *pl) const {
if (tinfo->elements_size() > 0) {
return true;
}
clang::TemplateParameterList::iterator template_it = pl->begin();
while (template_it != pl->end()) {
abi_dump::TemplateElement *template_parameterp =
tinfo->add_elements();
if (!template_parameterp) {
return false;
}
abi_dump::TemplateElement::BasicTemplateElementAbi *basic_abi =
template_parameterp->mutable_basic_abi();
if (!basic_abi) {
return false;
}
std::string name = (*template_it)->getName();
basic_abi->set_name(name);
// TODO : Default arg ?
basic_abi->set_linker_set_key(name);
template_it++;
}
return true;
}
std::string ABIWrapper::GetTagDeclQualifiedName( std::string ABIWrapper::GetTagDeclQualifiedName(
const clang::TagDecl *decl) const { const clang::TagDecl *decl) {
if (decl->getTypedefNameForAnonDecl()) { if (decl->getTypedefNameForAnonDecl()) {
return decl->getTypedefNameForAnonDecl()->getQualifiedNameAsString(); return decl->getTypedefNameForAnonDecl()->getQualifiedNameAsString();
} }
@@ -182,8 +259,10 @@ std::string ABIWrapper::GetTagDeclQualifiedName(
} }
bool ABIWrapper::SetupTemplateArguments( bool ABIWrapper::SetupTemplateArguments(
abi_dump::TemplateInfo *tinfo, const clang::TemplateArgumentList *tl,
const clang::TemplateArgumentList *tl) const { abi_util::TemplatedArtifactIR *ta,
const std::string &source_file) {
abi_util::TemplateInfoIR template_info;
for (int i = 0; i < tl->size(); i++) { for (int i = 0; i < tl->size(); i++) {
const clang::TemplateArgument &arg = (*tl)[i]; const clang::TemplateArgument &arg = (*tl)[i];
//TODO: More comprehensive checking needed. //TODO: More comprehensive checking needed.
@@ -191,25 +270,33 @@ bool ABIWrapper::SetupTemplateArguments(
continue; continue;
} }
clang::QualType type = arg.getAsType(); clang::QualType type = arg.getAsType();
abi_dump::TemplateElement *template_parameterp = template_info.AddTemplateElement(
tinfo->add_elements(); abi_util::TemplateElementIR(QualTypeToString(type)));
if (!template_parameterp) { if (!CreateBasicNamedAndTypedDecl(type, source_file)) {
return false; return false;
} }
abi_dump::TemplateElement::BasicTemplateElementAbi *basic_abi =
template_parameterp->mutable_basic_abi();
if (!basic_abi || !SetupBasicTypeAbi(basic_abi->mutable_type_abi(), type,
false)) {
return false;
}
// TODO : default arg
basic_abi->set_linker_set_key(QualTypeToString(type));
} }
ta->SetTemplateInfo(std::move(template_info));
return true; return true;
} }
static const clang::EnumDecl *GetAnonymousEnum(
const clang::QualType qual_type) {
const clang::Type *type_ptr = qual_type.getTypePtr();
assert(type_ptr != nullptr);
const clang::TagDecl *tag_decl = type_ptr->getAsTagDecl();
if (!tag_decl) {
return nullptr;
}
const clang::EnumDecl *enum_decl = llvm::dyn_cast<clang::EnumDecl>(tag_decl);
if (!enum_decl || enum_decl->hasNameForLinkage()) {
return nullptr;
}
return enum_decl;
}
std::string ABIWrapper::QualTypeToString( std::string ABIWrapper::QualTypeToString(
const clang::QualType &sweet_qt) const { const clang::QualType &sweet_qt) {
const clang::QualType salty_qt = sweet_qt.getCanonicalType(); const clang::QualType salty_qt = sweet_qt.getCanonicalType();
// clang::TypeName::getFullyQualifiedName removes the part of the type related // clang::TypeName::getFullyQualifiedName removes the part of the type related
// to it being a template parameter. Don't use it for dependent types. // to it being a template parameter. Don't use it for dependent types.
@@ -223,75 +310,84 @@ FunctionDeclWrapper::FunctionDeclWrapper(
clang::MangleContext *mangle_contextp, clang::MangleContext *mangle_contextp,
clang::ASTContext *ast_contextp, clang::ASTContext *ast_contextp,
const clang::CompilerInstance *compiler_instance_p, const clang::CompilerInstance *compiler_instance_p,
const clang::FunctionDecl *decl) const clang::FunctionDecl *decl,
: ABIWrapper(mangle_contextp, ast_contextp, compiler_instance_p), std::set<std::string> *type_cache,
abi_util::IRDumper *ir_dumper,
std::map<const clang::Decl *, std::string> &decl_to_source_cache)
: ABIWrapper(mangle_contextp, ast_contextp, compiler_instance_p,
type_cache, ir_dumper, decl_to_source_cache),
function_decl_(decl) { } function_decl_(decl) { }
bool FunctionDeclWrapper::SetupFunctionParameters( bool FunctionDeclWrapper::SetupThisParameter(abi_util::FunctionIR *functionp,
abi_dump::FunctionDecl *functionp) const { const std::string &source_file) {
clang::FunctionDecl::param_const_iterator param_it = const clang::CXXMethodDecl *cxx_method_decl =
function_decl_->param_begin(); llvm::dyn_cast<clang::CXXMethodDecl>(function_decl_);
while (param_it != function_decl_->param_end()) { // No this pointer for static methods.
abi_dump::ParamDecl *function_fieldp = functionp->add_parameters(); if (!cxx_method_decl || cxx_method_decl->isStatic()) {
if (!function_fieldp) { return true;
llvm::errs() << "Couldn't add parameter to method. Aborting\n"; }
clang::QualType this_type = cxx_method_decl->getThisType(*ast_contextp_);
return SetupFunctionParameter(functionp, this_type, false, source_file);
}
bool FunctionDeclWrapper::SetupFunctionParameter(
abi_util::FunctionIR *functionp, const clang::QualType qual_type,
bool has_default_arg, const std::string &source_file) {
if (!CreateBasicNamedAndTypedDecl(qual_type, source_file)) {
return false; return false;
} }
functionp->AddParameter(abi_util::ParamIR(QualTypeToString(qual_type),
has_default_arg));
return true;
}
bool FunctionDeclWrapper::SetupFunctionParameters(
abi_util::FunctionIR *functionp,
const std::string &source_file) {
clang::FunctionDecl::param_const_iterator param_it =
function_decl_->param_begin();
// If this is a CXXMethodDecl, we need to add the "this" pointer.
if (!SetupThisParameter(functionp, source_file)) {
return false;
}
while (param_it != function_decl_->param_end()) {
// The linker set key is blank since that shows up in the mangled name. // The linker set key is blank since that shows up in the mangled name.
bool has_default_arg = (*param_it)->hasDefaultArg(); bool has_default_arg = (*param_it)->hasDefaultArg();
clang::QualType param_qt = (*param_it)->getType(); clang::QualType param_qt = (*param_it)->getType();
bool should_dump_size = ShouldDumpSize(param_qt); if (!SetupFunctionParameter(functionp, param_qt, has_default_arg,
if (!SetupBasicNamedAndTypedDecl( source_file)) {
function_fieldp->mutable_basic_abi(),
(*param_it)->getType(), (*param_it)->getName(),
(*param_it)->getAccess(), has_default_arg ? "true" : "false",
should_dump_size)) {
return false; return false;
} }
function_fieldp->set_default_arg(has_default_arg);
param_it++; param_it++;
} }
return true; return true;
} }
bool FunctionDeclWrapper::SetupFunction(abi_dump::FunctionDecl *functionp, bool FunctionDeclWrapper::SetupFunction(abi_util::FunctionIR *functionp,
const std::string &source_file) const { const std::string &source_file) {
// Go through all the parameters in the method and add them to the fields. // Go through all the parameters in the method and add them to the fields.
// Also get the fully qualfied name. // Also get the fully qualfied name.
functionp->set_source_file(source_file); functionp->SetSourceFile(source_file);
// Combine the function name and return type to form a NamedAndTypedDecl functionp->SetName(function_decl_->getQualifiedNameAsString());
clang::QualType return_type = function_decl_->getReturnType(); clang::QualType return_type = function_decl_->getReturnType();
bool should_dump_size = ShouldDumpSize(return_type);
return SetupBasicNamedAndTypedDecl( functionp->SetReturnType(QualTypeToString(return_type));
functionp->mutable_basic_abi(), functionp->SetAccess(AccessClangToIR(function_decl_->getAccess()));
return_type, function_decl_->getQualifiedNameAsString(), return CreateBasicNamedAndTypedDecl(return_type, source_file) &&
function_decl_->getAccess(), "", should_dump_size) && SetupFunctionParameters(functionp, source_file) &&
SetupTemplateInfo(functionp) && SetupFunctionParameters(functionp); SetupTemplateInfo(functionp, source_file);
} }
bool FunctionDeclWrapper::SetupTemplateInfo( bool FunctionDeclWrapper::SetupTemplateInfo(
abi_dump::FunctionDecl *functionp) const { abi_util::FunctionIR *functionp,
const std::string &source_file) {
switch (function_decl_->getTemplatedKind()) { switch (function_decl_->getTemplatedKind()) {
case clang::FunctionDecl::TK_FunctionTemplate: {
clang::FunctionTemplateDecl *template_decl =
function_decl_->getDescribedFunctionTemplate();
if (template_decl) {
clang::TemplateParameterList *template_parameter_list =
template_decl->getTemplateParameters();
if (template_parameter_list &&
!SetupTemplateParamNames(functionp->mutable_template_info(),
template_parameter_list)) {
return false;
}
}
break;
}
case clang::FunctionDecl::TK_FunctionTemplateSpecialization: { case clang::FunctionDecl::TK_FunctionTemplateSpecialization: {
const clang::TemplateArgumentList *arg_list = const clang::TemplateArgumentList *arg_list =
function_decl_->getTemplateSpecializationArgs(); function_decl_->getTemplateSpecializationArgs();
if (arg_list && if (arg_list && !SetupTemplateArguments(arg_list, functionp,
!SetupTemplateArguments(functionp->mutable_template_info(), source_file)) {
arg_list)) {
return false; return false;
} }
break; break;
@@ -303,11 +399,9 @@ bool FunctionDeclWrapper::SetupTemplateInfo(
return true; return true;
} }
std::unique_ptr<abi_dump::FunctionDecl> std::unique_ptr<abi_util::FunctionIR> FunctionDeclWrapper::GetFunctionDecl() {
FunctionDeclWrapper::GetFunctionDecl() const { auto abi_decl = std::make_unique<abi_util::FunctionIR>();
std::unique_ptr<abi_dump::FunctionDecl> abi_decl( std::string source_file = GetCachedDeclSourceFile(function_decl_, cip_);
new abi_dump::FunctionDecl());
std::string source_file = GetDeclSourceFile(function_decl_, cip_);
if (!SetupFunction(abi_decl.get(), source_file)) { if (!SetupFunction(abi_decl.get(), source_file)) {
return nullptr; return nullptr;
} }
@@ -318,59 +412,110 @@ RecordDeclWrapper::RecordDeclWrapper(
clang::MangleContext *mangle_contextp, clang::MangleContext *mangle_contextp,
clang::ASTContext *ast_contextp, clang::ASTContext *ast_contextp,
const clang::CompilerInstance *compiler_instance_p, const clang::CompilerInstance *compiler_instance_p,
const clang::RecordDecl *decl) const clang::RecordDecl *decl,
: ABIWrapper(mangle_contextp, ast_contextp, compiler_instance_p), std::set<std::string> *type_cache,
record_decl_(decl) { } abi_util::IRDumper *ir_dumper,
std::map<const clang::Decl *, std::string> &decl_to_source_cache,
const std::string &previous_record_stages)
: ABIWrapper(mangle_contextp, ast_contextp, compiler_instance_p,
type_cache, ir_dumper, decl_to_source_cache),
record_decl_(decl), previous_record_stages_(previous_record_stages) { }
bool RecordDeclWrapper::SetupRecordFields(abi_dump::RecordDecl *recordp) const { bool RecordDeclWrapper::CreateAnonymousRecord(
const clang::RecordDecl *record_decl, const std::string &linker_set_key) {
RecordDeclWrapper record_decl_wrapper(mangle_contextp_, ast_contextp_, cip_,
record_decl, type_cache_, ir_dumper_,
decl_to_source_file_cache_,
linker_set_key);
return record_decl_wrapper.GetRecordDecl();
}
static const clang::RecordDecl *GetAnonymousRecord(clang::QualType type) {
const clang::Type *type_ptr = type.getTypePtr();
assert(type_ptr != nullptr);
if (!type_ptr->isRecordType()) {
return nullptr;
}
const clang::TagDecl *tag_decl = type_ptr->getAsTagDecl();
if (!tag_decl) {
return nullptr;
}
const clang::RecordDecl *record_decl =
llvm::dyn_cast<clang::RecordDecl>(tag_decl);
if (record_decl != nullptr && (!record_decl->hasNameForLinkage() ||
record_decl->isAnonymousStructOrUnion())) {
return record_decl;
}
return nullptr;
}
bool RecordDeclWrapper::SetupRecordFields(abi_util::RecordTypeIR *recordp,
const std::string &source_file) {
clang::RecordDecl::field_iterator field = record_decl_->field_begin(); clang::RecordDecl::field_iterator field = record_decl_->field_begin();
uint32_t field_index = 0;
const clang::ASTRecordLayout &record_layout =
ast_contextp_->getASTRecordLayout(record_decl_);
while (field != record_decl_->field_end()) { while (field != record_decl_->field_end()) {
abi_dump::RecordFieldDecl *record_fieldp = recordp->add_fields(); clang::QualType field_type = field->getType();
if (!record_fieldp) { if (!CreateBasicNamedAndTypedDecl(field_type, source_file)) {
llvm::errs() << " Couldn't add record field: " << field->getName() llvm::errs() << "Creation of Type failed\n";
<< " to reference dump\n";
return false; return false;
} }
if (!SetupBasicNamedAndTypedDecl(record_fieldp->mutable_basic_abi(), std::string field_type_str = QualTypeToString(field_type);
field->getType(), field->getName(), std::string field_name = field->getName();
field->getAccess(), "", true)) { // Handle anoymous structs / unions as fields.
if (const clang::RecordDecl *anon_record_decl =
GetAnonymousRecord(field_type)) {
// We need to create a unique linker set key for anonymous structs / enums
// since clang just names them with fully_qualified_scope::anonymous.
field_type_str =
previous_record_stages_ + "::(anonymous)" +
std::to_string(field_index);
if (!CreateAnonymousRecord(anon_record_decl, field_type_str)) {
return false; return false;
} }
} else if (const clang::EnumDecl *enum_decl =
GetAnonymousEnum(field_type)) {
// Handle anonymous enums.
field_type_str = QualTypeToString(enum_decl->getIntegerType());
}
uint64_t field_offset = record_layout.getFieldOffset(field_index);
recordp->AddRecordField(abi_util::RecordFieldIR(
field_name, field_type_str, field_offset,
AccessClangToIR(field->getAccess())));
field++; field++;
field_index++;
} }
return true; return true;
} }
bool RecordDeclWrapper::SetupCXXBases( bool RecordDeclWrapper::SetupCXXBases(
abi_dump::RecordDecl *cxxp, abi_util::RecordTypeIR *cxxp,
const clang::CXXRecordDecl *cxx_record_decl) const { const clang::CXXRecordDecl *cxx_record_decl) {
assert(cxx_record_decl != nullptr); if (!cxx_record_decl || !cxxp) {
return false;
}
clang::CXXRecordDecl::base_class_const_iterator base_class = clang::CXXRecordDecl::base_class_const_iterator base_class =
cxx_record_decl->bases_begin(); cxx_record_decl->bases_begin();
while (base_class != cxx_record_decl->bases_end()) { while (base_class != cxx_record_decl->bases_end()) {
abi_dump::CXXBaseSpecifier *base_specifierp = cxxp->add_base_specifiers();
if (!base_specifierp) {
llvm::errs() << " Couldn't add base specifier to reference dump\n";
return false;
}
std::string name = QualTypeToString(base_class->getType()); std::string name = QualTypeToString(base_class->getType());
bool is_virtual = base_class->isVirtual(); bool is_virtual = base_class->isVirtual();
if (!SetupBasicNamedAndTypedDecl(base_specifierp->mutable_basic_abi(), abi_util::AccessSpecifierIR access =
base_class->getType(), AccessClangToIR(base_class->getAccessSpecifier());
"", base_class->getAccessSpecifier(), cxxp->AddCXXBaseSpecifier(abi_util::CXXBaseSpecifierIR(name, is_virtual,
"", false)) { access));
return false;
}
base_specifierp->set_is_virtual(is_virtual);
base_class++; base_class++;
} }
return true; return true;
} }
bool RecordDeclWrapper::SetupRecordVTable( bool RecordDeclWrapper::SetupRecordVTable(
abi_dump::RecordDecl *record_declp, abi_util::RecordTypeIR *record_declp,
const clang::CXXRecordDecl *cxx_record_decl) const { const clang::CXXRecordDecl *cxx_record_decl) {
assert(cxx_record_decl != nullptr); if (!cxx_record_decl || !record_declp) {
return false;
}
clang::VTableContextBase *base_vtable_contextp = clang::VTableContextBase *base_vtable_contextp =
ast_contextp_->getVTableContext(); ast_contextp_->getVTableContext();
const clang::Type *typep = cxx_record_decl->getTypeForDecl(); const clang::Type *typep = cxx_record_decl->getTypeForDecl();
@@ -386,26 +531,20 @@ bool RecordDeclWrapper::SetupRecordVTable(
} }
const clang::VTableLayout &vtable_layout = const clang::VTableLayout &vtable_layout =
itanium_vtable_contextp->getVTableLayout(cxx_record_decl); itanium_vtable_contextp->getVTableLayout(cxx_record_decl);
abi_dump::VTableLayout *vtablep = record_declp->mutable_vtable_layout(); abi_util::VTableLayoutIR vtable_ir_layout;
if (!vtablep) {
return false;
}
for (const auto &vtable_component : vtable_layout.vtable_components()) { for (const auto &vtable_component : vtable_layout.vtable_components()) {
abi_dump::VTableComponent *added_vtable_component = abi_util::VTableComponentIR added_component=
vtablep->add_vtable_components(); SetupRecordVTableComponent(vtable_component);
if (!added_vtable_component || vtable_ir_layout.AddVTableComponent(std::move(added_component));
!SetupRecordVTableComponent(added_vtable_component, vtable_component)) {
return false;
}
} }
record_declp->SetVTableLayout(std::move(vtable_ir_layout));
return true; return true;
} }
bool RecordDeclWrapper::SetupRecordVTableComponent( abi_util::VTableComponentIR RecordDeclWrapper::SetupRecordVTableComponent(
abi_dump::VTableComponent *added_vtable_component, const clang::VTableComponent &vtable_component) {
const clang::VTableComponent &vtable_component) const { abi_util::VTableComponentIR::Kind kind =
assert(added_vtable_component != nullptr); abi_util::VTableComponentIR::Kind::RTTI;
abi_dump::VTableComponent_Kind kind = abi_dump::VTableComponent_Kind_RTTI;
std::string mangled_component_name = ""; std::string mangled_component_name = "";
llvm::raw_string_ostream ostream(mangled_component_name); llvm::raw_string_ostream ostream(mangled_component_name);
int64_t value = 0; int64_t value = 0;
@@ -413,20 +552,20 @@ bool RecordDeclWrapper::SetupRecordVTableComponent(
vtable_component.getKind(); vtable_component.getKind();
switch (clang_component_kind) { switch (clang_component_kind) {
case clang::VTableComponent::CK_VCallOffset: case clang::VTableComponent::CK_VCallOffset:
kind = abi_dump::VTableComponent_Kind_VCallOffset; kind = abi_util::VTableComponentIR::Kind::VCallOffset;
value = vtable_component.getVCallOffset().getQuantity(); value = vtable_component.getVCallOffset().getQuantity();
break; break;
case clang::VTableComponent::CK_VBaseOffset: case clang::VTableComponent::CK_VBaseOffset:
kind = abi_dump::VTableComponent_Kind_VBaseOffset; kind = abi_util::VTableComponentIR::Kind::VBaseOffset;
value = vtable_component.getVBaseOffset().getQuantity(); value = vtable_component.getVBaseOffset().getQuantity();
break; break;
case clang::VTableComponent::CK_OffsetToTop: case clang::VTableComponent::CK_OffsetToTop:
kind = abi_dump::VTableComponent_Kind_OffsetToTop; kind = abi_util::VTableComponentIR::Kind::OffsetToTop;
value = vtable_component.getOffsetToTop().getQuantity(); value = vtable_component.getOffsetToTop().getQuantity();
break; break;
case clang::VTableComponent::CK_RTTI: case clang::VTableComponent::CK_RTTI:
{ {
kind = abi_dump::VTableComponent_Kind_RTTI; kind = abi_util::VTableComponentIR::Kind::RTTI;
const clang::CXXRecordDecl *rtti_decl = const clang::CXXRecordDecl *rtti_decl =
vtable_component.getRTTIDecl(); vtable_component.getRTTIDecl();
assert(rtti_decl != nullptr); assert(rtti_decl != nullptr);
@@ -444,205 +583,202 @@ bool RecordDeclWrapper::SetupRecordVTableComponent(
assert(method_decl != nullptr); assert(method_decl != nullptr);
switch (clang_component_kind) { switch (clang_component_kind) {
case clang::VTableComponent::CK_FunctionPointer: case clang::VTableComponent::CK_FunctionPointer:
kind = abi_dump::VTableComponent_Kind_FunctionPointer; kind = abi_util::VTableComponentIR::Kind::FunctionPointer;
mangled_component_name = GetMangledNameDecl(method_decl, mangled_component_name = GetMangledNameDecl(method_decl,
mangle_contextp_); mangle_contextp_);
break; break;
case clang::VTableComponent::CK_CompleteDtorPointer: case clang::VTableComponent::CK_CompleteDtorPointer:
kind = abi_dump::VTableComponent_Kind_CompleteDtorPointer; kind = abi_util::VTableComponentIR::Kind::CompleteDtorPointer;
mangle_contextp_->mangleCXXDtor( mangle_contextp_->mangleCXXDtor(
vtable_component.getDestructorDecl(), vtable_component.getDestructorDecl(),
clang::CXXDtorType::Dtor_Complete, ostream); clang::CXXDtorType::Dtor_Complete, ostream);
ostream.flush(); ostream.flush();
break; break;
case clang::VTableComponent::CK_DeletingDtorPointer: case clang::VTableComponent::CK_DeletingDtorPointer:
kind = abi_dump::VTableComponent_Kind_DeletingDtorPointer; kind = abi_util::VTableComponentIR::Kind::DeletingDtorPointer;
mangle_contextp_->mangleCXXDtor( mangle_contextp_->mangleCXXDtor(
vtable_component.getDestructorDecl(), vtable_component.getDestructorDecl(),
clang::CXXDtorType::Dtor_Deleting, ostream); clang::CXXDtorType::Dtor_Deleting, ostream);
ostream.flush(); ostream.flush();
break; break;
case clang::VTableComponent::CK_UnusedFunctionPointer: case clang::VTableComponent::CK_UnusedFunctionPointer:
kind = abi_dump::VTableComponent_Kind_UnusedFunctionPointer; kind = abi_util::VTableComponentIR::Kind::UnusedFunctionPointer;
break;
default: default:
break; break;
} }
} }
break; break;
default: default:
return false; break;
} }
added_vtable_component->set_kind(kind); return abi_util::VTableComponentIR(mangled_component_name, kind, value);
added_vtable_component->set_component_value(value);
added_vtable_component->set_mangled_component_name(mangled_component_name);
return true;
} }
bool RecordDeclWrapper::SetupTemplateInfo( bool RecordDeclWrapper::SetupTemplateInfo(
abi_dump::RecordDecl *record_declp, abi_util::RecordTypeIR *record_declp,
const clang::CXXRecordDecl *cxx_record_decl) const { const clang::CXXRecordDecl *cxx_record_decl,
const std::string &source_file) {
assert(cxx_record_decl != nullptr); assert(cxx_record_decl != nullptr);
if (cxx_record_decl->isTemplateDecl()) {
clang::ClassTemplateDecl *template_decl =
cxx_record_decl->getDescribedClassTemplate();
if (template_decl) {
clang::TemplateParameterList *template_parameter_list =
template_decl->getTemplateParameters();
if (template_parameter_list &&
!SetupTemplateParamNames(record_declp->mutable_template_info(),
template_parameter_list)) {
return false;
}
}
} else {
const clang::ClassTemplateSpecializationDecl *specialization_decl = const clang::ClassTemplateSpecializationDecl *specialization_decl =
clang::dyn_cast<clang::ClassTemplateSpecializationDecl>( clang::dyn_cast<clang::ClassTemplateSpecializationDecl>(cxx_record_decl);
cxx_record_decl);
if (specialization_decl) { if (specialization_decl) {
const clang::TemplateArgumentList *arg_list = const clang::TemplateArgumentList *arg_list =
&specialization_decl->getTemplateArgs(); &specialization_decl->getTemplateArgs();
if (arg_list && if (arg_list &&
!SetupTemplateArguments(record_declp->mutable_template_info(), !SetupTemplateArguments(arg_list, record_declp, source_file)) {
arg_list)) {
return false; return false;
} }
} }
}
return true; return true;
} }
bool RecordDeclWrapper::SetupRecordInfo(abi_dump::RecordDecl *record_declp, bool RecordDeclWrapper::SetupRecordInfo(abi_util::RecordTypeIR *record_declp,
const std::string &source_file) const { const std::string &source_file) {
std::string qualified_name = GetTagDeclQualifiedName(record_decl_); if (!record_declp) {
return false;
}
if (record_decl_->isStruct()) {
record_declp->SetRecordKind(
abi_util::RecordTypeIR::RecordKind::struct_kind);
} else if (record_decl_->isClass()) {
record_declp->SetRecordKind(
abi_util::RecordTypeIR::RecordKind::class_kind);
} else {
record_declp->SetRecordKind(
abi_util::RecordTypeIR::RecordKind::union_kind);
}
const clang::Type *basic_type = nullptr; const clang::Type *basic_type = nullptr;
if (!(basic_type = record_decl_->getTypeForDecl())) { if (!(basic_type = record_decl_->getTypeForDecl())) {
return false; return false;
} }
std::string mangled_name = ABIWrapper::GetTypeLinkageName(basic_type); clang::QualType qual_type = basic_type->getCanonicalTypeInternal();
clang::QualType type = basic_type->getCanonicalTypeInternal(); if (!CreateExtendedType(qual_type, record_declp)) {
std::string linker_key = (mangled_name == "") ? qualified_name : mangled_name;
if (!SetupBasicNamedAndTypedDecl(record_declp->mutable_basic_abi(),
type, qualified_name,
record_decl_->getAccess(), linker_key,
true)) {
return false; return false;
} }
record_declp->set_mangled_record_name(mangled_name); std::string record_qual_type_str = QualTypeToString(qual_type);
record_declp->set_source_file(source_file); record_declp->SetSourceFile(source_file);
return true; if (!record_decl_->hasNameForLinkage() ||
record_decl_->isAnonymousStructOrUnion()) {
record_declp->SetLinkerSetKey(previous_record_stages_);
record_declp->SetAnonymity(true);
} else {
previous_record_stages_ = record_qual_type_str;
record_declp->SetLinkerSetKey(record_qual_type_str);
}
record_declp->SetAccess(AccessClangToIR(record_decl_->getAccess()));
return SetupRecordFields(record_declp, source_file) &&
SetupCXXRecordInfo(record_declp, source_file);
} }
bool RecordDeclWrapper::SetupCXXRecordInfo( bool RecordDeclWrapper::SetupCXXRecordInfo(
abi_dump::RecordDecl *record_declp) const { abi_util::RecordTypeIR *record_declp, const std::string &source_file) {
const clang::CXXRecordDecl *cxx_record_decl = const clang::CXXRecordDecl *cxx_record_decl =
clang::dyn_cast<clang::CXXRecordDecl>(record_decl_); clang::dyn_cast<clang::CXXRecordDecl>(record_decl_);
if (!cxx_record_decl) { if (!cxx_record_decl) {
return true; return true;
} }
return SetupTemplateInfo(record_declp, cxx_record_decl) && return SetupTemplateInfo(record_declp, cxx_record_decl, source_file) &&
SetupCXXBases(record_declp, cxx_record_decl) && SetupCXXBases(record_declp, cxx_record_decl) &&
SetupRecordVTable(record_declp, cxx_record_decl); SetupRecordVTable(record_declp, cxx_record_decl);
} }
std::unique_ptr<abi_dump::RecordDecl> RecordDeclWrapper::GetRecordDecl() const { bool RecordDeclWrapper::GetRecordDecl() {
std::unique_ptr<abi_dump::RecordDecl> abi_decl(new abi_dump::RecordDecl()); auto abi_decl = std::make_unique<abi_util::RecordTypeIR>();
std::string source_file = GetDeclSourceFile(record_decl_, cip_); std::string source_file = GetCachedDeclSourceFile(record_decl_, cip_);
abi_dump::RecordDecl *record_declp = abi_decl.get(); if (!SetupRecordInfo(abi_decl.get(), source_file)) {
if (!SetupRecordInfo(record_declp, source_file) ||
!SetupRecordFields(record_declp) ||
!SetupCXXRecordInfo(abi_decl.get())) {
llvm::errs() << "Setting up CXX Bases / Template Info failed\n"; llvm::errs() << "Setting up CXX Bases / Template Info failed\n";
return nullptr; return false;
} }
return abi_decl; return ir_dumper_->AddLinkableMessageIR(abi_decl.get());
} }
EnumDeclWrapper::EnumDeclWrapper( EnumDeclWrapper::EnumDeclWrapper(
clang::MangleContext *mangle_contextp, clang::MangleContext *mangle_contextp,
clang::ASTContext *ast_contextp, clang::ASTContext *ast_contextp,
const clang::CompilerInstance *compiler_instance_p, const clang::CompilerInstance *compiler_instance_p,
const clang::EnumDecl *decl) const clang::EnumDecl *decl,
: ABIWrapper(mangle_contextp, ast_contextp, compiler_instance_p), std::set<std::string> *type_cache,
abi_util::IRDumper *ir_dumper,
std::map<const clang::Decl *, std::string> &decl_to_source_cache)
: ABIWrapper(mangle_contextp, ast_contextp, compiler_instance_p,
type_cache, ir_dumper, decl_to_source_cache),
enum_decl_(decl) { } enum_decl_(decl) { }
bool EnumDeclWrapper::SetupEnumFields(abi_dump::EnumDecl *enump) const { bool EnumDeclWrapper::SetupEnumFields(abi_util::EnumTypeIR *enump) {
clang::EnumDecl::enumerator_iterator enum_it = enum_decl_->enumerator_begin(); if (!enump) {
while (enum_it != enum_decl_->enumerator_end()) {
abi_dump::EnumFieldDecl *enum_fieldp = enump->add_enum_fields();
std::string name = enum_it->getQualifiedNameAsString();
uint64_t field_value = enum_it->getInitVal().getExtValue();
if (!enum_fieldp ||
!SetupBasicNamedAndTypedDecl(enum_fieldp->mutable_basic_abi(),
enum_it->getType(), name,
enum_it->getAccess(),
std::to_string(field_value), true)) {
return false; return false;
} }
enum_fieldp->set_enum_field_value(field_value); clang::EnumDecl::enumerator_iterator enum_it = enum_decl_->enumerator_begin();
while (enum_it != enum_decl_->enumerator_end()) {
std::string name = enum_it->getQualifiedNameAsString();
uint64_t field_value = enum_it->getInitVal().getExtValue();
enump->AddEnumField(abi_util::EnumFieldIR(name, field_value));
enum_it++; enum_it++;
} }
return true; return true;
} }
bool EnumDeclWrapper::SetupEnum(abi_dump::EnumDecl *enump, bool EnumDeclWrapper::SetupEnum(abi_util::EnumTypeIR *enum_type,
const std::string &source_file) const { const std::string &source_file) {
std::string enum_name = GetTagDeclQualifiedName(enum_decl_); std::string enum_name = GetTagDeclQualifiedName(enum_decl_);
clang::QualType enum_type = enum_decl_->getIntegerType(); clang::QualType enum_qual_type =
if (!SetupBasicNamedAndTypedDecl(enump->mutable_basic_abi(), enum_type, enum_decl_->getTypeForDecl()->getCanonicalTypeInternal();
enum_name, enum_decl_->getAccess(), if (!CreateExtendedType(enum_qual_type, enum_type)) {
enum_name, true) ||
!SetupEnumFields(enump)) {
return false; return false;
} }
enump->set_source_file(source_file); enum_type->SetSourceFile(source_file);
return true; enum_type->SetUnderlyingType(QualTypeToString(enum_decl_->getIntegerType()));
enum_type->SetAccess(AccessClangToIR(enum_decl_->getAccess()));
return SetupEnumFields(enum_type) &&
CreateBasicNamedAndTypedDecl(enum_decl_->getIntegerType(), "");
} }
std::unique_ptr<abi_dump::EnumDecl> EnumDeclWrapper::GetEnumDecl() const { bool EnumDeclWrapper::GetEnumDecl() {
std::unique_ptr<abi_dump::EnumDecl> abi_decl(new abi_dump::EnumDecl()); auto abi_decl = std::make_unique<abi_util::EnumTypeIR>();
std::string source_file = GetDeclSourceFile(enum_decl_, cip_); std::string source_file = GetCachedDeclSourceFile(enum_decl_, cip_);
if (!SetupEnum(abi_decl.get(), source_file)) { if (!SetupEnum(abi_decl.get(), source_file)) {
llvm::errs() << "Setting up Enum fields failed\n"; llvm::errs() << "Setting up Enum failed\n";
return nullptr; return false;
} }
return abi_decl; return ir_dumper_->AddLinkableMessageIR(abi_decl.get());
} }
GlobalVarDeclWrapper::GlobalVarDeclWrapper( GlobalVarDeclWrapper::GlobalVarDeclWrapper(
clang::MangleContext *mangle_contextp, clang::MangleContext *mangle_contextp,
clang::ASTContext *ast_contextp, clang::ASTContext *ast_contextp,
const clang::CompilerInstance *compiler_instance_p, const clang::CompilerInstance *compiler_instance_p,
const clang::VarDecl *decl) const clang::VarDecl *decl,std::set<std::string> *type_cache,
: ABIWrapper(mangle_contextp, ast_contextp, compiler_instance_p), abi_util::IRDumper *ir_dumper,
std::map<const clang::Decl *, std::string> &decl_to_source_cache)
: ABIWrapper(mangle_contextp, ast_contextp, compiler_instance_p, type_cache,
ir_dumper, decl_to_source_cache),
global_var_decl_(decl) { } global_var_decl_(decl) { }
bool GlobalVarDeclWrapper::SetupGlobalVar( bool GlobalVarDeclWrapper::SetupGlobalVar(
abi_dump::GlobalVarDecl *global_varp, abi_util::GlobalVarIR *global_varp,
const std::string &source_file) const { const std::string &source_file) {
// Temporary fix : clang segfaults on trying to mangle global variable which // Temporary fix : clang segfaults on trying to mangle global variable which
// is a dependent sized array type. // is a dependent sized array type.
std::string qualified_name = global_var_decl_->getQualifiedNameAsString();
std::string mangled_name = std::string mangled_name =
GetMangledNameDecl(global_var_decl_, mangle_contextp_); GetMangledNameDecl(global_var_decl_, mangle_contextp_);
if (!SetupBasicNamedAndTypedDecl( if (!CreateBasicNamedAndTypedDecl(global_var_decl_->getType(), source_file)) {
global_varp->mutable_basic_abi(),global_var_decl_->getType(),
qualified_name, global_var_decl_->getAccess(),
mangled_name, true)) {
return false; return false;
} }
global_varp->set_source_file(source_file); global_varp->SetSourceFile(source_file);
global_varp->SetName(global_var_decl_->getQualifiedNameAsString());
global_varp->SetLinkerSetKey(mangled_name);
global_varp->SetReferencedType(
QualTypeToString(global_var_decl_->getType()));
return true; return true;
} }
std::unique_ptr<abi_dump::GlobalVarDecl> bool GlobalVarDeclWrapper::GetGlobalVarDecl() {
GlobalVarDeclWrapper::GetGlobalVarDecl() const { auto abi_decl = std::make_unique<abi_util::GlobalVarIR>();
std::unique_ptr<abi_dump::GlobalVarDecl> std::string source_file = GetCachedDeclSourceFile(global_var_decl_, cip_);
abi_decl(new abi_dump::GlobalVarDecl); return SetupGlobalVar(abi_decl.get(), source_file) &&
std::string source_file = GetDeclSourceFile(global_var_decl_, cip_); ir_dumper_->AddLinkableMessageIR(abi_decl.get());
if (!SetupGlobalVar(abi_decl.get(), source_file)) {
return nullptr;
}
return abi_decl;
} }

View File

@@ -15,6 +15,8 @@
#ifndef ABI_WRAPPERS_H_ #ifndef ABI_WRAPPERS_H_
#define ABI_WRAPPERS_H_ #define ABI_WRAPPERS_H_
#include <ir_representation.h>
#pragma clang diagnostic push #pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-parameter" #pragma clang diagnostic ignored "-Wunused-parameter"
#pragma clang diagnostic ignored "-Wnested-anon-types" #pragma clang diagnostic ignored "-Wnested-anon-types"
@@ -32,7 +34,10 @@ class ABIWrapper {
public: public:
ABIWrapper(clang::MangleContext *mangle_contextp, ABIWrapper(clang::MangleContext *mangle_contextp,
clang::ASTContext *ast_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, static std::string GetDeclSourceFile(const clang::Decl *decl,
const clang::CompilerInstance *cip); const clang::CompilerInstance *cip);
@@ -41,121 +46,159 @@ class ABIWrapper {
clang::MangleContext *mangle_context); clang::MangleContext *mangle_context);
protected: protected:
abi_dump::AccessSpecifier AccessClangToDump( 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, bool SetupTemplateArguments(const clang::TemplateArgumentList *tl,
clang::TemplateParameterList *pl) const; abi_util::TemplatedArtifactIR *ta,
const std::string &source_file);
bool SetupTemplateArguments(abi_dump::TemplateInfo *tinfo, std::string QualTypeToString(const clang::QualType &sweet_qt);
const clang::TemplateArgumentList *tl) const;
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, bool CreateExtendedType(
const clang::QualType type, bool dump_size) const; clang::QualType canonical_type,
abi_util::TypeIR *typep);
bool SetupBasicNamedAndTypedDecl( clang::QualType GetReferencedType(const clang::QualType qual_type);
abi_dump::BasicNamedAndTypedDecl *basic_named_and_typed_decl,
const clang::QualType type, const std::string &name, std::string GetTypeLinkageName(const clang::Type *typep);
const clang::AccessSpecifier &access, std::string key,
bool dump_size) const; std::unique_ptr<abi_util::TypeIR> SetTypeKind(const clang::QualType qtype,
const std::string &source_file);
std::string GetTypeLinkageName(const clang::Type *typep) const;
protected: protected:
const clang::CompilerInstance *cip_; const clang::CompilerInstance *cip_;
clang::MangleContext *mangle_contextp_; clang::MangleContext *mangle_contextp_;
clang::ASTContext *ast_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 { class RecordDeclWrapper : public ABIWrapper {
public: public:
RecordDeclWrapper(clang::MangleContext *mangle_contextp, RecordDeclWrapper(
clang::ASTContext *ast_contextp, clang::MangleContext *mangle_contextp, clang::ASTContext *ast_contextp,
const clang::CompilerInstance *compiler_instance_p, const clang::CompilerInstance *compiler_instance_p,
const clang::RecordDecl *decl); 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: private:
const clang::RecordDecl *record_decl_; const clang::RecordDecl *record_decl_;
std::string previous_record_stages_;
private: private:
bool SetupRecordInfo(abi_dump::RecordDecl *record_declp, bool SetupRecordInfo(abi_util::RecordTypeIR *type,
const std::string &source_file) const; 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, bool SetupCXXBases(abi_util::RecordTypeIR *cxxp,
const clang::CXXRecordDecl *cxx_record_decl) const; const clang::CXXRecordDecl *cxx_record_decl);
bool SetupTemplateInfo(abi_dump::RecordDecl *record_declp, bool SetupTemplateInfo(abi_util::RecordTypeIR *record_declp,
const clang::CXXRecordDecl *cxx_record_decl) const; const clang::CXXRecordDecl *cxx_record_decl,
const std::string &source_file);
bool SetupRecordVTable(abi_dump::RecordDecl *record_declp, bool SetupRecordVTable(abi_util::RecordTypeIR *record_declp,
const clang::CXXRecordDecl *cxx_record_decl) const; const clang::CXXRecordDecl *cxx_record_decl);
bool SetupRecordVTableComponent( abi_util::VTableComponentIR SetupRecordVTableComponent(
abi_dump::VTableComponent *added_vtable_component, const clang::VTableComponent &vtable_component);
const clang::VTableComponent &vtable_component) const;
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 { class FunctionDeclWrapper : public ABIWrapper {
public: public:
FunctionDeclWrapper(clang::MangleContext *mangle_contextp, FunctionDeclWrapper(
clang::ASTContext *ast_contextp, clang::MangleContext *mangle_contextp, clang::ASTContext *ast_contextp,
const clang::CompilerInstance *compiler_instance_p, const clang::CompilerInstance *compiler_instance_p,
const clang::FunctionDecl *decl); 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: private:
const clang::FunctionDecl *function_decl_; const clang::FunctionDecl *function_decl_;
private: private:
bool SetupFunction(abi_dump::FunctionDecl *methodp, bool SetupFunction(abi_util::FunctionIR *methodp,
const std::string &source_file) const; 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 { class EnumDeclWrapper : public ABIWrapper {
public: public:
EnumDeclWrapper(clang::MangleContext *mangle_contextp, EnumDeclWrapper(
clang::ASTContext *ast_contextp, clang::MangleContext *mangle_contextp, clang::ASTContext *ast_contextp,
const clang::CompilerInstance *compiler_instance_p, const clang::CompilerInstance *compiler_instance_p,
const clang::EnumDecl *decl); 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: private:
const clang::EnumDecl *enum_decl_; const clang::EnumDecl *enum_decl_;
private: private:
bool SetupEnum(abi_dump::EnumDecl *enump, bool SetupEnum(abi_util::EnumTypeIR *type,
const std::string &source_file) const; const std::string &source_file);
bool SetupEnumFields(abi_dump::EnumDecl *enump) const;
bool SetupEnumFields(abi_util::EnumTypeIR *enump);
}; };
class GlobalVarDeclWrapper : public ABIWrapper { class GlobalVarDeclWrapper : public ABIWrapper {
public: public:
GlobalVarDeclWrapper(clang::MangleContext *mangle_contextp, GlobalVarDeclWrapper(
clang::ASTContext *ast_contextp, clang::MangleContext *mangle_contextp, clang::ASTContext *ast_contextp,
const clang::CompilerInstance *compiler_instance_p, const clang::CompilerInstance *compiler_instance_p,
const clang::VarDecl *decl); 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: private:
const clang::VarDecl *global_var_decl_; const clang::VarDecl *global_var_decl_;
private: private:
bool SetupGlobalVar(abi_dump::GlobalVarDecl *global_varp, bool SetupGlobalVar(abi_util::GlobalVarIR *global_varp,
const std::string &source_file) const; const std::string &source_file);
}; };
} //end namespace abi_wrapper } //end namespace abi_wrapper

View File

@@ -19,9 +19,6 @@
#include <clang/Tooling/Core/QualTypeNames.h> #include <clang/Tooling/Core/QualTypeNames.h>
#include <clang/Index/CodegenNameGenerator.h> #include <clang/Index/CodegenNameGenerator.h>
#include <google/protobuf/text_format.h>
#include <google/protobuf/io/zero_copy_stream_impl.h>
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
#include <string> #include <string>
@@ -33,83 +30,64 @@ using abi_wrapper::EnumDeclWrapper;
using abi_wrapper::GlobalVarDeclWrapper; using abi_wrapper::GlobalVarDeclWrapper;
HeaderASTVisitor::HeaderASTVisitor( HeaderASTVisitor::HeaderASTVisitor(
abi_dump::TranslationUnit *tu_ptr,
clang::MangleContext *mangle_contextp, clang::MangleContext *mangle_contextp,
clang::ASTContext *ast_contextp, clang::ASTContext *ast_contextp,
const clang::CompilerInstance *compiler_instance_p, const clang::CompilerInstance *compiler_instance_p,
const std::string &current_file_name, const std::string &current_file_name,
const std::set<std::string> &exported_headers, const std::set<std::string> &exported_headers,
const clang::Decl *tu_decl) const clang::Decl *tu_decl,
: tu_ptr_(tu_ptr), std::set<std::string> *type_cache,
mangle_contextp_(mangle_contextp), abi_util::IRDumper *ir_dumper)
: mangle_contextp_(mangle_contextp),
ast_contextp_(ast_contextp), ast_contextp_(ast_contextp),
cip_(compiler_instance_p), cip_(compiler_instance_p),
current_file_name_(current_file_name), current_file_name_(current_file_name),
exported_headers_(exported_headers), 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) { 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() || if (!decl->isThisDeclarationADefinition() ||
decl->getTypeForDecl()->isDependentType()) { decl->getTypeForDecl()->isDependentType() ||
decl->isAnonymousStructOrUnion() ||
!decl->hasNameForLinkage()) {
return true; return true;
} }
RecordDeclWrapper record_decl_wrapper( RecordDeclWrapper record_decl_wrapper(
mangle_contextp_, ast_contextp_, cip_, decl); mangle_contextp_, ast_contextp_, cip_, decl, type_cache_,
std::unique_ptr<abi_dump::RecordDecl> wrapped_record_decl = ir_dumper_, decl_to_source_file_cache_, "");
record_decl_wrapper.GetRecordDecl(); return 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;
} }
bool HeaderASTVisitor::VisitEnumDecl(const clang::EnumDecl *decl) { bool HeaderASTVisitor::VisitEnumDecl(const clang::EnumDecl *decl) {
if (!decl->isThisDeclarationADefinition() || if (!decl->isThisDeclarationADefinition() ||
decl->getTypeForDecl()->isDependentType()) { decl->getTypeForDecl()->isDependentType() ||
!decl->hasNameForLinkage()) {
return true; return true;
} }
EnumDeclWrapper enum_decl_wrapper( EnumDeclWrapper enum_decl_wrapper(
mangle_contextp_, ast_contextp_, cip_, decl); mangle_contextp_, ast_contextp_, cip_, decl, type_cache_,
std::unique_ptr<abi_dump::EnumDecl> wrapped_enum_decl = ir_dumper_, decl_to_source_file_cache_);
enum_decl_wrapper.GetEnumDecl(); return 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;
} }
static bool MutateFunctionWithLinkageName(abi_dump::TranslationUnit *tu_ptr, static bool MutateFunctionWithLinkageName(const abi_util::FunctionIR *function,
const abi_dump::FunctionDecl *fd, abi_util::IRDumper *ir_dumper,
std::string &linkage_name) { std::string &linkage_name) {
abi_dump::FunctionDecl *added_function_declp = tu_ptr->add_functions(); auto added_function = std::make_unique<abi_util::FunctionIR>();
if (!added_function_declp) { *added_function = *function;
return false; added_function->SetLinkerSetKey(linkage_name);
} return ir_dumper->AddLinkableMessageIR(added_function.get());
*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;
} }
static bool AddMangledFunctions(abi_dump::TranslationUnit *tu_ptr, static bool AddMangledFunctions(const abi_util::FunctionIR *function,
const abi_dump::FunctionDecl *fd, abi_util:: IRDumper *ir_dumper,
std::vector<std::string> &manglings) { std::vector<std::string> &manglings) {
for (auto &&mangling : manglings) { for (auto &&mangling : manglings) {
if (!MutateFunctionWithLinkageName(tu_ptr, fd, mangling)) { if (!MutateFunctionWithLinkageName(function, ir_dumper, mangling)) {
return false; return false;
} }
} }
@@ -139,23 +117,20 @@ bool HeaderASTVisitor::VisitFunctionDecl(const clang::FunctionDecl *decl) {
return true; return true;
} }
FunctionDeclWrapper function_decl_wrapper(mangle_contextp_, ast_contextp_, FunctionDeclWrapper function_decl_wrapper(mangle_contextp_, ast_contextp_,
cip_, decl); cip_, decl, type_cache_,
std::unique_ptr<abi_dump::FunctionDecl> wrapped_function_decl = ir_dumper_,
function_decl_wrapper.GetFunctionDecl(); decl_to_source_file_cache_);
if (!wrapped_function_decl) { auto function_wrapper = function_decl_wrapper.GetFunctionDecl();
llvm::errs() << "Getting Function Decl failed\n";
return false;
}
// Destructors and Constructors can have more than 1 symbol generated from the // Destructors and Constructors can have more than 1 symbol generated from the
// same Decl. // same Decl.
clang::index::CodegenNameGenerator cg(*ast_contextp_); clang::index::CodegenNameGenerator cg(*ast_contextp_);
std::vector<std::string> manglings = cg.getAllManglings(decl); std::vector<std::string> manglings = cg.getAllManglings(decl);
if (!manglings.empty()) { if (!manglings.empty()) {
return AddMangledFunctions(tu_ptr_, wrapped_function_decl.get(), manglings); return AddMangledFunctions(function_wrapper.get(), ir_dumper_, manglings);
} }
std::string linkage_name = std::string linkage_name =
ABIWrapper::GetMangledNameDecl(decl, mangle_contextp_); ABIWrapper::GetMangledNameDecl(decl, mangle_contextp_);
return MutateFunctionWithLinkageName(tu_ptr_, wrapped_function_decl.get(), return MutateFunctionWithLinkageName(function_wrapper.get(), ir_dumper_,
linkage_name); linkage_name);
} }
@@ -166,19 +141,10 @@ bool HeaderASTVisitor::VisitVarDecl(const clang::VarDecl *decl) {
return true; return true;
} }
GlobalVarDeclWrapper global_var_decl_wrapper(mangle_contextp_, ast_contextp_, GlobalVarDeclWrapper global_var_decl_wrapper(mangle_contextp_, ast_contextp_,
cip_, decl); cip_, decl, type_cache_,
std::unique_ptr<abi_dump::GlobalVarDecl> wrapped_global_var_decl = ir_dumper_,
global_var_decl_wrapper.GetGlobalVarDecl(); decl_to_source_file_cache_);
if (!wrapped_global_var_decl) { return global_var_decl_wrapper.GetGlobalVarDecl();
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;
} }
static bool AreHeadersExported(const std::set<std::string> &exported_headers) { static bool AreHeadersExported(const std::set<std::string> &exported_headers) {
@@ -191,6 +157,7 @@ bool HeaderASTVisitor::TraverseDecl(clang::Decl *decl) {
return true; return true;
} }
std::string source_file = ABIWrapper::GetDeclSourceFile(decl, cip_); 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 no exported headers are specified we assume the whole AST is exported.
if ((decl != tu_decl_) && AreHeadersExported(exported_headers_) && if ((decl != tu_decl_) && AreHeadersExported(exported_headers_) &&
(exported_headers_.find(source_file) == exported_headers_.end())) { (exported_headers_.find(source_file) == exported_headers_.end())) {
@@ -210,18 +177,19 @@ HeaderASTConsumer::HeaderASTConsumer(
exported_headers_(exported_headers) { } exported_headers_(exported_headers) { }
void HeaderASTConsumer::HandleTranslationUnit(clang::ASTContext &ctx) { void HeaderASTConsumer::HandleTranslationUnit(clang::ASTContext &ctx) {
GOOGLE_PROTOBUF_VERIFY_VERSION; clang::PrintingPolicy policy(ctx.getPrintingPolicy());
std::ofstream text_output(out_dump_name_); policy.SuppressTagKeyword = true;
google::protobuf::io::OstreamOutputStream text_os(&text_output); ctx.setPrintingPolicy(policy);
clang::TranslationUnitDecl *translation_unit = ctx.getTranslationUnitDecl(); clang::TranslationUnitDecl *translation_unit = ctx.getTranslationUnitDecl();
std::unique_ptr<clang::MangleContext> mangle_contextp( std::unique_ptr<clang::MangleContext> mangle_contextp(
ctx.createMangleContext()); ctx.createMangleContext());
abi_dump::TranslationUnit tu; std::set<std::string> type_cache;
std::string str_out; std::unique_ptr<abi_util::IRDumper> ir_dumper =
HeaderASTVisitor v(&tu, mangle_contextp.get(), &ctx, cip_, file_name_, abi_util::IRDumper::CreateIRDumper("protobuf", out_dump_name_);
exported_headers_, translation_unit); HeaderASTVisitor v(mangle_contextp.get(), &ctx, cip_, file_name_,
if (!v.TraverseDecl(translation_unit) || exported_headers_, translation_unit, &type_cache,
!google::protobuf::TextFormat::Print(tu, &text_os)) { ir_dumper.get());
if (!v.TraverseDecl(translation_unit) || !ir_dumper->Dump()) {
llvm::errs() << "Serialization to ostream failed\n"; llvm::errs() << "Serialization to ostream failed\n";
::exit(1); ::exit(1);
} }

View File

@@ -21,6 +21,8 @@
#include "proto/abi_dump.pb.h" #include "proto/abi_dump.pb.h"
#pragma clang diagnostic pop #pragma clang diagnostic pop
#include <ir_representation.h>
#include <clang/AST/AST.h> #include <clang/AST/AST.h>
#include <clang/AST/ASTConsumer.h> #include <clang/AST/ASTConsumer.h>
#include <clang/AST/Mangle.h> #include <clang/AST/Mangle.h>
@@ -33,13 +35,14 @@
class HeaderASTVisitor class HeaderASTVisitor
: public clang::RecursiveASTVisitor<HeaderASTVisitor> { : public clang::RecursiveASTVisitor<HeaderASTVisitor> {
public: public:
HeaderASTVisitor(abi_dump::TranslationUnit *tu_ptr, HeaderASTVisitor(clang::MangleContext *mangle_contextp,
clang::MangleContext *mangle_contextp,
clang::ASTContext *ast_contextp, clang::ASTContext *ast_contextp,
const clang::CompilerInstance *compiler_instance_p, const clang::CompilerInstance *compiler_instance_p,
const std::string &current_file_name, const std::string &current_file_name,
const std::set<std::string> &exported_headers, 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); bool VisitRecordDecl(const clang::RecordDecl *decl);
@@ -57,7 +60,6 @@ class HeaderASTVisitor
} }
private: private:
abi_dump::TranslationUnit *tu_ptr_;
clang::MangleContext *mangle_contextp_; clang::MangleContext *mangle_contextp_;
clang::ASTContext *ast_contextp_; clang::ASTContext *ast_contextp_;
const clang::CompilerInstance *cip_; const clang::CompilerInstance *cip_;
@@ -65,6 +67,11 @@ class HeaderASTVisitor
const std::set<std::string> &exported_headers_; const std::set<std::string> &exported_headers_;
// To optimize recursion into only exported abi. // To optimize recursion into only exported abi.
const clang::Decl *tu_decl_; 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 { class HeaderASTConsumer : public clang::ASTConsumer {

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, "no-filter", llvm::cl::desc("Do not filter any abi"), llvm::cl::Optional,
llvm::cl::cat(header_linker_category)); 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( 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)); llvm::cl::cat(header_linker_category));
class HeaderAbiLinker { class HeaderAbiLinker {
@@ -85,16 +91,20 @@ class HeaderAbiLinker {
bool LinkAndDump(); 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: private:
bool LinkRecords(const abi_dump::TranslationUnit &dump_tu, bool LinkTypes(const abi_dump::TranslationUnit &dump_tu,
abi_dump::TranslationUnit *linked_tu); abi_dump::TranslationUnit *linked_tu);
bool LinkFunctions(const abi_dump::TranslationUnit &dump_tu, bool LinkFunctions(const abi_dump::TranslationUnit &dump_tu,
abi_dump::TranslationUnit *linked_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, bool LinkGlobalVars(const abi_dump::TranslationUnit &dump_tu,
abi_dump::TranslationUnit *linked_tu); abi_dump::TranslationUnit *linked_tu);
@@ -110,6 +120,8 @@ class HeaderAbiLinker {
bool ParseSoFile(); bool ParseSoFile();
bool AddElfSymbols(abi_dump::TranslationUnit *linked_tu);
private: private:
const std::vector<std::string> &dump_files_; const std::vector<std::string> &dump_files_;
const std::vector<std::string> &exported_header_dirs_; const std::vector<std::string> &exported_header_dirs_;
@@ -120,9 +132,8 @@ class HeaderAbiLinker {
const std::string &api_; const std::string &api_;
// TODO: Add to a map of std::sets instead. // TODO: Add to a map of std::sets instead.
std::set<std::string> exported_headers_; 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> function_decl_set_;
std::set<std::string> enum_decl_set_;
std::set<std::string> globvar_decl_set_; std::set<std::string> globvar_decl_set_;
// Version Script Regex Matching. // Version Script Regex Matching.
std::set<std::string> functions_regex_matched_set; std::set<std::string> functions_regex_matched_set;
@@ -132,12 +143,32 @@ class HeaderAbiLinker {
std::regex globvars_vs_regex_; 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() { bool HeaderAbiLinker::LinkAndDump() {
abi_dump::TranslationUnit linked_tu; abi_dump::TranslationUnit linked_tu;
std::ofstream text_output(out_dump_name_); std::ofstream text_output(out_dump_name_);
google::protobuf::io::OstreamOutputStream text_os(&text_output); google::protobuf::io::OstreamOutputStream text_os(&text_output);
// If a version script is available, we use that as a filter. // If the user specifies that a version script should be used, use that.
if (version_script.empty()) { if (!use_version_script) {
exported_headers_ = exported_headers_ =
abi_util::CollectAllExportedHeaders(exported_header_dirs_); abi_util::CollectAllExportedHeaders(exported_header_dirs_);
if (!ParseSoFile()) { if (!ParseSoFile()) {
@@ -149,20 +180,20 @@ bool HeaderAbiLinker::LinkAndDump() {
return false; return false;
} }
AddElfSymbols(&linked_tu);
for (auto &&i : dump_files_) { for (auto &&i : dump_files_) {
abi_dump::TranslationUnit dump_tu; abi_dump::TranslationUnit dump_tu;
std::ifstream input(i); std::ifstream input(i);
google::protobuf::io::IstreamInputStream text_is(&input); google::protobuf::io::IstreamInputStream text_is(&input);
if (!google::protobuf::TextFormat::Parse(&text_is, &dump_tu) || if (!google::protobuf::TextFormat::Parse(&text_is, &dump_tu) ||
!LinkRecords(dump_tu, &linked_tu) || !LinkTypes(dump_tu, &linked_tu) ||
!LinkFunctions(dump_tu, &linked_tu) || !LinkFunctions(dump_tu, &linked_tu) ||
!LinkEnums(dump_tu, &linked_tu) ||
!LinkGlobalVars(dump_tu, &linked_tu)) { !LinkGlobalVars(dump_tu, &linked_tu)) {
llvm::errs() << "Failed to link elements\n"; llvm::errs() << "Failed to link elements\n";
return false; return false;
} }
} }
if (!google::protobuf::TextFormat::Print(linked_tu, &text_os)) { if (!google::protobuf::TextFormat::Print(linked_tu, &text_os)) {
llvm::errs() << "Serialization to ostream failed\n"; llvm::errs() << "Serialization to ostream failed\n";
return false; return false;
@@ -170,22 +201,6 @@ bool HeaderAbiLinker::LinkAndDump() {
return true; 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, static bool QueryRegexMatches(std::set<std::string> *regex_matched_link_set,
const std::regex *vs_regex, const std::regex *vs_regex,
const std::string &symbol) { const std::string &symbol) {
@@ -219,10 +234,10 @@ static std::regex CreateRegexMatchExprFromSet(
return std::regex(all_regex_match_str); return std::regex(all_regex_match_str);
} }
//TODO: make linking decls multi-threaded b/63590537.
template <typename T> template <typename T>
inline bool HeaderAbiLinker::LinkDecl( inline bool HeaderAbiLinker::LinkDecl(
google::protobuf::RepeatedPtrField<T> *dst, google::protobuf::RepeatedPtrField<T> *dst, std::set<std::string> *link_set,
std::set<std::string> *link_set,
std::set<std::string> *regex_matched_link_set, const std::regex *vs_regex, std::set<std::string> *regex_matched_link_set, const std::regex *vs_regex,
const google::protobuf::RepeatedPtrField<T> &src, bool use_version_script) { const google::protobuf::RepeatedPtrField<T> &src, bool use_version_script) {
assert(dst != nullptr); assert(dst != nullptr);
@@ -230,18 +245,20 @@ inline bool HeaderAbiLinker::LinkDecl(
for (auto &&element : src) { for (auto &&element : src) {
// If we are not using a version script and exported headers are available, // If we are not using a version script and exported headers are available,
// filter out unexported abi. // filter out unexported abi.
if (!exported_headers_.empty() && std::string source_file = GetSourceFile(element);
exported_headers_.find(element.source_file()) == // Builtin types will not have source file information.
if (!exported_headers_.empty() && !source_file.empty() &&
exported_headers_.find(source_file) ==
exported_headers_.end()) { exported_headers_.end()) {
continue; continue;
} }
std::string element_str = GetLinkageName(element);
// Check for the existence of the element in linked dump / symbol file. // Check for the existence of the element in linked dump / symbol file.
if (!use_version_script) { if (!use_version_script) {
if (!link_set->insert(element.basic_abi().linker_set_key()).second) { if (!link_set->insert(element_str).second) {
continue; continue;
} }
} else { } else {
std::string element_str = GetSymbol(element);
std::set<std::string>::iterator it = std::set<std::string>::iterator it =
link_set->find(element_str); link_set->find(element_str);
if (it == link_set->end()) { if (it == link_set->end()) {
@@ -263,13 +280,52 @@ inline bool HeaderAbiLinker::LinkDecl(
return true; return true;
} }
bool HeaderAbiLinker::LinkRecords(const abi_dump::TranslationUnit &dump_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) { abi_dump::TranslationUnit *linked_tu) {
assert(linked_tu != nullptr); assert(linked_tu != nullptr);
// Even if version scripts are available we take in records, since the symbols // Even if version scripts are available we take in types, since the symbols
// in the version script might reference a record exposed by the library. // in the version script might reference a type exposed by the library.
return LinkDecl(linked_tu->mutable_records(), &record_decl_set_, nullptr, return LinkDecl(linked_tu->mutable_record_types(), &types_set_, nullptr,
nullptr, dump_tu.records(), false); 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, 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())); (!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, bool HeaderAbiLinker::LinkGlobalVars(const abi_dump::TranslationUnit &dump_tu,
abi_dump::TranslationUnit *linked_tu) { abi_dump::TranslationUnit *linked_tu) {
assert(linked_tu != nullptr); assert(linked_tu != nullptr);

View File

@@ -18,6 +18,7 @@
#include <llvm/Support/Endian.h> #include <llvm/Support/Endian.h>
#include <llvm/Support/raw_ostream.h> #include <llvm/Support/raw_ostream.h>
#include <map>
#include <regex> #include <regex>
#include <set> #include <set>
#include <string> #include <string>
@@ -128,4 +129,60 @@ class ELFSoFileParser : public SoFileParser {
bool IsSymbolExported(const Elf_Sym *elf_sym) const; 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 } // 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; package abi_diff;
message RecordFieldDeclDiff { message TypeInfo {
optional abi_dump.RecordFieldDecl old = 1; optional uint64 size = 1;
optional abi_dump.RecordFieldDecl new = 2; optional uint32 alignment = 2;
optional uint32 index = 3;
} }
message EnumFieldDeclDiff { message TypeInfoDiff {
optional abi_dump.EnumFieldDecl old = 1; optional TypeInfo old_type_info = 1;
optional abi_dump.EnumFieldDecl new = 2; optional TypeInfo new_type_info = 2;
optional uint32 index = 3; }
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 { message CXXBaseSpecifierDiff {
optional abi_dump.CXXBaseSpecifier old = 1; repeated abi_dump.CXXBaseSpecifier old_bases = 1;
optional abi_dump.CXXBaseSpecifier new = 2; repeated abi_dump.CXXBaseSpecifier new_bases = 2;
optional uint32 index = 3;
} }
message CXXVTableDiff { message RecordTypeDiff {
optional abi_dump.VTableComponent old = 1; optional string name = 1;
optional abi_dump.VTableComponent new = 2; optional string type_stack = 2;
optional uint32 index = 3; 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 { message UnderlyingTypeDiff {
optional abi_dump.BasicNamedAndTypedDecl old = 1; optional string old_type = 1;
optional abi_dump.BasicNamedAndTypedDecl new = 2; optional string new_type = 2;
} }
message RecordDeclDiff { message EnumFieldDeclDiff {
repeated RecordFieldDeclDiff field_diffs = 1; optional abi_dump.EnumFieldDecl old_field = 1;
repeated CXXBaseSpecifierDiff base_diffs = 2; optional abi_dump.EnumFieldDecl new_field = 2;
repeated CXXVTableDiff vtable_diffs = 3;
optional BasicNamedAndTypedDeclDiff type_diff = 4;
optional string name = 5;
} }
message EnumDeclDiff { message EnumTypeDiff {
repeated EnumFieldDeclDiff field_diffs = 1; optional string type_stack = 1;
optional BasicNamedAndTypedDeclDiff type_diff = 2; optional string name = 2;
optional string name = 3; repeated EnumFieldDeclDiff fields_diff = 3;
} optional UnderlyingTypeDiff underlying_type_diff = 4;
repeated abi_dump.EnumFieldDecl fields_added = 5;
message ReturnTypeDiff { repeated abi_dump.EnumFieldDecl fields_removed = 6;
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 FunctionDeclDiff { message FunctionDeclDiff {
optional ReturnTypeDiff return_type_diffs = 1; // Template diffs are not required since in C++, they will show up in mangled
repeated ParamDeclDiff param_diffs = 2; // names and in C, templates are not supported.
optional string name = 3; optional string type_stack = 1;
optional string name = 2;
optional abi_dump.FunctionDecl old = 3;
optional abi_dump.FunctionDecl new = 4;
} }
message GlobalVarDeclDiff { message GlobalVarDeclDiff {
optional BasicNamedAndTypedDeclDiff type_diff = 1; optional string type_stack = 1;
} optional string name = 2;
optional abi_dump.GlobalVarDecl old = 3; // Old global var
enum CompatibilityStatus { optional abi_dump.GlobalVarDecl new = 4; // New global var
COMPATIBLE = 0;
EXTENSION = 1;
INCOMPATIBLE = 4;
} }
message TranslationUnitDiff { message TranslationUnitDiff {
// Library Name // Library Name
optional string lib_name = 1; optional string lib_name = 1;
optional string arch = 2; optional string arch = 2;
// Differing Elements. // Records.
repeated RecordDeclDiff records_diff = 3; repeated RecordTypeDiff record_type_diffs = 3;
repeated EnumDeclDiff enums_diff = 4; repeated RecordTypeDiff unreferenced_record_type_diffs = 4;
repeated FunctionDeclDiff functions_diff = 5; repeated abi_dump.RecordType unreferenced_record_types_removed = 5;
repeated GlobalVarDeclDiff global_vars_diff = 6; repeated abi_dump.RecordType unreferenced_record_types_added = 6;
// Removed Elements.
repeated abi_dump.RecordDecl records_removed = 7; // Enums
repeated abi_dump.FunctionDecl functions_removed = 8; repeated EnumTypeDiff enum_type_diffs = 7;
repeated abi_dump.EnumDecl enums_removed = 9; repeated EnumTypeDiff enum_type_extension_diffs = 8;
repeated abi_dump.GlobalVarDecl global_vars_removed = 10; repeated EnumTypeDiff unreferenced_enum_type_diffs = 9;
// Added Elements. repeated EnumTypeDiff unreferenced_enum_type_extension_diffs = 10;
repeated abi_dump.RecordDecl records_added = 11; repeated abi_dump.EnumType unreferenced_enum_types_removed = 11;
repeated abi_dump.FunctionDecl functions_added = 12; repeated abi_dump.EnumType unreferenced_enum_types_added = 12;
repeated abi_dump.EnumDecl enums_added = 13;
repeated abi_dump.GlobalVarDecl global_vars_added = 14; // 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 // Compatiblity Status
optional CompatibilityStatus compatibility_status = 15; optional CompatibilityStatus compatibility_status = 23;
} }
// Not merged with TranslationUnitDiff to allow future extensions. // Not merged with TranslationUnitDiff to allow future extensions.
@@ -108,3 +123,9 @@ message ConciseDiffReportInformation {
message MergedTranslationUnitDiff { message MergedTranslationUnitDiff {
repeated ConciseDiffReportInformation diff_reports = 1; repeated ConciseDiffReportInformation diff_reports = 1;
} }
enum CompatibilityStatus {
COMPATIBLE = 0;
EXTENSION = 1;
INCOMPATIBLE = 4;
}

View File

@@ -2,50 +2,84 @@ syntax = "proto2";
package abi_dump; 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 { enum AccessSpecifier {
public_access = 1; public_access = 1;
private_access = 2; private_access = 2;
protected_access = 3; protected_access = 3;
} }
enum RecordKind {
struct_kind = 1;
class_kind = 2;
union_kind = 3;
}
message BasicNamedAndTypedDecl { message BasicNamedAndTypedDecl {
optional BasicTypeAbi type_abi = 1;
// The TypedDecl's name. // The TypedDecl's name.
optional string name = 2; optional string name = 1;
optional AccessSpecifier access = 3; optional uint64 size = 2 [default = 0];
optional string linker_set_key = 4; 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 { message FunctionDecl {
optional BasicNamedAndTypedDecl basic_abi = 1; // Return type reference
// Mangled name. optional string return_type = 1;
optional string mangled_function_name = 2; optional string function_name = 2;
optional string source_file = 3; optional string source_file = 3;
repeated ParamDecl parameters = 4; repeated ParamDecl parameters = 4;
optional TemplateInfo template_info = 5; optional TemplateInfo template_info = 5;
optional string linker_set_key = 6;
optional AccessSpecifier access = 7 [default = public_access];
} }
message ParamDecl { message ParamDecl {
optional BasicNamedAndTypedDecl basic_abi = 1; optional string referenced_type = 1;
optional bool default_arg = 2; optional bool default_arg = 2;
} }
message RecordFieldDecl { message RecordFieldDecl {
// For future additions. // 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 { message EnumFieldDecl {
optional BasicNamedAndTypedDecl basic_abi = 1; optional int64 enum_field_value = 1; // assumption: fits int64
optional int64 enum_field_value = 2; // assumption: fits int64 optional string name = 3;
} }
message TemplateInfo { message TemplateInfo {
@@ -53,17 +87,13 @@ message TemplateInfo {
} }
message TemplateElement { message TemplateElement {
optional BasicTemplateElementAbi basic_abi = 1; optional string referenced_type = 1;
message BasicTemplateElementAbi {
optional BasicTypeAbi type_abi = 1;
optional string name = 2;
optional string linker_set_key = 3;
}
} }
message CXXBaseSpecifier { message CXXBaseSpecifier {
optional BasicNamedAndTypedDecl basic_abi = 1; optional string referenced_type = 1;
optional bool is_virtual = 2; optional bool is_virtual = 2;
optional AccessSpecifier access = 3;
} }
message VTableComponent { message VTableComponent {
@@ -89,30 +119,51 @@ message VTableLayout {
repeated VTableComponent vtable_components = 1; repeated VTableComponent vtable_components = 1;
} }
message RecordDecl { message RecordType {
optional BasicNamedAndTypedDecl basic_abi = 1; optional BasicNamedAndTypedDecl type_info = 1;
repeated RecordFieldDecl fields = 2; repeated RecordFieldDecl fields = 2;
repeated CXXBaseSpecifier base_specifiers = 3; repeated CXXBaseSpecifier base_specifiers = 3;
optional string source_file = 4;
optional TemplateInfo template_info = 5; optional TemplateInfo template_info = 5;
optional string mangled_record_name = 6;
optional VTableLayout vtable_layout = 7; 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 { message EnumType {
optional BasicNamedAndTypedDecl basic_abi = 1; optional BasicNamedAndTypedDecl type_info = 1;
repeated EnumFieldDecl enum_fields = 2; optional string underlying_type = 2;
optional string source_file = 3; repeated EnumFieldDecl enum_fields = 3;
optional AccessSpecifier access = 4 [default = public_access];
} }
message GlobalVarDecl { message GlobalVarDecl {
optional BasicNamedAndTypedDecl basic_abi = 1; optional string name = 1;
optional string source_file = 2; 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 { message TranslationUnit {
repeated RecordDecl records = 1; repeated RecordType record_types = 1;
repeated FunctionDecl functions = 2; repeated EnumType enum_types = 2;
repeated EnumDecl enums = 3; repeated PointerType pointer_types = 3;
repeated GlobalVarDecl global_vars = 4; 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" { extern "C" {
#endif #endif
struct ForwardDeclaration;
int uses_forward_decl(struct ForwardDeclaration *);
struct Hello { struct Hello {
int foo; int foo;
int bar; int bar;
enum {A, B} enum_field;
enum {C, D} enum_field2;
struct {
int a;
int b;
struct {
int c;
};
};
}; };
#if defined(__cplusplus) #if defined(__cplusplus)
@@ -24,8 +36,17 @@ struct CPPHello : private HelloAgain, public ByeAgain<float_type> {
cfloat_type cpp_bar; cfloat_type cpp_bar;
virtual int again() { return 0; } virtual int again() { return 0; }
CPPHello() : cpp_foo(20), cpp_bar(1.234) { } 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> template<typename T>
struct StackNode { struct StackNode {
public: public:
@@ -63,7 +84,7 @@ public:
template<typename T> template<typename T>
class List class List
{ {
protected: public:
/* /*
* One element in the list. * One element in the list.
*/ */
@@ -74,6 +95,7 @@ protected:
inline T& getRef() { return mVal; } inline T& getRef() { return mVal; }
inline const T& getRef() const { return mVal; } inline const T& getRef() const { return mVal; }
private: private:
void PrivateNode();
friend class List; friend class List;
friend class _ListIterator; friend class _ListIterator;
T mVal; T mVal;
@@ -83,11 +105,20 @@ protected:
_Node *middle; _Node *middle;
}; };
typedef List<float> float_list; typedef List<float> float_list;
float_list float_list_test; float_list float_list_test;
typedef List<int> int_list; typedef List<int> int_list;
int_list int_list_test; 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_ #endif // EXAMPLE1_H_

View File

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