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       continue;
332     }
333 
334     // If an element (FunctionIR or GlobalVarIR) is missing from the new ABI
335     // dump but a corresponding ELF symbol (ElfFunctionIR or ElfObjectIR) can
336     // be found in the new ABI dump file, don't emit error on this element.
337     // This may happen when the standard reference target implements the
338     // function (or the global variable) in C/C++ and the target-under-test
339     // implements the function (or the global variable) in assembly.
340     const std::string &element_linker_set_key = element->GetLinkerSetKey();
341     if (new_elf_map &&
342         new_elf_map->find(element_linker_set_key) != new_elf_map->end()) {
343       continue;
344     }
345 
346     // If the `-ignore-weak-symbols` option is enabled, ignore the element if
347     // it was a weak symbol.
348     if (allow_adding_removing_weak_symbols_ && old_elf_map) {
349       auto elem_it = old_elf_map->find(element_linker_set_key);
350       if (elem_it != old_elf_map->end() &&
351           elem_it->second->GetBinding() == repr::ElfSymbolIR::Weak) {
352         continue;
353       }
354     }
355 
356     // If the record / enum has source file information, skip it.
357     if (std::regex_search(element_linker_set_key, source_file_match,
358                           source_file_regex)) {
359       continue;
360     }
361 
362     auto element_copy = *element;
363     ReplaceTypeIdsWithTypeNames(types_map, &element_copy);
364     if (!ir_diff_dumper->AddLinkableMessageIR(&element_copy, diff_kind)) {
365       llvm::errs() << "Couldn't dump added or removed element\n";
366       return false;
367     }
368   }
369   return true;
370 }
371 
372 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)373 bool HeaderAbiDiff::DumpDiffElements(
374     std::vector<std::pair<const T *,const T *>> &pairs,
375     const AbiElementMap<const repr::TypeIR *> &old_types,
376     const AbiElementMap<const repr::TypeIR *> &new_types,
377     repr::IRDiffDumper *ir_diff_dumper,
378     repr::IRDiffDumper::DiffKind diff_kind) {
379   for (auto &&pair : pairs) {
380     const T *old_element = pair.first;
381     const T *new_element = pair.second;
382 
383     if (IgnoreSymbol<T>(old_element, ignored_symbols_,
384                         [](const T *e) {return e->GetLinkerSetKey();})) {
385       continue;
386     }
387 
388     DiffWrapper<T> diff_wrapper(
389         old_element, new_element, ir_diff_dumper, old_types, new_types,
390         diff_policy_options_, &type_cache_);
391     if (!diff_wrapper.DumpDiff(diff_kind)) {
392       llvm::errs() << "Failed to diff elements\n";
393       return false;
394     }
395   }
396   return true;
397 }
398 
399 
400 }  // namespace diff
401 }  // namespace header_checker
402