blob: 7d21566d88ae46620d897bbbd47fbd94af88bbde [file] [log] [blame]
// Copyright (C) 2016 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "abi_diff.h"
#include <header_abi_util.h>
#include <llvm/Support/raw_ostream.h>
#include <google/protobuf/text_format.h>
#include <google/protobuf/io/zero_copy_stream_impl.h>
#include <memory>
#include <string>
#include <vector>
#include <stdlib.h>
abi_util::CompatibilityStatusIR HeaderAbiDiff::GenerateCompatibilityReport() {
using abi_util::TextFormatToIRReader;
std::unique_ptr<abi_util::TextFormatToIRReader> old_reader =
TextFormatToIRReader::CreateTextFormatToIRReader("protobuf", old_dump_);
std::unique_ptr<abi_util::TextFormatToIRReader> new_reader =
TextFormatToIRReader::CreateTextFormatToIRReader("protobuf", new_dump_);
if (!old_reader || !new_reader || !old_reader->ReadDump() ||
!new_reader->ReadDump()) {
llvm::errs() << "Could not create Text Format readers\n";
::exit(1);
}
std::unique_ptr<abi_util::IRDiffDumper> ir_diff_dumper =
abi_util::IRDiffDumper::CreateIRDiffDumper("protobuf", cr_);
abi_util::CompatibilityStatusIR status =
CompareTUs(old_reader.get(), new_reader.get(), ir_diff_dumper.get());
if (!ir_diff_dumper->Dump()) {
llvm::errs() << "Could not dump diff report\n";
::exit(1);
}
return status;
}
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;
}
bool HeaderAbiDiff::CollectUserDefinedTypes(
const abi_util::TextFormatToIRReader *old_tu,
const abi_util::TextFormatToIRReader *new_tu,
const std::map<std::string, const abi_util::TypeIR *> &old_types_map,
const std::map<std::string, const abi_util::TypeIR *> &new_types_map,
abi_util::IRDiffDumper *ir_diff_dumper) {
return CollectUserDefinedTypesInternal(
old_tu->GetRecordTypes(), new_tu->GetRecordTypes(), old_types_map,
new_types_map, ir_diff_dumper) &&
CollectUserDefinedTypesInternal(old_tu->GetEnumTypes(),
new_tu->GetEnumTypes(), old_types_map,
new_types_map, ir_diff_dumper);
}
template <typename T>
bool HeaderAbiDiff::CollectUserDefinedTypesInternal(
const std::vector<T> &old_ud_types,
const std::vector<T> &new_ud_types,
const std::map<std::string, const abi_util::TypeIR *> &old_types_map,
const std::map<std::string, const abi_util::TypeIR *> &new_types_map,
abi_util::IRDiffDumper *ir_diff_dumper) {
// No elf information for records and enums.
std::map<std::string, const T *> old_ud_types_map;
std::map<std::string, const T *> new_ud_types_map;
abi_util::AddToMap(&old_ud_types_map, old_ud_types,
[](const T *e)
{ return e->GetLinkerSetKey();});
abi_util::AddToMap(&new_ud_types_map, new_ud_types,
[](const T *e)
{ return e->GetLinkerSetKey();});
return Collect(old_ud_types_map, new_ud_types_map, nullptr, nullptr,
ir_diff_dumper) &&
PopulateCommonElements(old_ud_types_map, new_ud_types_map, old_types_map,
new_types_map, ir_diff_dumper,
abi_util::IRDiffDumper::Unreferenced);
}
template <typename T, typename ElfSymbolType>
bool HeaderAbiDiff::CollectDynsymExportables(
const std::vector<T> &old_exportables,
const std::vector<T> &new_exportables,
const std::vector<ElfSymbolType> &old_elf_symbols,
const std::vector<ElfSymbolType> &new_elf_symbols,
const std::map<std::string, const abi_util::TypeIR *> &old_types_map,
const std::map<std::string, const abi_util::TypeIR *> &new_types_map,
abi_util::IRDiffDumper *ir_diff_dumper) {
std::map<std::string, const T *> old_exportables_map;
std::map<std::string, const T *> new_exportables_map;
std::map<std::string, const abi_util::ElfSymbolIR *> old_elf_symbol_map;
std::map<std::string, const abi_util::ElfSymbolIR *> new_elf_symbol_map;
abi_util::AddToMap(&old_exportables_map, old_exportables,
[](const T *e)
{ return e->GetLinkerSetKey();});
abi_util::AddToMap(&new_exportables_map, new_exportables,
[](const T *e)
{ return e->GetLinkerSetKey();});
abi_util::AddToMap(
&old_elf_symbol_map, old_elf_symbols,
[](const ElfSymbolType *symbol) { return symbol->GetName();});
abi_util::AddToMap(
&new_elf_symbol_map, new_elf_symbols,
[](const ElfSymbolType *symbol) { return symbol->GetName();});
if (!Collect(old_exportables_map,
new_exportables_map, &old_elf_symbol_map, &new_elf_symbol_map,
ir_diff_dumper) ||
!CollectElfSymbols(old_elf_symbol_map, new_elf_symbol_map,
ir_diff_dumper) ||
!PopulateCommonElements(old_exportables_map, new_exportables_map,
old_types_map, new_types_map, ir_diff_dumper,
abi_util::IRDiffDumper::Referenced)) {
llvm::errs() << "Diffing dynsym exportables failed\n";
return false;
}
return true;
}
// Collect added and removed Elements. The elf set is needed since some symbols
// might not have meta-data about them collected through the AST. For eg: if a
// function Foo is defined in an assembly file on target A, but in a c/c++ file
// on target B, foo does not have meta-data surrounding it when building target
// A, this does not mean it is not in the ABI + API of the library.
template <typename T>
bool HeaderAbiDiff::Collect(
const std::map<std::string, const T*> &old_elements_map,
const std::map<std::string, const T*> &new_elements_map,
const std::map<std::string, const abi_util::ElfSymbolIR *> *old_elf_map,
const std::map<std::string, const abi_util::ElfSymbolIR *> *new_elf_map,
abi_util::IRDiffDumper *ir_diff_dumper) {
if (!PopulateRemovedElements(
old_elements_map, new_elements_map, new_elf_map, ir_diff_dumper,
abi_util::IRDiffDumper::Removed) ||
!PopulateRemovedElements(new_elements_map, old_elements_map, old_elf_map,
ir_diff_dumper,
abi_util::IRDiffDumper::DiffKind::Added)) {
llvm::errs() << "Populating functions in report failed\n";
return false;
}
return true;
}
bool HeaderAbiDiff::CollectElfSymbols(
const std::map<std::string, const abi_util::ElfSymbolIR *> &old_symbols,
const std::map<std::string, const abi_util::ElfSymbolIR *> &new_symbols,
abi_util::IRDiffDumper *ir_diff_dumper) {
std::vector<const abi_util::ElfSymbolIR *> removed_elements =
abi_util::FindRemovedElements(old_symbols, new_symbols);
std::vector<const abi_util::ElfSymbolIR *> added_elements =
abi_util::FindRemovedElements(new_symbols, old_symbols);
return PopulateElfElements(removed_elements, ir_diff_dumper,
abi_util::IRDiffDumper::DiffKind::Removed) &&
PopulateElfElements(added_elements, ir_diff_dumper,
abi_util::IRDiffDumper::DiffKind::Added);
}
bool HeaderAbiDiff::PopulateElfElements(
std::vector<const abi_util::ElfSymbolIR *> &elf_elements,
abi_util::IRDiffDumper *ir_diff_dumper,
abi_util::IRDiffDumper::DiffKind diff_kind) {
for (auto &&elf_element : elf_elements) {
if (!ir_diff_dumper->AddElfSymbolMessageIR(elf_element, diff_kind)) {
return false;
}
}
return true;
}
template <typename T>
bool HeaderAbiDiff::PopulateRemovedElements(
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 *> *elf_map,
abi_util::IRDiffDumper *ir_diff_dumper,
abi_util::IRDiffDumper::DiffKind diff_kind) {
std::vector<const T *> removed_elements =
abi_util::FindRemovedElements(old_elements_map, new_elements_map);
if (!DumpLoneElements(removed_elements, elf_map, ir_diff_dumper, diff_kind)) {
llvm::errs() << "Dumping added / removed element to report failed\n";
return false;
}
return true;
}
template <typename T>
bool HeaderAbiDiff::PopulateCommonElements(
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::TypeIR *> &old_types,
const std::map<std::string, const abi_util::TypeIR *> &new_types,
abi_util::IRDiffDumper *ir_diff_dumper,
abi_util::IRDiffDumper::DiffKind diff_kind) {
std::vector<std::pair<const T *, const T *>> common_elements =
abi_util::FindCommonElements(old_elements_map, new_elements_map);
if (!DumpDiffElements(common_elements, old_types, new_types,
ir_diff_dumper, diff_kind)) {
llvm::errs() << "Dumping difference in common element to report failed\n";
return false;
}
return true;
}
template <typename T>
bool HeaderAbiDiff::DumpLoneElements(
std::vector<const T *> &elements,
const std::map<std::string, const abi_util::ElfSymbolIR *> *elf_map,
abi_util::IRDiffDumper *ir_diff_dumper,
abi_util::IRDiffDumper::DiffKind diff_kind) {
// If the record / enum has source file information, skip it.
std::smatch source_file_match;
std::regex source_file_regex(" at ");
for (auto &&element : elements) {
if (abi_diff_wrappers::IgnoreSymbol<T>(
element, ignored_symbols_,
[](const T *e) {return e->GetLinkerSetKey();})) {
continue;
}
// The element does exist in the .dynsym table, we do not have meta-data
// surrounding the element.
const std::string &element_linker_set_key = element->GetLinkerSetKey();
if ((elf_map != nullptr) &&
(elf_map->find(element_linker_set_key) != elf_map->end())) {
continue;
}
if (std::regex_search(element_linker_set_key, source_file_match,
source_file_regex)) {
continue;
}
if (!ir_diff_dumper->AddLinkableMessageIR(element, diff_kind)) {
llvm::errs() << "Couldn't dump added /removed element\n";
return false;
}
}
return true;
}
template <typename T>
bool HeaderAbiDiff::DumpDiffElements(
std::vector<std::pair<const T *,const T *>> &pairs,
const std::map<std::string, const abi_util::TypeIR *> &old_types,
const std::map<std::string, const abi_util::TypeIR *> &new_types,
abi_util::IRDiffDumper *ir_diff_dumper,
abi_util::IRDiffDumper::DiffKind diff_kind) {
for (auto &&pair : pairs) {
const T *old_element = pair.first;
const T *new_element = pair.second;
if (abi_diff_wrappers::IgnoreSymbol<T>(
old_element, ignored_symbols_,
[](const T *e) {return e->GetLinkerSetKey();})) {
continue;
}
abi_diff_wrappers::DiffWrapper<T> diff_wrapper(old_element, new_element,
ir_diff_dumper, old_types,
new_types, &type_cache_);
if (!diff_wrapper.DumpDiff(diff_kind)) {
llvm::errs() << "Failed to diff elements\n";
return false;
}
}
return true;
}