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