1 //===- DylibVerifier.cpp ----------------------------------------*- C++--*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "clang/InstallAPI/DylibVerifier.h" 10 #include "DiagnosticBuilderWrappers.h" 11 #include "clang/InstallAPI/FrontendRecords.h" 12 #include "clang/InstallAPI/InstallAPIDiagnostic.h" 13 #include "llvm/Demangle/Demangle.h" 14 #include "llvm/TextAPI/DylibReader.h" 15 16 using namespace llvm::MachO; 17 18 namespace clang { 19 namespace installapi { 20 21 /// Metadata stored about a mapping of a declaration to a symbol. 22 struct DylibVerifier::SymbolContext { 23 // Name to use for all querying and verification 24 // purposes. 25 std::string SymbolName{""}; 26 27 // Kind to map symbol type against record. 28 EncodeKind Kind = EncodeKind::GlobalSymbol; 29 30 // Frontend Attributes tied to the AST. 31 const FrontendAttrs *FA = nullptr; 32 33 // The ObjCInterface symbol type, if applicable. 34 ObjCIFSymbolKind ObjCIFKind = ObjCIFSymbolKind::None; 35 36 // Whether Decl is inlined. 37 bool Inlined = false; 38 }; 39 40 struct DylibVerifier::DWARFContext { 41 // Track whether DSYM parsing has already been attempted to avoid re-parsing. 42 bool ParsedDSYM{false}; 43 44 // Lookup table for source locations by symbol name. 45 DylibReader::SymbolToSourceLocMap SourceLocs{}; 46 }; 47 48 static bool isCppMangled(StringRef Name) { 49 // InstallAPI currently only supports itanium manglings. 50 return (Name.starts_with("_Z") || Name.starts_with("__Z") || 51 Name.starts_with("___Z")); 52 } 53 54 static std::string demangle(StringRef Name) { 55 // InstallAPI currently only supports itanium manglings. 56 if (!isCppMangled(Name)) 57 return Name.str(); 58 char *Result = llvm::itaniumDemangle(Name); 59 if (!Result) 60 return Name.str(); 61 62 std::string Demangled(Result); 63 free(Result); 64 return Demangled; 65 } 66 67 std::string DylibVerifier::getAnnotatedName(const Record *R, 68 SymbolContext &SymCtx, 69 bool ValidSourceLoc) { 70 assert(!SymCtx.SymbolName.empty() && "Expected symbol name"); 71 72 const StringRef SymbolName = SymCtx.SymbolName; 73 std::string PrettyName = 74 (Demangle && (SymCtx.Kind == EncodeKind::GlobalSymbol)) 75 ? demangle(SymbolName) 76 : SymbolName.str(); 77 78 std::string Annotation; 79 if (R->isWeakDefined()) 80 Annotation += "(weak-def) "; 81 if (R->isWeakReferenced()) 82 Annotation += "(weak-ref) "; 83 if (R->isThreadLocalValue()) 84 Annotation += "(tlv) "; 85 86 // Check if symbol represents only part of a @interface declaration. 87 switch (SymCtx.ObjCIFKind) { 88 default: 89 break; 90 case ObjCIFSymbolKind::EHType: 91 return Annotation + "Exception Type of " + PrettyName; 92 case ObjCIFSymbolKind::MetaClass: 93 return Annotation + "Metaclass of " + PrettyName; 94 case ObjCIFSymbolKind::Class: 95 return Annotation + "Class of " + PrettyName; 96 } 97 98 // Only print symbol type prefix or leading "_" if there is no source location 99 // tied to it. This can only ever happen when the location has to come from 100 // debug info. 101 if (ValidSourceLoc) { 102 StringRef PrettyNameRef(PrettyName); 103 if ((SymCtx.Kind == EncodeKind::GlobalSymbol) && 104 !isCppMangled(SymbolName) && PrettyNameRef.starts_with("_")) 105 return Annotation + PrettyNameRef.drop_front(1).str(); 106 return Annotation + PrettyName; 107 } 108 109 switch (SymCtx.Kind) { 110 case EncodeKind::GlobalSymbol: 111 return Annotation + PrettyName; 112 case EncodeKind::ObjectiveCInstanceVariable: 113 return Annotation + "(ObjC IVar) " + PrettyName; 114 case EncodeKind::ObjectiveCClass: 115 return Annotation + "(ObjC Class) " + PrettyName; 116 case EncodeKind::ObjectiveCClassEHType: 117 return Annotation + "(ObjC Class EH) " + PrettyName; 118 } 119 120 llvm_unreachable("unexpected case for EncodeKind"); 121 } 122 123 static DylibVerifier::Result updateResult(const DylibVerifier::Result Prev, 124 const DylibVerifier::Result Curr) { 125 if (Prev == Curr) 126 return Prev; 127 128 // Never update from invalid or noverify state. 129 if ((Prev == DylibVerifier::Result::Invalid) || 130 (Prev == DylibVerifier::Result::NoVerify)) 131 return Prev; 132 133 // Don't let an ignored verification remove a valid one. 134 if (Prev == DylibVerifier::Result::Valid && 135 Curr == DylibVerifier::Result::Ignore) 136 return Prev; 137 138 return Curr; 139 } 140 // __private_extern__ is a deprecated specifier that clang does not 141 // respect in all contexts, it should just be considered hidden for InstallAPI. 142 static bool shouldIgnorePrivateExternAttr(const Decl *D) { 143 if (const FunctionDecl *FD = cast<FunctionDecl>(D)) 144 return FD->getStorageClass() == StorageClass::SC_PrivateExtern; 145 if (const VarDecl *VD = cast<VarDecl>(D)) 146 return VD->getStorageClass() == StorageClass::SC_PrivateExtern; 147 148 return false; 149 } 150 151 Record *findRecordFromSlice(const RecordsSlice *Slice, StringRef Name, 152 EncodeKind Kind) { 153 switch (Kind) { 154 case EncodeKind::GlobalSymbol: 155 return Slice->findGlobal(Name); 156 case EncodeKind::ObjectiveCInstanceVariable: 157 return Slice->findObjCIVar(Name.contains('.'), Name); 158 case EncodeKind::ObjectiveCClass: 159 case EncodeKind::ObjectiveCClassEHType: 160 return Slice->findObjCInterface(Name); 161 } 162 llvm_unreachable("unexpected end when finding record"); 163 } 164 165 void DylibVerifier::updateState(Result State) { 166 Ctx.FrontendState = updateResult(Ctx.FrontendState, State); 167 } 168 169 void DylibVerifier::addSymbol(const Record *R, SymbolContext &SymCtx, 170 TargetList &&Targets) { 171 if (Targets.empty()) 172 Targets = {Ctx.Target}; 173 174 Exports->addGlobal(SymCtx.Kind, SymCtx.SymbolName, R->getFlags(), Targets); 175 } 176 177 bool DylibVerifier::shouldIgnoreObsolete(const Record *R, SymbolContext &SymCtx, 178 const Record *DR) { 179 if (!SymCtx.FA->Avail.isObsoleted()) 180 return false; 181 182 if (Zippered) 183 DeferredZipperedSymbols[SymCtx.SymbolName].emplace_back(ZipperedDeclSource{ 184 SymCtx.FA, &Ctx.Diag->getSourceManager(), Ctx.Target}); 185 return true; 186 } 187 188 bool DylibVerifier::shouldIgnoreReexport(const Record *R, 189 SymbolContext &SymCtx) const { 190 if (Reexports.empty()) 191 return false; 192 193 for (const InterfaceFile &Lib : Reexports) { 194 if (!Lib.hasTarget(Ctx.Target)) 195 continue; 196 if (auto Sym = 197 Lib.getSymbol(SymCtx.Kind, SymCtx.SymbolName, SymCtx.ObjCIFKind)) 198 if ((*Sym)->hasTarget(Ctx.Target)) 199 return true; 200 } 201 return false; 202 } 203 204 bool DylibVerifier::shouldIgnoreInternalZipperedSymbol( 205 const Record *R, const SymbolContext &SymCtx) const { 206 if (!Zippered) 207 return false; 208 209 return Exports->findSymbol(SymCtx.Kind, SymCtx.SymbolName, 210 SymCtx.ObjCIFKind) != nullptr; 211 } 212 213 bool DylibVerifier::shouldIgnoreZipperedAvailability(const Record *R, 214 SymbolContext &SymCtx) { 215 if (!(Zippered && SymCtx.FA->Avail.isUnavailable())) 216 return false; 217 218 // Collect source location incase there is an exported symbol to diagnose 219 // during `verifyRemainingSymbols`. 220 DeferredZipperedSymbols[SymCtx.SymbolName].emplace_back( 221 ZipperedDeclSource{SymCtx.FA, SourceManagers.back().get(), Ctx.Target}); 222 223 return true; 224 } 225 226 bool DylibVerifier::compareObjCInterfaceSymbols(const Record *R, 227 SymbolContext &SymCtx, 228 const ObjCInterfaceRecord *DR) { 229 const bool IsDeclVersionComplete = 230 ((SymCtx.ObjCIFKind & ObjCIFSymbolKind::Class) == 231 ObjCIFSymbolKind::Class) && 232 ((SymCtx.ObjCIFKind & ObjCIFSymbolKind::MetaClass) == 233 ObjCIFSymbolKind::MetaClass); 234 235 const bool IsDylibVersionComplete = DR->isCompleteInterface(); 236 237 // The common case, a complete ObjCInterface. 238 if (IsDeclVersionComplete && IsDylibVersionComplete) 239 return true; 240 241 auto PrintDiagnostic = [&](auto SymLinkage, const Record *Record, 242 StringRef SymName, bool PrintAsWarning = false) { 243 if (SymLinkage == RecordLinkage::Unknown) 244 Ctx.emitDiag([&]() { 245 Ctx.Diag->Report(SymCtx.FA->Loc, PrintAsWarning 246 ? diag::warn_library_missing_symbol 247 : diag::err_library_missing_symbol) 248 << SymName; 249 }); 250 else 251 Ctx.emitDiag([&]() { 252 Ctx.Diag->Report(SymCtx.FA->Loc, PrintAsWarning 253 ? diag::warn_library_hidden_symbol 254 : diag::err_library_hidden_symbol) 255 << SymName; 256 }); 257 }; 258 259 if (IsDeclVersionComplete) { 260 // The decl represents a complete ObjCInterface, but the symbols in the 261 // dylib do not. Determine which symbol is missing. To keep older projects 262 // building, treat this as a warning. 263 if (!DR->isExportedSymbol(ObjCIFSymbolKind::Class)) { 264 SymCtx.ObjCIFKind = ObjCIFSymbolKind::Class; 265 PrintDiagnostic(DR->getLinkageForSymbol(ObjCIFSymbolKind::Class), R, 266 getAnnotatedName(R, SymCtx), 267 /*PrintAsWarning=*/true); 268 } 269 if (!DR->isExportedSymbol(ObjCIFSymbolKind::MetaClass)) { 270 SymCtx.ObjCIFKind = ObjCIFSymbolKind::MetaClass; 271 PrintDiagnostic(DR->getLinkageForSymbol(ObjCIFSymbolKind::MetaClass), R, 272 getAnnotatedName(R, SymCtx), 273 /*PrintAsWarning=*/true); 274 } 275 return true; 276 } 277 278 if (DR->isExportedSymbol(SymCtx.ObjCIFKind)) { 279 if (!IsDylibVersionComplete) { 280 // Both the declaration and dylib have a non-complete interface. 281 SymCtx.Kind = EncodeKind::GlobalSymbol; 282 SymCtx.SymbolName = R->getName(); 283 } 284 return true; 285 } 286 287 // At this point that means there was not a matching class symbol 288 // to represent the one discovered as a declaration. 289 PrintDiagnostic(DR->getLinkageForSymbol(SymCtx.ObjCIFKind), R, 290 SymCtx.SymbolName); 291 return false; 292 } 293 294 DylibVerifier::Result DylibVerifier::compareVisibility(const Record *R, 295 SymbolContext &SymCtx, 296 const Record *DR) { 297 298 if (R->isExported()) { 299 if (!DR) { 300 Ctx.emitDiag([&]() { 301 Ctx.Diag->Report(SymCtx.FA->Loc, diag::err_library_missing_symbol) 302 << getAnnotatedName(R, SymCtx); 303 }); 304 return Result::Invalid; 305 } 306 if (DR->isInternal()) { 307 Ctx.emitDiag([&]() { 308 Ctx.Diag->Report(SymCtx.FA->Loc, diag::err_library_hidden_symbol) 309 << getAnnotatedName(R, SymCtx); 310 }); 311 return Result::Invalid; 312 } 313 } 314 315 // Emit a diagnostic for hidden declarations with external symbols, except 316 // when theres an inlined attribute. 317 if ((R->isInternal() && !SymCtx.Inlined) && DR && DR->isExported()) { 318 319 if (Mode == VerificationMode::ErrorsOnly) 320 return Result::Ignore; 321 322 if (shouldIgnorePrivateExternAttr(SymCtx.FA->D)) 323 return Result::Ignore; 324 325 if (shouldIgnoreInternalZipperedSymbol(R, SymCtx)) 326 return Result::Ignore; 327 328 unsigned ID; 329 Result Outcome; 330 if (Mode == VerificationMode::ErrorsAndWarnings) { 331 ID = diag::warn_header_hidden_symbol; 332 Outcome = Result::Ignore; 333 } else { 334 ID = diag::err_header_hidden_symbol; 335 Outcome = Result::Invalid; 336 } 337 Ctx.emitDiag([&]() { 338 Ctx.Diag->Report(SymCtx.FA->Loc, ID) << getAnnotatedName(R, SymCtx); 339 }); 340 return Outcome; 341 } 342 343 if (R->isInternal()) 344 return Result::Ignore; 345 346 return Result::Valid; 347 } 348 349 DylibVerifier::Result DylibVerifier::compareAvailability(const Record *R, 350 SymbolContext &SymCtx, 351 const Record *DR) { 352 if (!SymCtx.FA->Avail.isUnavailable()) 353 return Result::Valid; 354 355 if (shouldIgnoreZipperedAvailability(R, SymCtx)) 356 return Result::Ignore; 357 358 const bool IsDeclAvailable = SymCtx.FA->Avail.isUnavailable(); 359 360 switch (Mode) { 361 case VerificationMode::ErrorsAndWarnings: 362 Ctx.emitDiag([&]() { 363 Ctx.Diag->Report(SymCtx.FA->Loc, diag::warn_header_availability_mismatch) 364 << getAnnotatedName(R, SymCtx) << IsDeclAvailable << IsDeclAvailable; 365 }); 366 return Result::Ignore; 367 case VerificationMode::Pedantic: 368 Ctx.emitDiag([&]() { 369 Ctx.Diag->Report(SymCtx.FA->Loc, diag::err_header_availability_mismatch) 370 << getAnnotatedName(R, SymCtx) << IsDeclAvailable << IsDeclAvailable; 371 }); 372 return Result::Invalid; 373 case VerificationMode::ErrorsOnly: 374 return Result::Ignore; 375 case VerificationMode::Invalid: 376 llvm_unreachable("Unexpected verification mode symbol verification"); 377 } 378 llvm_unreachable("Unexpected verification mode symbol verification"); 379 } 380 381 bool DylibVerifier::compareSymbolFlags(const Record *R, SymbolContext &SymCtx, 382 const Record *DR) { 383 if (DR->isThreadLocalValue() && !R->isThreadLocalValue()) { 384 Ctx.emitDiag([&]() { 385 Ctx.Diag->Report(SymCtx.FA->Loc, diag::err_dylib_symbol_flags_mismatch) 386 << getAnnotatedName(DR, SymCtx) << DR->isThreadLocalValue(); 387 }); 388 return false; 389 } 390 if (!DR->isThreadLocalValue() && R->isThreadLocalValue()) { 391 Ctx.emitDiag([&]() { 392 Ctx.Diag->Report(SymCtx.FA->Loc, diag::err_header_symbol_flags_mismatch) 393 << getAnnotatedName(R, SymCtx) << R->isThreadLocalValue(); 394 }); 395 return false; 396 } 397 398 if (DR->isWeakDefined() && !R->isWeakDefined()) { 399 Ctx.emitDiag([&]() { 400 Ctx.Diag->Report(SymCtx.FA->Loc, diag::err_dylib_symbol_flags_mismatch) 401 << getAnnotatedName(DR, SymCtx) << R->isWeakDefined(); 402 }); 403 return false; 404 } 405 if (!DR->isWeakDefined() && R->isWeakDefined()) { 406 Ctx.emitDiag([&]() { 407 Ctx.Diag->Report(SymCtx.FA->Loc, diag::err_header_symbol_flags_mismatch) 408 << getAnnotatedName(R, SymCtx) << R->isWeakDefined(); 409 }); 410 return false; 411 } 412 413 return true; 414 } 415 416 DylibVerifier::Result DylibVerifier::verifyImpl(Record *R, 417 SymbolContext &SymCtx) { 418 R->setVerify(); 419 if (!canVerify()) { 420 // Accumulate symbols when not in verifying against dylib. 421 if (R->isExported() && !SymCtx.FA->Avail.isUnavailable() && 422 !SymCtx.FA->Avail.isObsoleted()) { 423 addSymbol(R, SymCtx); 424 } 425 return Ctx.FrontendState; 426 } 427 428 if (shouldIgnoreReexport(R, SymCtx)) { 429 updateState(Result::Ignore); 430 return Ctx.FrontendState; 431 } 432 433 Record *DR = 434 findRecordFromSlice(Ctx.DylibSlice, SymCtx.SymbolName, SymCtx.Kind); 435 if (DR) 436 DR->setVerify(); 437 438 if (shouldIgnoreObsolete(R, SymCtx, DR)) { 439 updateState(Result::Ignore); 440 return Ctx.FrontendState; 441 } 442 443 // Unavailable declarations don't need matching symbols. 444 if (SymCtx.FA->Avail.isUnavailable() && (!DR || DR->isInternal())) { 445 updateState(Result::Valid); 446 return Ctx.FrontendState; 447 } 448 449 Result VisibilityCheck = compareVisibility(R, SymCtx, DR); 450 if (VisibilityCheck != Result::Valid) { 451 updateState(VisibilityCheck); 452 return Ctx.FrontendState; 453 } 454 455 // All missing symbol cases to diagnose have been handled now. 456 if (!DR) { 457 updateState(Result::Ignore); 458 return Ctx.FrontendState; 459 } 460 461 // Check for mismatching ObjC interfaces. 462 if (SymCtx.ObjCIFKind != ObjCIFSymbolKind::None) { 463 if (!compareObjCInterfaceSymbols( 464 R, SymCtx, Ctx.DylibSlice->findObjCInterface(DR->getName()))) { 465 updateState(Result::Invalid); 466 return Ctx.FrontendState; 467 } 468 } 469 470 Result AvailabilityCheck = compareAvailability(R, SymCtx, DR); 471 if (AvailabilityCheck != Result::Valid) { 472 updateState(AvailabilityCheck); 473 return Ctx.FrontendState; 474 } 475 476 if (!compareSymbolFlags(R, SymCtx, DR)) { 477 updateState(Result::Invalid); 478 return Ctx.FrontendState; 479 } 480 481 addSymbol(R, SymCtx); 482 updateState(Result::Valid); 483 return Ctx.FrontendState; 484 } 485 486 bool DylibVerifier::canVerify() { 487 return Ctx.FrontendState != Result::NoVerify; 488 } 489 490 void DylibVerifier::assignSlice(const Target &T) { 491 assert(T == Ctx.Target && "Active targets should match."); 492 if (Dylib.empty()) 493 return; 494 495 // Note: there are no reexport slices with binaries, as opposed to TBD files, 496 // so it can be assumed that the target match is the active top-level library. 497 auto It = find_if( 498 Dylib, [&T](const auto &Slice) { return T == Slice->getTarget(); }); 499 500 assert(It != Dylib.end() && "Target slice should always exist."); 501 Ctx.DylibSlice = It->get(); 502 } 503 504 void DylibVerifier::setTarget(const Target &T) { 505 Ctx.Target = T; 506 Ctx.DiscoveredFirstError = false; 507 if (Dylib.empty()) { 508 updateState(Result::NoVerify); 509 return; 510 } 511 updateState(Result::Ignore); 512 assignSlice(T); 513 } 514 515 void DylibVerifier::setSourceManager( 516 IntrusiveRefCntPtr<SourceManager> SourceMgr) { 517 if (!Ctx.Diag) 518 return; 519 SourceManagers.push_back(std::move(SourceMgr)); 520 Ctx.Diag->setSourceManager(SourceManagers.back().get()); 521 } 522 523 DylibVerifier::Result DylibVerifier::verify(ObjCIVarRecord *R, 524 const FrontendAttrs *FA, 525 const StringRef SuperClass) { 526 if (R->isVerified()) 527 return getState(); 528 529 std::string FullName = 530 ObjCIVarRecord::createScopedName(SuperClass, R->getName()); 531 SymbolContext SymCtx{FullName, EncodeKind::ObjectiveCInstanceVariable, FA}; 532 return verifyImpl(R, SymCtx); 533 } 534 535 static ObjCIFSymbolKind assignObjCIFSymbolKind(const ObjCInterfaceRecord *R) { 536 ObjCIFSymbolKind Result = ObjCIFSymbolKind::None; 537 if (R->getLinkageForSymbol(ObjCIFSymbolKind::Class) != RecordLinkage::Unknown) 538 Result |= ObjCIFSymbolKind::Class; 539 if (R->getLinkageForSymbol(ObjCIFSymbolKind::MetaClass) != 540 RecordLinkage::Unknown) 541 Result |= ObjCIFSymbolKind::MetaClass; 542 if (R->getLinkageForSymbol(ObjCIFSymbolKind::EHType) != 543 RecordLinkage::Unknown) 544 Result |= ObjCIFSymbolKind::EHType; 545 return Result; 546 } 547 548 DylibVerifier::Result DylibVerifier::verify(ObjCInterfaceRecord *R, 549 const FrontendAttrs *FA) { 550 if (R->isVerified()) 551 return getState(); 552 SymbolContext SymCtx; 553 SymCtx.SymbolName = R->getName(); 554 SymCtx.ObjCIFKind = assignObjCIFSymbolKind(R); 555 556 SymCtx.Kind = R->hasExceptionAttribute() ? EncodeKind::ObjectiveCClassEHType 557 : EncodeKind::ObjectiveCClass; 558 SymCtx.FA = FA; 559 560 return verifyImpl(R, SymCtx); 561 } 562 563 DylibVerifier::Result DylibVerifier::verify(GlobalRecord *R, 564 const FrontendAttrs *FA) { 565 if (R->isVerified()) 566 return getState(); 567 568 // Global classifications could be obfusciated with `asm`. 569 SimpleSymbol Sym = parseSymbol(R->getName()); 570 SymbolContext SymCtx; 571 SymCtx.SymbolName = Sym.Name; 572 SymCtx.Kind = Sym.Kind; 573 SymCtx.FA = FA; 574 SymCtx.Inlined = R->isInlined(); 575 return verifyImpl(R, SymCtx); 576 } 577 578 void DylibVerifier::VerifierContext::emitDiag(llvm::function_ref<void()> Report, 579 RecordLoc *Loc) { 580 if (!DiscoveredFirstError) { 581 Diag->Report(diag::warn_target) 582 << (PrintArch ? getArchitectureName(Target.Arch) 583 : getTargetTripleName(Target)); 584 DiscoveredFirstError = true; 585 } 586 if (Loc && Loc->isValid()) 587 llvm::errs() << Loc->File << ":" << Loc->Line << ":" << 0 << ": "; 588 589 Report(); 590 } 591 592 // The existence of weak-defined RTTI can not always be inferred from the 593 // header files because they can be generated as part of an implementation 594 // file. 595 // InstallAPI doesn't warn about weak-defined RTTI, because this doesn't affect 596 // static linking and so can be ignored for text-api files. 597 static bool shouldIgnoreCpp(StringRef Name, bool IsWeakDef) { 598 return (IsWeakDef && 599 (Name.starts_with("__ZTI") || Name.starts_with("__ZTS"))); 600 } 601 void DylibVerifier::visitSymbolInDylib(const Record &R, SymbolContext &SymCtx) { 602 // Undefined symbols should not be in InstallAPI generated text-api files. 603 if (R.isUndefined()) { 604 updateState(Result::Valid); 605 return; 606 } 607 608 // Internal symbols should not be in InstallAPI generated text-api files. 609 if (R.isInternal()) { 610 updateState(Result::Valid); 611 return; 612 } 613 614 // Allow zippered symbols with potentially mismatching availability 615 // between macOS and macCatalyst in the final text-api file. 616 const StringRef SymbolName(SymCtx.SymbolName); 617 if (const Symbol *Sym = Exports->findSymbol(SymCtx.Kind, SymCtx.SymbolName, 618 SymCtx.ObjCIFKind)) { 619 if (Sym->hasArchitecture(Ctx.Target.Arch)) { 620 updateState(Result::Ignore); 621 return; 622 } 623 } 624 625 const bool IsLinkerSymbol = SymbolName.starts_with("$ld$"); 626 627 if (R.isVerified()) { 628 // Check for unavailable symbols. 629 // This should only occur in the zippered case where we ignored 630 // availability until all headers have been parsed. 631 auto It = DeferredZipperedSymbols.find(SymCtx.SymbolName); 632 if (It == DeferredZipperedSymbols.end()) { 633 updateState(Result::Valid); 634 return; 635 } 636 637 ZipperedDeclSources Locs; 638 for (const ZipperedDeclSource &ZSource : It->second) { 639 if (ZSource.FA->Avail.isObsoleted()) { 640 updateState(Result::Ignore); 641 return; 642 } 643 if (ZSource.T.Arch != Ctx.Target.Arch) 644 continue; 645 Locs.emplace_back(ZSource); 646 } 647 assert(Locs.size() == 2 && "Expected two decls for zippered symbol"); 648 649 // Print violating declarations per platform. 650 for (const ZipperedDeclSource &ZSource : Locs) { 651 unsigned DiagID = 0; 652 if (Mode == VerificationMode::Pedantic || IsLinkerSymbol) { 653 updateState(Result::Invalid); 654 DiagID = diag::err_header_availability_mismatch; 655 } else if (Mode == VerificationMode::ErrorsAndWarnings) { 656 updateState(Result::Ignore); 657 DiagID = diag::warn_header_availability_mismatch; 658 } else { 659 updateState(Result::Ignore); 660 return; 661 } 662 // Bypass emitDiag banner and print the target everytime. 663 Ctx.Diag->setSourceManager(ZSource.SrcMgr); 664 Ctx.Diag->Report(diag::warn_target) << getTargetTripleName(ZSource.T); 665 Ctx.Diag->Report(ZSource.FA->Loc, DiagID) 666 << getAnnotatedName(&R, SymCtx) << ZSource.FA->Avail.isUnavailable() 667 << ZSource.FA->Avail.isUnavailable(); 668 } 669 return; 670 } 671 672 if (shouldIgnoreCpp(SymbolName, R.isWeakDefined())) { 673 updateState(Result::Valid); 674 return; 675 } 676 677 // All checks at this point classify as some kind of violation. 678 // The different verification modes dictate whether they are reported to the 679 // user. 680 if (IsLinkerSymbol || (Mode > VerificationMode::ErrorsOnly)) 681 accumulateSrcLocForDylibSymbols(); 682 RecordLoc Loc = DWARFCtx->SourceLocs.lookup(SymCtx.SymbolName); 683 684 // Regardless of verification mode, error out on mismatched special linker 685 // symbols. 686 if (IsLinkerSymbol) { 687 Ctx.emitDiag( 688 [&]() { 689 Ctx.Diag->Report(diag::err_header_symbol_missing) 690 << getAnnotatedName(&R, SymCtx, Loc.isValid()); 691 }, 692 &Loc); 693 updateState(Result::Invalid); 694 return; 695 } 696 697 // Missing declarations for exported symbols are hard errors on Pedantic mode. 698 if (Mode == VerificationMode::Pedantic) { 699 Ctx.emitDiag( 700 [&]() { 701 Ctx.Diag->Report(diag::err_header_symbol_missing) 702 << getAnnotatedName(&R, SymCtx, Loc.isValid()); 703 }, 704 &Loc); 705 updateState(Result::Invalid); 706 return; 707 } 708 709 // Missing declarations for exported symbols are warnings on ErrorsAndWarnings 710 // mode. 711 if (Mode == VerificationMode::ErrorsAndWarnings) { 712 Ctx.emitDiag( 713 [&]() { 714 Ctx.Diag->Report(diag::warn_header_symbol_missing) 715 << getAnnotatedName(&R, SymCtx, Loc.isValid()); 716 }, 717 &Loc); 718 updateState(Result::Ignore); 719 return; 720 } 721 722 // Missing declarations are dropped for ErrorsOnly mode. It is the last 723 // remaining mode. 724 updateState(Result::Ignore); 725 return; 726 } 727 728 void DylibVerifier::visitGlobal(const GlobalRecord &R) { 729 SymbolContext SymCtx; 730 SimpleSymbol Sym = parseSymbol(R.getName()); 731 SymCtx.SymbolName = Sym.Name; 732 SymCtx.Kind = Sym.Kind; 733 visitSymbolInDylib(R, SymCtx); 734 } 735 736 void DylibVerifier::visitObjCIVar(const ObjCIVarRecord &R, 737 const StringRef Super) { 738 SymbolContext SymCtx; 739 SymCtx.SymbolName = ObjCIVarRecord::createScopedName(Super, R.getName()); 740 SymCtx.Kind = EncodeKind::ObjectiveCInstanceVariable; 741 visitSymbolInDylib(R, SymCtx); 742 } 743 744 void DylibVerifier::accumulateSrcLocForDylibSymbols() { 745 if (DSYMPath.empty()) 746 return; 747 748 assert(DWARFCtx != nullptr && "Expected an initialized DWARFContext"); 749 if (DWARFCtx->ParsedDSYM) 750 return; 751 DWARFCtx->ParsedDSYM = true; 752 DWARFCtx->SourceLocs = 753 DylibReader::accumulateSourceLocFromDSYM(DSYMPath, Ctx.Target); 754 } 755 756 void DylibVerifier::visitObjCInterface(const ObjCInterfaceRecord &R) { 757 SymbolContext SymCtx; 758 SymCtx.SymbolName = R.getName(); 759 SymCtx.ObjCIFKind = assignObjCIFSymbolKind(&R); 760 if (SymCtx.ObjCIFKind > ObjCIFSymbolKind::EHType) { 761 if (R.hasExceptionAttribute()) { 762 SymCtx.Kind = EncodeKind::ObjectiveCClassEHType; 763 visitSymbolInDylib(R, SymCtx); 764 } 765 SymCtx.Kind = EncodeKind::ObjectiveCClass; 766 visitSymbolInDylib(R, SymCtx); 767 } else { 768 SymCtx.Kind = R.hasExceptionAttribute() ? EncodeKind::ObjectiveCClassEHType 769 : EncodeKind::ObjectiveCClass; 770 visitSymbolInDylib(R, SymCtx); 771 } 772 773 for (const ObjCIVarRecord *IV : R.getObjCIVars()) 774 visitObjCIVar(*IV, R.getName()); 775 } 776 777 void DylibVerifier::visitObjCCategory(const ObjCCategoryRecord &R) { 778 for (const ObjCIVarRecord *IV : R.getObjCIVars()) 779 visitObjCIVar(*IV, R.getSuperClassName()); 780 } 781 782 DylibVerifier::Result DylibVerifier::verifyRemainingSymbols() { 783 if (getState() == Result::NoVerify) 784 return Result::NoVerify; 785 assert(!Dylib.empty() && "No binary to verify against"); 786 787 DWARFContext DWARFInfo; 788 DWARFCtx = &DWARFInfo; 789 Ctx.Target = Target(Architecture::AK_unknown, PlatformType::PLATFORM_UNKNOWN); 790 for (std::shared_ptr<RecordsSlice> Slice : Dylib) { 791 if (Ctx.Target.Arch == Slice->getTarget().Arch) 792 continue; 793 Ctx.DiscoveredFirstError = false; 794 Ctx.PrintArch = true; 795 Ctx.Target = Slice->getTarget(); 796 Ctx.DylibSlice = Slice.get(); 797 Slice->visit(*this); 798 } 799 return getState(); 800 } 801 802 bool DylibVerifier::verifyBinaryAttrs(const ArrayRef<Target> ProvidedTargets, 803 const BinaryAttrs &ProvidedBA, 804 const LibAttrs &ProvidedReexports, 805 const LibAttrs &ProvidedClients, 806 const LibAttrs &ProvidedRPaths, 807 const FileType &FT) { 808 assert(!Dylib.empty() && "Need dylib to verify."); 809 810 // Pickup any load commands that can differ per slice to compare. 811 TargetList DylibTargets; 812 LibAttrs DylibReexports; 813 LibAttrs DylibClients; 814 LibAttrs DylibRPaths; 815 for (const std::shared_ptr<RecordsSlice> &RS : Dylib) { 816 DylibTargets.push_back(RS->getTarget()); 817 const BinaryAttrs &BinInfo = RS->getBinaryAttrs(); 818 for (const StringRef LibName : BinInfo.RexportedLibraries) 819 DylibReexports[LibName].set(DylibTargets.back().Arch); 820 for (const StringRef LibName : BinInfo.AllowableClients) 821 DylibClients[LibName].set(DylibTargets.back().Arch); 822 // Compare attributes that are only representable in >= TBD_V5. 823 if (FT >= FileType::TBD_V5) 824 for (const StringRef Name : BinInfo.RPaths) 825 DylibRPaths[Name].set(DylibTargets.back().Arch); 826 } 827 828 // Check targets first. 829 ArchitectureSet ProvidedArchs = mapToArchitectureSet(ProvidedTargets); 830 ArchitectureSet DylibArchs = mapToArchitectureSet(DylibTargets); 831 if (ProvidedArchs != DylibArchs) { 832 Ctx.Diag->Report(diag::err_architecture_mismatch) 833 << ProvidedArchs << DylibArchs; 834 return false; 835 } 836 auto ProvidedPlatforms = mapToPlatformVersionSet(ProvidedTargets); 837 auto DylibPlatforms = mapToPlatformVersionSet(DylibTargets); 838 if (ProvidedPlatforms != DylibPlatforms) { 839 const bool DiffMinOS = 840 mapToPlatformSet(ProvidedTargets) == mapToPlatformSet(DylibTargets); 841 if (DiffMinOS) 842 Ctx.Diag->Report(diag::warn_platform_mismatch) 843 << ProvidedPlatforms << DylibPlatforms; 844 else { 845 Ctx.Diag->Report(diag::err_platform_mismatch) 846 << ProvidedPlatforms << DylibPlatforms; 847 return false; 848 } 849 } 850 851 // Because InstallAPI requires certain attributes to match across architecture 852 // slices, take the first one to compare those with. 853 const BinaryAttrs &DylibBA = (*Dylib.begin())->getBinaryAttrs(); 854 855 if (ProvidedBA.InstallName != DylibBA.InstallName) { 856 Ctx.Diag->Report(diag::err_install_name_mismatch) 857 << ProvidedBA.InstallName << DylibBA.InstallName; 858 return false; 859 } 860 861 if (ProvidedBA.CurrentVersion != DylibBA.CurrentVersion) { 862 Ctx.Diag->Report(diag::err_current_version_mismatch) 863 << ProvidedBA.CurrentVersion << DylibBA.CurrentVersion; 864 return false; 865 } 866 867 if (ProvidedBA.CompatVersion != DylibBA.CompatVersion) { 868 Ctx.Diag->Report(diag::err_compatibility_version_mismatch) 869 << ProvidedBA.CompatVersion << DylibBA.CompatVersion; 870 return false; 871 } 872 873 if (ProvidedBA.AppExtensionSafe != DylibBA.AppExtensionSafe) { 874 Ctx.Diag->Report(diag::err_appextension_safe_mismatch) 875 << (ProvidedBA.AppExtensionSafe ? "true" : "false") 876 << (DylibBA.AppExtensionSafe ? "true" : "false"); 877 return false; 878 } 879 880 if (!DylibBA.TwoLevelNamespace) { 881 Ctx.Diag->Report(diag::err_no_twolevel_namespace); 882 return false; 883 } 884 885 if (ProvidedBA.OSLibNotForSharedCache != DylibBA.OSLibNotForSharedCache) { 886 Ctx.Diag->Report(diag::err_shared_cache_eligiblity_mismatch) 887 << (ProvidedBA.OSLibNotForSharedCache ? "true" : "false") 888 << (DylibBA.OSLibNotForSharedCache ? "true" : "false"); 889 return false; 890 } 891 892 if (ProvidedBA.ParentUmbrella.empty() && !DylibBA.ParentUmbrella.empty()) { 893 Ctx.Diag->Report(diag::err_parent_umbrella_missing) 894 << "installAPI option" << DylibBA.ParentUmbrella; 895 return false; 896 } 897 898 if (!ProvidedBA.ParentUmbrella.empty() && DylibBA.ParentUmbrella.empty()) { 899 Ctx.Diag->Report(diag::err_parent_umbrella_missing) 900 << "binary file" << ProvidedBA.ParentUmbrella; 901 return false; 902 } 903 904 if ((!ProvidedBA.ParentUmbrella.empty()) && 905 (ProvidedBA.ParentUmbrella != DylibBA.ParentUmbrella)) { 906 Ctx.Diag->Report(diag::err_parent_umbrella_mismatch) 907 << ProvidedBA.ParentUmbrella << DylibBA.ParentUmbrella; 908 return false; 909 } 910 911 auto CompareLibraries = [&](const LibAttrs &Provided, const LibAttrs &Dylib, 912 unsigned DiagID_missing, unsigned DiagID_mismatch, 913 bool Fatal = true) { 914 if (Provided == Dylib) 915 return true; 916 917 for (const llvm::StringMapEntry<ArchitectureSet> &PAttr : Provided) { 918 const auto DAttrIt = Dylib.find(PAttr.getKey()); 919 if (DAttrIt == Dylib.end()) { 920 Ctx.Diag->Report(DiagID_missing) << "binary file" << PAttr; 921 if (Fatal) 922 return false; 923 } 924 925 if (PAttr.getValue() != DAttrIt->getValue()) { 926 Ctx.Diag->Report(DiagID_mismatch) << PAttr << *DAttrIt; 927 if (Fatal) 928 return false; 929 } 930 } 931 932 for (const llvm::StringMapEntry<ArchitectureSet> &DAttr : Dylib) { 933 const auto PAttrIt = Provided.find(DAttr.getKey()); 934 if (PAttrIt == Provided.end()) { 935 Ctx.Diag->Report(DiagID_missing) << "installAPI option" << DAttr; 936 if (!Fatal) 937 continue; 938 return false; 939 } 940 941 if (PAttrIt->getValue() != DAttr.getValue()) { 942 if (Fatal) 943 llvm_unreachable("this case was already covered above."); 944 } 945 } 946 return true; 947 }; 948 949 if (!CompareLibraries(ProvidedReexports, DylibReexports, 950 diag::err_reexported_libraries_missing, 951 diag::err_reexported_libraries_mismatch)) 952 return false; 953 954 if (!CompareLibraries(ProvidedClients, DylibClients, 955 diag::err_allowable_clients_missing, 956 diag::err_allowable_clients_mismatch)) 957 return false; 958 959 if (FT >= FileType::TBD_V5) { 960 // Ignore rpath differences if building an asan variant, since the 961 // compiler injects additional paths. 962 // FIXME: Building with sanitizers does not always change the install 963 // name, so this is not a foolproof solution. 964 if (!ProvidedBA.InstallName.ends_with("_asan")) { 965 if (!CompareLibraries(ProvidedRPaths, DylibRPaths, 966 diag::warn_rpaths_missing, 967 diag::warn_rpaths_mismatch, 968 /*Fatal=*/false)) 969 return true; 970 } 971 } 972 973 return true; 974 } 975 976 } // namespace installapi 977 } // namespace clang 978