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