• 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 "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 
GenerateCompatibilityReport()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 
CompareTUs(const repr::ModuleIR & old_tu,const repr::ModuleIR & new_tu,repr::IRDiffDumper * ir_diff_dumper)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 *>>
ExtractUserDefinedTypes(const repr::ModuleIR & tu)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 
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)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>
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)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>
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)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>
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)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 
CollectElfSymbols(const AbiElementMap<const repr::ElfSymbolIR * > & old_symbols,const AbiElementMap<const repr::ElfSymbolIR * > & new_symbols,repr::IRDiffDumper * ir_diff_dumper)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 
PopulateElfElements(std::vector<const repr::ElfSymbolIR * > & elf_elements,repr::IRDiffDumper * ir_diff_dumper,repr::IRDiffDumper::DiffKind diff_kind)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>
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)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>
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)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>
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)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>
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)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