Home | History | Annotate | Download | only in diff
      1 // Copyright (C) 2016 The Android Open Source Project
      2 //
      3 // Licensed under the Apache License, Version 2.0 (the "License");
      4 // you may not use this file except in compliance with the License.
      5 // You may obtain a copy of the License at
      6 //
      7 //      http://www.apache.org/licenses/LICENSE-2.0
      8 //
      9 // Unless required by applicable law or agreed to in writing, software
     10 // distributed under the License is distributed on an "AS IS" BASIS,
     11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 // See the License for the specific language governing permissions and
     13 // limitations under the License.
     14 
     15 #include "diff/abi_diff.h"
     16 
     17 #include "utils/header_abi_util.h"
     18 
     19 #include <llvm/Support/raw_ostream.h>
     20 
     21 #include <memory>
     22 #include <string>
     23 #include <vector>
     24 
     25 #include <stdlib.h>
     26 
     27 
     28 namespace header_checker {
     29 namespace diff {
     30 
     31 
     32 repr::CompatibilityStatusIR HeaderAbiDiff::GenerateCompatibilityReport() {
     33   std::unique_ptr<repr::IRReader> old_reader =
     34       repr::IRReader::CreateIRReader(text_format_old_);
     35   std::unique_ptr<repr::IRReader> new_reader =
     36       repr::IRReader::CreateIRReader(text_format_new_);
     37   if (!old_reader || !new_reader || !old_reader->ReadDump(old_dump_) ||
     38       !new_reader->ReadDump(new_dump_)) {
     39     llvm::errs() << "Could not create Text Format readers\n";
     40     ::exit(1);
     41   }
     42   std::unique_ptr<repr::IRDiffDumper> ir_diff_dumper =
     43       repr::IRDiffDumper::CreateIRDiffDumper(text_format_diff_, cr_);
     44   repr::CompatibilityStatusIR status =
     45       CompareTUs(old_reader->GetModule(), new_reader->GetModule(),
     46                  ir_diff_dumper.get());
     47   if (!ir_diff_dumper->Dump()) {
     48     llvm::errs() << "Could not dump diff report\n";
     49     ::exit(1);
     50   }
     51   return status;
     52 }
     53 
     54 repr::CompatibilityStatusIR HeaderAbiDiff::CompareTUs(
     55     const repr::ModuleIR &old_tu, const repr::ModuleIR &new_tu,
     56     repr::IRDiffDumper *ir_diff_dumper) {
     57   // Collect all old and new types in maps, so that we can refer to them by
     58   // type name / linker_set_key later.
     59   const AbiElementMap<const repr::TypeIR *> old_types =
     60       old_tu.GetTypeGraph();
     61   const AbiElementMap<const repr::TypeIR *> new_types =
     62       new_tu.GetTypeGraph();
     63 
     64   // CollectDynsymExportables() fills in added, removed, unsafe, and safe function diffs.
     65   if (!CollectDynsymExportables(old_tu.GetFunctions(), new_tu.GetFunctions(),
     66                                 old_tu.GetElfFunctions(),
     67                                 new_tu.GetElfFunctions(),
     68                                 old_types, new_types,
     69                                 ir_diff_dumper) ||
     70       !CollectDynsymExportables(old_tu.GetGlobalVariables(),
     71                                 new_tu.GetGlobalVariables(),
     72                                 old_tu.GetElfObjects(),
     73                                 new_tu.GetElfObjects(),
     74                                 old_types, new_types,
     75                                 ir_diff_dumper)) {
     76     llvm::errs() << "Unable to collect dynsym exportables\n";
     77     ::exit(1);
     78   }
     79 
     80   // By the time this call is reached, all referenced types have been diffed.
     81   // So all additional calls on ir_diff_dumper get DiffKind::Unreferenced.
     82   if (check_all_apis_ && !CollectUserDefinedTypes(old_tu, new_tu, old_types,
     83                                                   new_types, ir_diff_dumper)) {
     84     llvm::errs() << "Unable to collect user defined types\n";
     85     ::exit(1);
     86   }
     87 
     88   repr::CompatibilityStatusIR combined_status =
     89       ir_diff_dumper->GetCompatibilityStatusIR();
     90 
     91   ir_diff_dumper->AddLibNameIR(lib_name_);
     92   ir_diff_dumper->AddArchIR(arch_);
     93   ir_diff_dumper->AddCompatibilityStatusIR(combined_status);
     94   return combined_status;
     95 }
     96 
     97 std::pair<AbiElementMap<const repr::EnumTypeIR *>,
     98           AbiElementMap<const repr::RecordTypeIR *>>
     99 HeaderAbiDiff::ExtractUserDefinedTypes(const repr::ModuleIR &tu) {
    100   AbiElementMap<const repr::EnumTypeIR *> enum_types;
    101   AbiElementMap<const repr::RecordTypeIR *> record_types;
    102   // Iterate through the ODRListMap, if there is more than 1 element in the
    103   // list, we cannot really unique the type by name, so skip it. If not, add a
    104   // map entry UniqueId -> const Record(Enum)TypeIR *.
    105   for (auto &it : tu.GetODRListMap()) {
    106     auto &odr_list = it.second;
    107     if (odr_list.size() != 1) {
    108       continue;
    109     }
    110     const repr::TypeIR *type = *(odr_list.begin());
    111     const repr::RecordTypeIR *record_type = nullptr;
    112     switch (type->GetKind()) {
    113       case repr::RecordTypeKind:
    114         record_type = static_cast<const repr::RecordTypeIR *>(type);
    115         if (record_type->IsAnonymous()) {
    116           continue;
    117         }
    118         record_types.emplace(
    119             record_type->GetUniqueId(), record_type);
    120         break;
    121       case repr::EnumTypeKind:
    122         enum_types.emplace(
    123             static_cast<const repr::EnumTypeIR *>(type)->GetUniqueId(),
    124             static_cast<const repr::EnumTypeIR *>(type));
    125         break;
    126       case repr::FunctionTypeKind:
    127         continue;
    128       default:
    129         // Only user defined types should have ODR list entries.
    130         assert(0);
    131     }
    132   }
    133   return std::make_pair(std::move(enum_types), std::move(record_types));
    134 }
    135 
    136 bool HeaderAbiDiff::CollectUserDefinedTypes(
    137     const repr::ModuleIR &old_tu, const repr::ModuleIR &new_tu,
    138     const AbiElementMap<const repr::TypeIR *> &old_types_map,
    139     const AbiElementMap<const repr::TypeIR *> &new_types_map,
    140     repr::IRDiffDumper *ir_diff_dumper) {
    141 
    142   auto old_enums_and_records_extracted = ExtractUserDefinedTypes(old_tu);
    143   auto new_enums_and_records_extracted = ExtractUserDefinedTypes(new_tu);
    144 
    145   return (CollectUserDefinedTypesInternal(
    146               old_enums_and_records_extracted.second,
    147               new_enums_and_records_extracted.second, old_types_map,
    148               new_types_map, ir_diff_dumper) &&
    149           CollectUserDefinedTypesInternal(
    150               old_enums_and_records_extracted.first,
    151               new_enums_and_records_extracted.first,
    152               old_types_map, new_types_map, ir_diff_dumper));
    153 }
    154 
    155 template <typename T>
    156 bool HeaderAbiDiff::CollectUserDefinedTypesInternal(
    157     const AbiElementMap<const T*> &old_ud_types_map,
    158     const AbiElementMap<const T*> &new_ud_types_map,
    159     const AbiElementMap<const repr::TypeIR *> &old_types_map,
    160     const AbiElementMap<const repr::TypeIR *> &new_types_map,
    161     repr::IRDiffDumper *ir_diff_dumper) {
    162 
    163   return (Collect(old_ud_types_map, new_ud_types_map, nullptr, nullptr,
    164                   ir_diff_dumper, old_types_map, new_types_map) &&
    165           PopulateCommonElements(old_ud_types_map, new_ud_types_map,
    166                                  old_types_map, new_types_map, ir_diff_dumper,
    167                                  repr::DiffMessageIR::Unreferenced));
    168 }
    169 
    170 template <typename T, typename ElfSymbolType>
    171 bool HeaderAbiDiff::CollectDynsymExportables(
    172     const AbiElementMap<T> &old_exportables,
    173     const AbiElementMap<T> &new_exportables,
    174     const AbiElementMap<ElfSymbolType> &old_elf_symbols,
    175     const AbiElementMap<ElfSymbolType> &new_elf_symbols,
    176     const AbiElementMap<const repr::TypeIR *> &old_types_map,
    177     const AbiElementMap<const repr::TypeIR *> &new_types_map,
    178     repr::IRDiffDumper *ir_diff_dumper) {
    179   AbiElementMap<const T *> old_exportables_map;
    180   AbiElementMap<const T *> new_exportables_map;
    181   AbiElementMap<const repr::ElfSymbolIR *> old_elf_symbol_map;
    182   AbiElementMap<const repr::ElfSymbolIR *> new_elf_symbol_map;
    183 
    184   utils::AddToMap(&old_exportables_map, old_exportables,
    185                   [](auto e) { return e->first;},
    186                   [](auto e) {return &(e->second);});
    187   utils::AddToMap(&new_exportables_map, new_exportables,
    188                   [](auto e) { return e->first;},
    189                   [](auto e) { return &(e->second);});
    190 
    191   utils::AddToMap(&old_elf_symbol_map, old_elf_symbols,
    192                   [](auto e) { return e->first;},
    193                   [](auto e) {return &(e->second);});
    194   utils::AddToMap(&new_elf_symbol_map, new_elf_symbols,
    195                   [](auto e) { return e->first;},
    196                   [](auto e) {return &(e->second);});
    197 
    198   if (!Collect(old_exportables_map,
    199                new_exportables_map, &old_elf_symbol_map, &new_elf_symbol_map,
    200                ir_diff_dumper, old_types_map, new_types_map) ||
    201       !CollectElfSymbols(old_elf_symbol_map, new_elf_symbol_map,
    202                          ir_diff_dumper) ||
    203       !PopulateCommonElements(old_exportables_map, new_exportables_map,
    204                               old_types_map, new_types_map, ir_diff_dumper,
    205                               repr::DiffMessageIR::Referenced)) {
    206     llvm::errs() << "Diffing dynsym exportables failed\n";
    207     return false;
    208   }
    209   return true;
    210 }
    211 
    212 // Collect the added and removed elements. The ELF maps are needed because the
    213 // metadata for some symbols might be absent from AST.  For example, if a
    214 // function Foo() is defined in an assembly file on target A, but in a C/C++
    215 // file on target B. Even though Foo() does not have metadata surrounding it
    216 // when building target A, it doesn't mean that Foo() is not a part of the ABI
    217 // of the library.
    218 template <typename T>
    219 bool HeaderAbiDiff::Collect(
    220     const AbiElementMap<const T*> &old_elements_map,
    221     const AbiElementMap<const T*> &new_elements_map,
    222     const AbiElementMap<const repr::ElfSymbolIR *> *old_elf_map,
    223     const AbiElementMap<const repr::ElfSymbolIR *> *new_elf_map,
    224     repr::IRDiffDumper *ir_diff_dumper,
    225     const AbiElementMap<const repr::TypeIR *> &old_types_map,
    226     const AbiElementMap<const repr::TypeIR *> &new_types_map) {
    227   if (!PopulateRemovedElements(
    228           old_elements_map, new_elements_map, old_elf_map, new_elf_map,
    229           ir_diff_dumper, repr::DiffMessageIR::Removed, old_types_map) ||
    230       !PopulateRemovedElements(
    231           new_elements_map, old_elements_map, new_elf_map, old_elf_map,
    232           ir_diff_dumper, repr::DiffMessageIR::Added, new_types_map)) {
    233     llvm::errs() << "Populating functions in report failed\n";
    234     return false;
    235   }
    236   return true;
    237 }
    238 
    239 bool HeaderAbiDiff::CollectElfSymbols(
    240     const AbiElementMap<const repr::ElfSymbolIR *> &old_symbols,
    241     const AbiElementMap<const repr::ElfSymbolIR *> &new_symbols,
    242     repr::IRDiffDumper *ir_diff_dumper) {
    243   std::vector<const repr::ElfSymbolIR *> removed_elements =
    244       utils::FindRemovedElements(old_symbols, new_symbols);
    245 
    246   std::vector<const repr::ElfSymbolIR *> added_elements =
    247       utils::FindRemovedElements(new_symbols, old_symbols);
    248 
    249   return (PopulateElfElements(removed_elements, ir_diff_dumper,
    250                               repr::IRDiffDumper::DiffKind::Removed) &&
    251           PopulateElfElements(added_elements, ir_diff_dumper,
    252                               repr::IRDiffDumper::DiffKind::Added));
    253 }
    254 
    255 bool HeaderAbiDiff::PopulateElfElements(
    256     std::vector<const repr::ElfSymbolIR *> &elf_elements,
    257     repr::IRDiffDumper *ir_diff_dumper,
    258     repr::IRDiffDumper::DiffKind diff_kind) {
    259   for (auto &&elf_element : elf_elements) {
    260     if (allow_adding_removing_weak_symbols_ &&
    261         elf_element->GetBinding() == repr::ElfSymbolIR::Weak) {
    262       continue;
    263     }
    264     if (!ir_diff_dumper->AddElfSymbolMessageIR(elf_element, diff_kind)) {
    265       return false;
    266     }
    267   }
    268   return true;
    269 }
    270 
    271 template <typename T>
    272 bool HeaderAbiDiff::PopulateRemovedElements(
    273     const AbiElementMap<const T*> &old_elements_map,
    274     const AbiElementMap<const T*> &new_elements_map,
    275     const AbiElementMap<const repr::ElfSymbolIR *> *old_elf_map,
    276     const AbiElementMap<const repr::ElfSymbolIR *> *new_elf_map,
    277     repr::IRDiffDumper *ir_diff_dumper,
    278     repr::IRDiffDumper::DiffKind diff_kind,
    279     const AbiElementMap<const repr::TypeIR *> &removed_types_map) {
    280   std::vector<const T *> removed_elements =
    281       utils::FindRemovedElements(old_elements_map, new_elements_map);
    282   if (!DumpLoneElements(removed_elements, old_elf_map, new_elf_map,
    283                         ir_diff_dumper, diff_kind, removed_types_map)) {
    284     llvm::errs() << "Dumping added or removed element to report failed\n";
    285     return false;
    286   }
    287   return true;
    288 }
    289 
    290 // Find the common elements (common records, common enums, common functions etc)
    291 // Dump the differences (we need type maps for this diff since we'll get
    292 // reachable types from here)
    293 template <typename T>
    294 bool HeaderAbiDiff::PopulateCommonElements(
    295     const AbiElementMap<const T *> &old_elements_map,
    296     const AbiElementMap<const T *> &new_elements_map,
    297     const AbiElementMap<const repr::TypeIR *> &old_types,
    298     const AbiElementMap<const repr::TypeIR *> &new_types,
    299     repr::IRDiffDumper *ir_diff_dumper,
    300     repr::IRDiffDumper::DiffKind diff_kind) {
    301   std::vector<std::pair<const T *, const T *>> common_elements =
    302       utils::FindCommonElements(old_elements_map, new_elements_map);
    303   if (!DumpDiffElements(common_elements, old_types, new_types,
    304                         ir_diff_dumper, diff_kind)) {
    305     llvm::errs() << "Dumping difference in common element to report failed\n";
    306     return false;
    307   }
    308   return true;
    309 }
    310 
    311 template <typename T>
    312 bool HeaderAbiDiff::DumpLoneElements(
    313     std::vector<const T *> &elements,
    314     const AbiElementMap<const repr::ElfSymbolIR *> *old_elf_map,
    315     const AbiElementMap<const repr::ElfSymbolIR *> *new_elf_map,
    316     repr::IRDiffDumper *ir_diff_dumper,
    317     repr::IRDiffDumper::DiffKind diff_kind,
    318     const AbiElementMap<const repr::TypeIR *> &types_map) {
    319   std::smatch source_file_match;
    320   std::regex source_file_regex(" at ");
    321 
    322   for (auto &&element : elements) {
    323     if (IgnoreSymbol<T>(element, ignored_symbols_,
    324                         [](const T *e) {return e->GetLinkerSetKey();})) {
    325       continue;
    326     }
    327 
    328     // If an element (FunctionIR or GlobalVarIR) is missing from the new ABI
    329     // dump but a corresponding ELF symbol (ElfFunctionIR or ElfObjectIR) can
    330     // be found in the new ABI dump file, don't emit error on this element.
    331     // This may happen when the standard reference target implements the
    332     // function (or the global variable) in C/C++ and the target-under-test
    333     // implements the function (or the global variable) in assembly.
    334     const std::string &element_linker_set_key = element->GetLinkerSetKey();
    335     if (new_elf_map &&
    336         new_elf_map->find(element_linker_set_key) != new_elf_map->end()) {
    337       continue;
    338     }
    339 
    340     // If the `-ignore-weak-symbols` option is enabled, ignore the element if
    341     // it was a weak symbol.
    342     if (allow_adding_removing_weak_symbols_ && old_elf_map) {
    343       auto elem_it = old_elf_map->find(element_linker_set_key);
    344       if (elem_it != old_elf_map->end() &&
    345           elem_it->second->GetBinding() == repr::ElfSymbolIR::Weak) {
    346         continue;
    347       }
    348     }
    349 
    350     // If the record / enum has source file information, skip it.
    351     if (std::regex_search(element_linker_set_key, source_file_match,
    352                           source_file_regex)) {
    353       continue;
    354     }
    355 
    356     auto element_copy = *element;
    357     ReplaceTypeIdsWithTypeNames(types_map, &element_copy);
    358     if (!ir_diff_dumper->AddLinkableMessageIR(&element_copy, diff_kind)) {
    359       llvm::errs() << "Couldn't dump added or removed element\n";
    360       return false;
    361     }
    362   }
    363   return true;
    364 }
    365 
    366 template <typename T>
    367 bool HeaderAbiDiff::DumpDiffElements(
    368     std::vector<std::pair<const T *,const T *>> &pairs,
    369     const AbiElementMap<const repr::TypeIR *> &old_types,
    370     const AbiElementMap<const repr::TypeIR *> &new_types,
    371     repr::IRDiffDumper *ir_diff_dumper,
    372     repr::IRDiffDumper::DiffKind diff_kind) {
    373   for (auto &&pair : pairs) {
    374     const T *old_element = pair.first;
    375     const T *new_element = pair.second;
    376 
    377     if (IgnoreSymbol<T>(old_element, ignored_symbols_,
    378                         [](const T *e) {return e->GetLinkerSetKey();})) {
    379       continue;
    380     }
    381 
    382     DiffWrapper<T> diff_wrapper(
    383         old_element, new_element, ir_diff_dumper, old_types, new_types,
    384         diff_policy_options_, &type_cache_);
    385     if (!diff_wrapper.DumpDiff(diff_kind)) {
    386       llvm::errs() << "Failed to diff elements\n";
    387       return false;
    388     }
    389   }
    390   return true;
    391 }
    392 
    393 
    394 }  // namespace diff
    395 }  // namespace header_checker
    396