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