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