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