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