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