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