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 StringRef SymName = SymCtx.SymbolName; 191 // Linker directive symbols can never be ignored. 192 if (SymName.starts_with("$ld$")) 193 return false; 194 195 if (Reexports.empty()) 196 return false; 197 198 for (const InterfaceFile &Lib : Reexports) { 199 if (!Lib.hasTarget(Ctx.Target)) 200 continue; 201 if (auto Sym = Lib.getSymbol(SymCtx.Kind, SymName, SymCtx.ObjCIFKind)) 202 if ((*Sym)->hasTarget(Ctx.Target)) 203 return true; 204 } 205 return false; 206 } 207 208 bool DylibVerifier::shouldIgnoreInternalZipperedSymbol( 209 const Record *R, const SymbolContext &SymCtx) const { 210 if (!Zippered) 211 return false; 212 213 return Exports->findSymbol(SymCtx.Kind, SymCtx.SymbolName, 214 SymCtx.ObjCIFKind) != nullptr; 215 } 216 217 bool DylibVerifier::shouldIgnoreZipperedAvailability(const Record *R, 218 SymbolContext &SymCtx) { 219 if (!(Zippered && SymCtx.FA->Avail.isUnavailable())) 220 return false; 221 222 // Collect source location incase there is an exported symbol to diagnose 223 // during `verifyRemainingSymbols`. 224 DeferredZipperedSymbols[SymCtx.SymbolName].emplace_back( 225 ZipperedDeclSource{SymCtx.FA, SourceManagers.back().get(), Ctx.Target}); 226 227 return true; 228 } 229 230 bool DylibVerifier::compareObjCInterfaceSymbols(const Record *R, 231 SymbolContext &SymCtx, 232 const ObjCInterfaceRecord *DR) { 233 const bool IsDeclVersionComplete = 234 ((SymCtx.ObjCIFKind & ObjCIFSymbolKind::Class) == 235 ObjCIFSymbolKind::Class) && 236 ((SymCtx.ObjCIFKind & ObjCIFSymbolKind::MetaClass) == 237 ObjCIFSymbolKind::MetaClass); 238 239 const bool IsDylibVersionComplete = DR->isCompleteInterface(); 240 241 // The common case, a complete ObjCInterface. 242 if (IsDeclVersionComplete && IsDylibVersionComplete) 243 return true; 244 245 auto PrintDiagnostic = [&](auto SymLinkage, const Record *Record, 246 StringRef SymName, bool PrintAsWarning = false) { 247 if (SymLinkage == RecordLinkage::Unknown) 248 Ctx.emitDiag([&]() { 249 Ctx.Diag->Report(SymCtx.FA->Loc, PrintAsWarning 250 ? diag::warn_library_missing_symbol 251 : diag::err_library_missing_symbol) 252 << SymName; 253 }); 254 else 255 Ctx.emitDiag([&]() { 256 Ctx.Diag->Report(SymCtx.FA->Loc, PrintAsWarning 257 ? diag::warn_library_hidden_symbol 258 : diag::err_library_hidden_symbol) 259 << SymName; 260 }); 261 }; 262 263 if (IsDeclVersionComplete) { 264 // The decl represents a complete ObjCInterface, but the symbols in the 265 // dylib do not. Determine which symbol is missing. To keep older projects 266 // building, treat this as a warning. 267 if (!DR->isExportedSymbol(ObjCIFSymbolKind::Class)) { 268 SymCtx.ObjCIFKind = ObjCIFSymbolKind::Class; 269 PrintDiagnostic(DR->getLinkageForSymbol(ObjCIFSymbolKind::Class), R, 270 getAnnotatedName(R, SymCtx), 271 /*PrintAsWarning=*/true); 272 } 273 if (!DR->isExportedSymbol(ObjCIFSymbolKind::MetaClass)) { 274 SymCtx.ObjCIFKind = ObjCIFSymbolKind::MetaClass; 275 PrintDiagnostic(DR->getLinkageForSymbol(ObjCIFSymbolKind::MetaClass), R, 276 getAnnotatedName(R, SymCtx), 277 /*PrintAsWarning=*/true); 278 } 279 return true; 280 } 281 282 if (DR->isExportedSymbol(SymCtx.ObjCIFKind)) { 283 if (!IsDylibVersionComplete) { 284 // Both the declaration and dylib have a non-complete interface. 285 SymCtx.Kind = EncodeKind::GlobalSymbol; 286 SymCtx.SymbolName = R->getName(); 287 } 288 return true; 289 } 290 291 // At this point that means there was not a matching class symbol 292 // to represent the one discovered as a declaration. 293 PrintDiagnostic(DR->getLinkageForSymbol(SymCtx.ObjCIFKind), R, 294 SymCtx.SymbolName); 295 return false; 296 } 297 298 DylibVerifier::Result DylibVerifier::compareVisibility(const Record *R, 299 SymbolContext &SymCtx, 300 const Record *DR) { 301 302 if (R->isExported()) { 303 if (!DR) { 304 Ctx.emitDiag([&]() { 305 Ctx.Diag->Report(SymCtx.FA->Loc, diag::err_library_missing_symbol) 306 << getAnnotatedName(R, SymCtx); 307 }); 308 return Result::Invalid; 309 } 310 if (DR->isInternal()) { 311 Ctx.emitDiag([&]() { 312 Ctx.Diag->Report(SymCtx.FA->Loc, diag::err_library_hidden_symbol) 313 << getAnnotatedName(R, SymCtx); 314 }); 315 return Result::Invalid; 316 } 317 } 318 319 // Emit a diagnostic for hidden declarations with external symbols, except 320 // when theres an inlined attribute. 321 if ((R->isInternal() && !SymCtx.Inlined) && DR && DR->isExported()) { 322 323 if (Mode == VerificationMode::ErrorsOnly) 324 return Result::Ignore; 325 326 if (shouldIgnorePrivateExternAttr(SymCtx.FA->D)) 327 return Result::Ignore; 328 329 if (shouldIgnoreInternalZipperedSymbol(R, SymCtx)) 330 return Result::Ignore; 331 332 unsigned ID; 333 Result Outcome; 334 if (Mode == VerificationMode::ErrorsAndWarnings) { 335 ID = diag::warn_header_hidden_symbol; 336 Outcome = Result::Ignore; 337 } else { 338 ID = diag::err_header_hidden_symbol; 339 Outcome = Result::Invalid; 340 } 341 Ctx.emitDiag([&]() { 342 Ctx.Diag->Report(SymCtx.FA->Loc, ID) << getAnnotatedName(R, SymCtx); 343 }); 344 return Outcome; 345 } 346 347 if (R->isInternal()) 348 return Result::Ignore; 349 350 return Result::Valid; 351 } 352 353 DylibVerifier::Result DylibVerifier::compareAvailability(const Record *R, 354 SymbolContext &SymCtx, 355 const Record *DR) { 356 if (!SymCtx.FA->Avail.isUnavailable()) 357 return Result::Valid; 358 359 if (shouldIgnoreZipperedAvailability(R, SymCtx)) 360 return Result::Ignore; 361 362 const bool IsDeclAvailable = SymCtx.FA->Avail.isUnavailable(); 363 364 switch (Mode) { 365 case VerificationMode::ErrorsAndWarnings: 366 Ctx.emitDiag([&]() { 367 Ctx.Diag->Report(SymCtx.FA->Loc, diag::warn_header_availability_mismatch) 368 << getAnnotatedName(R, SymCtx) << IsDeclAvailable << IsDeclAvailable; 369 }); 370 return Result::Ignore; 371 case VerificationMode::Pedantic: 372 Ctx.emitDiag([&]() { 373 Ctx.Diag->Report(SymCtx.FA->Loc, diag::err_header_availability_mismatch) 374 << getAnnotatedName(R, SymCtx) << IsDeclAvailable << IsDeclAvailable; 375 }); 376 return Result::Invalid; 377 case VerificationMode::ErrorsOnly: 378 return Result::Ignore; 379 case VerificationMode::Invalid: 380 llvm_unreachable("Unexpected verification mode symbol verification"); 381 } 382 llvm_unreachable("Unexpected verification mode symbol verification"); 383 } 384 385 bool DylibVerifier::compareSymbolFlags(const Record *R, SymbolContext &SymCtx, 386 const Record *DR) { 387 if (DR->isThreadLocalValue() && !R->isThreadLocalValue()) { 388 Ctx.emitDiag([&]() { 389 Ctx.Diag->Report(SymCtx.FA->Loc, diag::err_dylib_symbol_flags_mismatch) 390 << getAnnotatedName(DR, SymCtx) << DR->isThreadLocalValue(); 391 }); 392 return false; 393 } 394 if (!DR->isThreadLocalValue() && R->isThreadLocalValue()) { 395 Ctx.emitDiag([&]() { 396 Ctx.Diag->Report(SymCtx.FA->Loc, diag::err_header_symbol_flags_mismatch) 397 << getAnnotatedName(R, SymCtx) << R->isThreadLocalValue(); 398 }); 399 return false; 400 } 401 402 if (DR->isWeakDefined() && !R->isWeakDefined()) { 403 Ctx.emitDiag([&]() { 404 Ctx.Diag->Report(SymCtx.FA->Loc, diag::err_dylib_symbol_flags_mismatch) 405 << getAnnotatedName(DR, SymCtx) << R->isWeakDefined(); 406 }); 407 return false; 408 } 409 if (!DR->isWeakDefined() && R->isWeakDefined()) { 410 Ctx.emitDiag([&]() { 411 Ctx.Diag->Report(SymCtx.FA->Loc, diag::err_header_symbol_flags_mismatch) 412 << getAnnotatedName(R, SymCtx) << R->isWeakDefined(); 413 }); 414 return false; 415 } 416 417 return true; 418 } 419 420 DylibVerifier::Result DylibVerifier::verifyImpl(Record *R, 421 SymbolContext &SymCtx) { 422 R->setVerify(); 423 if (!canVerify()) { 424 // Accumulate symbols when not in verifying against dylib. 425 if (R->isExported() && !SymCtx.FA->Avail.isUnavailable() && 426 !SymCtx.FA->Avail.isObsoleted()) { 427 addSymbol(R, SymCtx); 428 } 429 return Ctx.FrontendState; 430 } 431 432 if (shouldIgnoreReexport(R, SymCtx)) { 433 updateState(Result::Ignore); 434 return Ctx.FrontendState; 435 } 436 437 Record *DR = 438 findRecordFromSlice(Ctx.DylibSlice, SymCtx.SymbolName, SymCtx.Kind); 439 if (DR) 440 DR->setVerify(); 441 442 if (shouldIgnoreObsolete(R, SymCtx, DR)) { 443 updateState(Result::Ignore); 444 return Ctx.FrontendState; 445 } 446 447 // Unavailable declarations don't need matching symbols. 448 if (SymCtx.FA->Avail.isUnavailable() && (!DR || DR->isInternal())) { 449 updateState(Result::Valid); 450 return Ctx.FrontendState; 451 } 452 453 Result VisibilityCheck = compareVisibility(R, SymCtx, DR); 454 if (VisibilityCheck != Result::Valid) { 455 updateState(VisibilityCheck); 456 return Ctx.FrontendState; 457 } 458 459 // All missing symbol cases to diagnose have been handled now. 460 if (!DR) { 461 updateState(Result::Ignore); 462 return Ctx.FrontendState; 463 } 464 465 // Check for mismatching ObjC interfaces. 466 if (SymCtx.ObjCIFKind != ObjCIFSymbolKind::None) { 467 if (!compareObjCInterfaceSymbols( 468 R, SymCtx, Ctx.DylibSlice->findObjCInterface(DR->getName()))) { 469 updateState(Result::Invalid); 470 return Ctx.FrontendState; 471 } 472 } 473 474 Result AvailabilityCheck = compareAvailability(R, SymCtx, DR); 475 if (AvailabilityCheck != Result::Valid) { 476 updateState(AvailabilityCheck); 477 return Ctx.FrontendState; 478 } 479 480 if (!compareSymbolFlags(R, SymCtx, DR)) { 481 updateState(Result::Invalid); 482 return Ctx.FrontendState; 483 } 484 485 addSymbol(R, SymCtx); 486 updateState(Result::Valid); 487 return Ctx.FrontendState; 488 } 489 490 bool DylibVerifier::canVerify() { 491 return Ctx.FrontendState != Result::NoVerify; 492 } 493 494 void DylibVerifier::assignSlice(const Target &T) { 495 assert(T == Ctx.Target && "Active targets should match."); 496 if (Dylib.empty()) 497 return; 498 499 // Note: there are no reexport slices with binaries, as opposed to TBD files, 500 // so it can be assumed that the target match is the active top-level library. 501 auto It = find_if( 502 Dylib, [&T](const auto &Slice) { return T == Slice->getTarget(); }); 503 504 assert(It != Dylib.end() && "Target slice should always exist."); 505 Ctx.DylibSlice = It->get(); 506 } 507 508 void DylibVerifier::setTarget(const Target &T) { 509 Ctx.Target = T; 510 Ctx.DiscoveredFirstError = false; 511 if (Dylib.empty()) { 512 updateState(Result::NoVerify); 513 return; 514 } 515 updateState(Result::Ignore); 516 assignSlice(T); 517 } 518 519 void DylibVerifier::setSourceManager( 520 IntrusiveRefCntPtr<SourceManager> SourceMgr) { 521 if (!Ctx.Diag) 522 return; 523 SourceManagers.push_back(std::move(SourceMgr)); 524 Ctx.Diag->setSourceManager(SourceManagers.back().get()); 525 } 526 527 DylibVerifier::Result DylibVerifier::verify(ObjCIVarRecord *R, 528 const FrontendAttrs *FA, 529 const StringRef SuperClass) { 530 if (R->isVerified()) 531 return getState(); 532 533 std::string FullName = 534 ObjCIVarRecord::createScopedName(SuperClass, R->getName()); 535 SymbolContext SymCtx{FullName, EncodeKind::ObjectiveCInstanceVariable, FA}; 536 return verifyImpl(R, SymCtx); 537 } 538 539 static ObjCIFSymbolKind assignObjCIFSymbolKind(const ObjCInterfaceRecord *R) { 540 ObjCIFSymbolKind Result = ObjCIFSymbolKind::None; 541 if (R->getLinkageForSymbol(ObjCIFSymbolKind::Class) != RecordLinkage::Unknown) 542 Result |= ObjCIFSymbolKind::Class; 543 if (R->getLinkageForSymbol(ObjCIFSymbolKind::MetaClass) != 544 RecordLinkage::Unknown) 545 Result |= ObjCIFSymbolKind::MetaClass; 546 if (R->getLinkageForSymbol(ObjCIFSymbolKind::EHType) != 547 RecordLinkage::Unknown) 548 Result |= ObjCIFSymbolKind::EHType; 549 return Result; 550 } 551 552 DylibVerifier::Result DylibVerifier::verify(ObjCInterfaceRecord *R, 553 const FrontendAttrs *FA) { 554 if (R->isVerified()) 555 return getState(); 556 SymbolContext SymCtx; 557 SymCtx.SymbolName = R->getName(); 558 SymCtx.ObjCIFKind = assignObjCIFSymbolKind(R); 559 560 SymCtx.Kind = R->hasExceptionAttribute() ? EncodeKind::ObjectiveCClassEHType 561 : EncodeKind::ObjectiveCClass; 562 SymCtx.FA = FA; 563 564 return verifyImpl(R, SymCtx); 565 } 566 567 DylibVerifier::Result DylibVerifier::verify(GlobalRecord *R, 568 const FrontendAttrs *FA) { 569 if (R->isVerified()) 570 return getState(); 571 572 // Global classifications could be obfusciated with `asm`. 573 SimpleSymbol Sym = parseSymbol(R->getName()); 574 SymbolContext SymCtx; 575 SymCtx.SymbolName = Sym.Name; 576 SymCtx.Kind = Sym.Kind; 577 SymCtx.FA = FA; 578 SymCtx.Inlined = R->isInlined(); 579 return verifyImpl(R, SymCtx); 580 } 581 582 void DylibVerifier::VerifierContext::emitDiag(llvm::function_ref<void()> Report, 583 RecordLoc *Loc) { 584 if (!DiscoveredFirstError) { 585 Diag->Report(diag::warn_target) 586 << (PrintArch ? getArchitectureName(Target.Arch) 587 : getTargetTripleName(Target)); 588 DiscoveredFirstError = true; 589 } 590 if (Loc && Loc->isValid()) 591 llvm::errs() << Loc->File << ":" << Loc->Line << ":" << 0 << ": "; 592 593 Report(); 594 } 595 596 // The existence of weak-defined RTTI can not always be inferred from the 597 // header files because they can be generated as part of an implementation 598 // file. 599 // InstallAPI doesn't warn about weak-defined RTTI, because this doesn't affect 600 // static linking and so can be ignored for text-api files. 601 static bool shouldIgnoreCpp(StringRef Name, bool IsWeakDef) { 602 return (IsWeakDef && 603 (Name.starts_with("__ZTI") || Name.starts_with("__ZTS"))); 604 } 605 void DylibVerifier::visitSymbolInDylib(const Record &R, SymbolContext &SymCtx) { 606 // Undefined symbols should not be in InstallAPI generated text-api files. 607 if (R.isUndefined()) { 608 updateState(Result::Valid); 609 return; 610 } 611 612 // Internal symbols should not be in InstallAPI generated text-api files. 613 if (R.isInternal()) { 614 updateState(Result::Valid); 615 return; 616 } 617 618 // Allow zippered symbols with potentially mismatching availability 619 // between macOS and macCatalyst in the final text-api file. 620 const StringRef SymbolName(SymCtx.SymbolName); 621 if (const Symbol *Sym = Exports->findSymbol(SymCtx.Kind, SymCtx.SymbolName, 622 SymCtx.ObjCIFKind)) { 623 if (Sym->hasArchitecture(Ctx.Target.Arch)) { 624 updateState(Result::Ignore); 625 return; 626 } 627 } 628 629 const bool IsLinkerSymbol = SymbolName.starts_with("$ld$"); 630 631 if (R.isVerified()) { 632 // Check for unavailable symbols. 633 // This should only occur in the zippered case where we ignored 634 // availability until all headers have been parsed. 635 auto It = DeferredZipperedSymbols.find(SymCtx.SymbolName); 636 if (It == DeferredZipperedSymbols.end()) { 637 updateState(Result::Valid); 638 return; 639 } 640 641 ZipperedDeclSources Locs; 642 for (const ZipperedDeclSource &ZSource : It->second) { 643 if (ZSource.FA->Avail.isObsoleted()) { 644 updateState(Result::Ignore); 645 return; 646 } 647 if (ZSource.T.Arch != Ctx.Target.Arch) 648 continue; 649 Locs.emplace_back(ZSource); 650 } 651 assert(Locs.size() == 2 && "Expected two decls for zippered symbol"); 652 653 // Print violating declarations per platform. 654 for (const ZipperedDeclSource &ZSource : Locs) { 655 unsigned DiagID = 0; 656 if (Mode == VerificationMode::Pedantic || IsLinkerSymbol) { 657 updateState(Result::Invalid); 658 DiagID = diag::err_header_availability_mismatch; 659 } else if (Mode == VerificationMode::ErrorsAndWarnings) { 660 updateState(Result::Ignore); 661 DiagID = diag::warn_header_availability_mismatch; 662 } else { 663 updateState(Result::Ignore); 664 return; 665 } 666 // Bypass emitDiag banner and print the target everytime. 667 Ctx.Diag->setSourceManager(ZSource.SrcMgr); 668 Ctx.Diag->Report(diag::warn_target) << getTargetTripleName(ZSource.T); 669 Ctx.Diag->Report(ZSource.FA->Loc, DiagID) 670 << getAnnotatedName(&R, SymCtx) << ZSource.FA->Avail.isUnavailable() 671 << ZSource.FA->Avail.isUnavailable(); 672 } 673 return; 674 } 675 676 if (shouldIgnoreCpp(SymbolName, R.isWeakDefined())) { 677 updateState(Result::Valid); 678 return; 679 } 680 681 if (Aliases.count({SymbolName.str(), SymCtx.Kind})) { 682 updateState(Result::Valid); 683 return; 684 } 685 686 // All checks at this point classify as some kind of violation. 687 // The different verification modes dictate whether they are reported to the 688 // user. 689 if (IsLinkerSymbol || (Mode > VerificationMode::ErrorsOnly)) 690 accumulateSrcLocForDylibSymbols(); 691 RecordLoc Loc = DWARFCtx->SourceLocs.lookup(SymCtx.SymbolName); 692 693 // Regardless of verification mode, error out on mismatched special linker 694 // symbols. 695 if (IsLinkerSymbol) { 696 Ctx.emitDiag( 697 [&]() { 698 Ctx.Diag->Report(diag::err_header_symbol_missing) 699 << getAnnotatedName(&R, SymCtx, Loc.isValid()); 700 }, 701 &Loc); 702 updateState(Result::Invalid); 703 return; 704 } 705 706 // Missing declarations for exported symbols are hard errors on Pedantic mode. 707 if (Mode == VerificationMode::Pedantic) { 708 Ctx.emitDiag( 709 [&]() { 710 Ctx.Diag->Report(diag::err_header_symbol_missing) 711 << getAnnotatedName(&R, SymCtx, Loc.isValid()); 712 }, 713 &Loc); 714 updateState(Result::Invalid); 715 return; 716 } 717 718 // Missing declarations for exported symbols are warnings on ErrorsAndWarnings 719 // mode. 720 if (Mode == VerificationMode::ErrorsAndWarnings) { 721 Ctx.emitDiag( 722 [&]() { 723 Ctx.Diag->Report(diag::warn_header_symbol_missing) 724 << getAnnotatedName(&R, SymCtx, Loc.isValid()); 725 }, 726 &Loc); 727 updateState(Result::Ignore); 728 return; 729 } 730 731 // Missing declarations are dropped for ErrorsOnly mode. It is the last 732 // remaining mode. 733 updateState(Result::Ignore); 734 return; 735 } 736 737 void DylibVerifier::visitGlobal(const GlobalRecord &R) { 738 SymbolContext SymCtx; 739 SimpleSymbol Sym = parseSymbol(R.getName()); 740 SymCtx.SymbolName = Sym.Name; 741 SymCtx.Kind = Sym.Kind; 742 visitSymbolInDylib(R, SymCtx); 743 } 744 745 void DylibVerifier::visitObjCIVar(const ObjCIVarRecord &R, 746 const StringRef Super) { 747 SymbolContext SymCtx; 748 SymCtx.SymbolName = ObjCIVarRecord::createScopedName(Super, R.getName()); 749 SymCtx.Kind = EncodeKind::ObjectiveCInstanceVariable; 750 visitSymbolInDylib(R, SymCtx); 751 } 752 753 void DylibVerifier::accumulateSrcLocForDylibSymbols() { 754 if (DSYMPath.empty()) 755 return; 756 757 assert(DWARFCtx != nullptr && "Expected an initialized DWARFContext"); 758 if (DWARFCtx->ParsedDSYM) 759 return; 760 DWARFCtx->ParsedDSYM = true; 761 DWARFCtx->SourceLocs = 762 DylibReader::accumulateSourceLocFromDSYM(DSYMPath, Ctx.Target); 763 } 764 765 void DylibVerifier::visitObjCInterface(const ObjCInterfaceRecord &R) { 766 SymbolContext SymCtx; 767 SymCtx.SymbolName = R.getName(); 768 SymCtx.ObjCIFKind = assignObjCIFSymbolKind(&R); 769 if (SymCtx.ObjCIFKind > ObjCIFSymbolKind::EHType) { 770 if (R.hasExceptionAttribute()) { 771 SymCtx.Kind = EncodeKind::ObjectiveCClassEHType; 772 visitSymbolInDylib(R, SymCtx); 773 } 774 SymCtx.Kind = EncodeKind::ObjectiveCClass; 775 visitSymbolInDylib(R, SymCtx); 776 } else { 777 SymCtx.Kind = R.hasExceptionAttribute() ? EncodeKind::ObjectiveCClassEHType 778 : EncodeKind::ObjectiveCClass; 779 visitSymbolInDylib(R, SymCtx); 780 } 781 782 for (const ObjCIVarRecord *IV : R.getObjCIVars()) 783 visitObjCIVar(*IV, R.getName()); 784 } 785 786 void DylibVerifier::visitObjCCategory(const ObjCCategoryRecord &R) { 787 for (const ObjCIVarRecord *IV : R.getObjCIVars()) 788 visitObjCIVar(*IV, R.getSuperClassName()); 789 } 790 791 DylibVerifier::Result DylibVerifier::verifyRemainingSymbols() { 792 if (getState() == Result::NoVerify) 793 return Result::NoVerify; 794 assert(!Dylib.empty() && "No binary to verify against"); 795 796 DWARFContext DWARFInfo; 797 DWARFCtx = &DWARFInfo; 798 Ctx.Target = Target(Architecture::AK_unknown, PlatformType::PLATFORM_UNKNOWN); 799 for (std::shared_ptr<RecordsSlice> Slice : Dylib) { 800 if (Ctx.Target.Arch == Slice->getTarget().Arch) 801 continue; 802 Ctx.DiscoveredFirstError = false; 803 Ctx.PrintArch = true; 804 Ctx.Target = Slice->getTarget(); 805 Ctx.DylibSlice = Slice.get(); 806 Slice->visit(*this); 807 } 808 return getState(); 809 } 810 811 bool DylibVerifier::verifyBinaryAttrs(const ArrayRef<Target> ProvidedTargets, 812 const BinaryAttrs &ProvidedBA, 813 const LibAttrs &ProvidedReexports, 814 const LibAttrs &ProvidedClients, 815 const LibAttrs &ProvidedRPaths, 816 const FileType &FT) { 817 assert(!Dylib.empty() && "Need dylib to verify."); 818 819 // Pickup any load commands that can differ per slice to compare. 820 TargetList DylibTargets; 821 LibAttrs DylibReexports; 822 LibAttrs DylibClients; 823 LibAttrs DylibRPaths; 824 for (const std::shared_ptr<RecordsSlice> &RS : Dylib) { 825 DylibTargets.push_back(RS->getTarget()); 826 const BinaryAttrs &BinInfo = RS->getBinaryAttrs(); 827 for (const StringRef LibName : BinInfo.RexportedLibraries) 828 DylibReexports[LibName].set(DylibTargets.back().Arch); 829 for (const StringRef LibName : BinInfo.AllowableClients) 830 DylibClients[LibName].set(DylibTargets.back().Arch); 831 // Compare attributes that are only representable in >= TBD_V5. 832 if (FT >= FileType::TBD_V5) 833 for (const StringRef Name : BinInfo.RPaths) 834 DylibRPaths[Name].set(DylibTargets.back().Arch); 835 } 836 837 // Check targets first. 838 ArchitectureSet ProvidedArchs = mapToArchitectureSet(ProvidedTargets); 839 ArchitectureSet DylibArchs = mapToArchitectureSet(DylibTargets); 840 if (ProvidedArchs != DylibArchs) { 841 Ctx.Diag->Report(diag::err_architecture_mismatch) 842 << ProvidedArchs << DylibArchs; 843 return false; 844 } 845 auto ProvidedPlatforms = mapToPlatformVersionSet(ProvidedTargets); 846 auto DylibPlatforms = mapToPlatformVersionSet(DylibTargets); 847 if (ProvidedPlatforms != DylibPlatforms) { 848 const bool DiffMinOS = 849 mapToPlatformSet(ProvidedTargets) == mapToPlatformSet(DylibTargets); 850 if (DiffMinOS) 851 Ctx.Diag->Report(diag::warn_platform_mismatch) 852 << ProvidedPlatforms << DylibPlatforms; 853 else { 854 Ctx.Diag->Report(diag::err_platform_mismatch) 855 << ProvidedPlatforms << DylibPlatforms; 856 return false; 857 } 858 } 859 860 // Because InstallAPI requires certain attributes to match across architecture 861 // slices, take the first one to compare those with. 862 const BinaryAttrs &DylibBA = (*Dylib.begin())->getBinaryAttrs(); 863 864 if (ProvidedBA.InstallName != DylibBA.InstallName) { 865 Ctx.Diag->Report(diag::err_install_name_mismatch) 866 << ProvidedBA.InstallName << DylibBA.InstallName; 867 return false; 868 } 869 870 if (ProvidedBA.CurrentVersion != DylibBA.CurrentVersion) { 871 Ctx.Diag->Report(diag::err_current_version_mismatch) 872 << ProvidedBA.CurrentVersion << DylibBA.CurrentVersion; 873 return false; 874 } 875 876 if (ProvidedBA.CompatVersion != DylibBA.CompatVersion) { 877 Ctx.Diag->Report(diag::err_compatibility_version_mismatch) 878 << ProvidedBA.CompatVersion << DylibBA.CompatVersion; 879 return false; 880 } 881 882 if (ProvidedBA.AppExtensionSafe != DylibBA.AppExtensionSafe) { 883 Ctx.Diag->Report(diag::err_appextension_safe_mismatch) 884 << (ProvidedBA.AppExtensionSafe ? "true" : "false") 885 << (DylibBA.AppExtensionSafe ? "true" : "false"); 886 return false; 887 } 888 889 if (!DylibBA.TwoLevelNamespace) { 890 Ctx.Diag->Report(diag::err_no_twolevel_namespace); 891 return false; 892 } 893 894 if (ProvidedBA.OSLibNotForSharedCache != DylibBA.OSLibNotForSharedCache) { 895 Ctx.Diag->Report(diag::err_shared_cache_eligiblity_mismatch) 896 << (ProvidedBA.OSLibNotForSharedCache ? "true" : "false") 897 << (DylibBA.OSLibNotForSharedCache ? "true" : "false"); 898 return false; 899 } 900 901 if (ProvidedBA.ParentUmbrella.empty() && !DylibBA.ParentUmbrella.empty()) { 902 Ctx.Diag->Report(diag::err_parent_umbrella_missing) 903 << "installAPI option" << DylibBA.ParentUmbrella; 904 return false; 905 } 906 907 if (!ProvidedBA.ParentUmbrella.empty() && DylibBA.ParentUmbrella.empty()) { 908 Ctx.Diag->Report(diag::err_parent_umbrella_missing) 909 << "binary file" << ProvidedBA.ParentUmbrella; 910 return false; 911 } 912 913 if ((!ProvidedBA.ParentUmbrella.empty()) && 914 (ProvidedBA.ParentUmbrella != DylibBA.ParentUmbrella)) { 915 Ctx.Diag->Report(diag::err_parent_umbrella_mismatch) 916 << ProvidedBA.ParentUmbrella << DylibBA.ParentUmbrella; 917 return false; 918 } 919 920 auto CompareLibraries = [&](const LibAttrs &Provided, const LibAttrs &Dylib, 921 unsigned DiagID_missing, unsigned DiagID_mismatch, 922 bool Fatal = true) { 923 if (Provided == Dylib) 924 return true; 925 926 for (const llvm::StringMapEntry<ArchitectureSet> &PAttr : Provided) { 927 const auto DAttrIt = Dylib.find(PAttr.getKey()); 928 if (DAttrIt == Dylib.end()) { 929 Ctx.Diag->Report(DiagID_missing) << "binary file" << PAttr; 930 if (Fatal) 931 return false; 932 } 933 934 if (PAttr.getValue() != DAttrIt->getValue()) { 935 Ctx.Diag->Report(DiagID_mismatch) << PAttr << *DAttrIt; 936 if (Fatal) 937 return false; 938 } 939 } 940 941 for (const llvm::StringMapEntry<ArchitectureSet> &DAttr : Dylib) { 942 const auto PAttrIt = Provided.find(DAttr.getKey()); 943 if (PAttrIt == Provided.end()) { 944 Ctx.Diag->Report(DiagID_missing) << "installAPI option" << DAttr; 945 if (!Fatal) 946 continue; 947 return false; 948 } 949 950 if (PAttrIt->getValue() != DAttr.getValue()) { 951 if (Fatal) 952 llvm_unreachable("this case was already covered above."); 953 } 954 } 955 return true; 956 }; 957 958 if (!CompareLibraries(ProvidedReexports, DylibReexports, 959 diag::err_reexported_libraries_missing, 960 diag::err_reexported_libraries_mismatch)) 961 return false; 962 963 if (!CompareLibraries(ProvidedClients, DylibClients, 964 diag::err_allowable_clients_missing, 965 diag::err_allowable_clients_mismatch)) 966 return false; 967 968 if (FT >= FileType::TBD_V5) { 969 // Ignore rpath differences if building an asan variant, since the 970 // compiler injects additional paths. 971 // FIXME: Building with sanitizers does not always change the install 972 // name, so this is not a foolproof solution. 973 if (!ProvidedBA.InstallName.ends_with("_asan")) { 974 if (!CompareLibraries(ProvidedRPaths, DylibRPaths, 975 diag::warn_rpaths_missing, 976 diag::warn_rpaths_mismatch, 977 /*Fatal=*/false)) 978 return true; 979 } 980 } 981 982 return true; 983 } 984 985 std::unique_ptr<SymbolSet> DylibVerifier::takeExports() { 986 for (const auto &[Alias, Base] : Aliases) { 987 TargetList Targets; 988 SymbolFlags Flags = SymbolFlags::None; 989 if (const Symbol *Sym = Exports->findSymbol(Base.second, Base.first)) { 990 Flags = Sym->getFlags(); 991 Targets = {Sym->targets().begin(), Sym->targets().end()}; 992 } 993 994 Record R(Alias.first, RecordLinkage::Exported, Flags); 995 SymbolContext SymCtx; 996 SymCtx.SymbolName = Alias.first; 997 SymCtx.Kind = Alias.second; 998 addSymbol(&R, SymCtx, std::move(Targets)); 999 } 1000 1001 return std::move(Exports); 1002 } 1003 1004 } // namespace installapi 1005 } // namespace clang 1006