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