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