16d579cd1SCyndy Ishida //===- DylibVerifier.cpp ----------------------------------------*- C++--*-===// 26d579cd1SCyndy Ishida // 36d579cd1SCyndy Ishida // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 46d579cd1SCyndy Ishida // See https://llvm.org/LICENSE.txt for license information. 56d579cd1SCyndy Ishida // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 66d579cd1SCyndy Ishida // 76d579cd1SCyndy Ishida //===----------------------------------------------------------------------===// 86d579cd1SCyndy Ishida 9f2794cceSCyndy Ishida #include "clang/InstallAPI/DylibVerifier.h" 1027b2d7d4SCyndy Ishida #include "DiagnosticBuilderWrappers.h" 11f2794cceSCyndy Ishida #include "clang/InstallAPI/FrontendRecords.h" 12936519f2SCyndy Ishida #include "clang/InstallAPI/InstallAPIDiagnostic.h" 13f2794cceSCyndy Ishida #include "llvm/Demangle/Demangle.h" 14a4de589dSCyndy Ishida #include "llvm/TextAPI/DylibReader.h" 15f2794cceSCyndy Ishida 16f2794cceSCyndy Ishida using namespace llvm::MachO; 17f2794cceSCyndy Ishida 18f2794cceSCyndy Ishida namespace clang { 19f2794cceSCyndy Ishida namespace installapi { 20f2794cceSCyndy Ishida 21f2794cceSCyndy Ishida /// Metadata stored about a mapping of a declaration to a symbol. 22f2794cceSCyndy Ishida struct DylibVerifier::SymbolContext { 23f2794cceSCyndy Ishida // Name to use for all querying and verification 24f2794cceSCyndy Ishida // purposes. 25f2794cceSCyndy Ishida std::string SymbolName{""}; 26f2794cceSCyndy Ishida 27f2794cceSCyndy Ishida // Kind to map symbol type against record. 28f2794cceSCyndy Ishida EncodeKind Kind = EncodeKind::GlobalSymbol; 29f2794cceSCyndy Ishida 30f2794cceSCyndy Ishida // Frontend Attributes tied to the AST. 31f2794cceSCyndy Ishida const FrontendAttrs *FA = nullptr; 32f2794cceSCyndy Ishida 33f2794cceSCyndy Ishida // The ObjCInterface symbol type, if applicable. 34f2794cceSCyndy Ishida ObjCIFSymbolKind ObjCIFKind = ObjCIFSymbolKind::None; 35936519f2SCyndy Ishida 36936519f2SCyndy Ishida // Whether Decl is inlined. 37936519f2SCyndy Ishida bool Inlined = false; 38f2794cceSCyndy Ishida }; 39f2794cceSCyndy Ishida 40a4de589dSCyndy Ishida struct DylibVerifier::DWARFContext { 41a4de589dSCyndy Ishida // Track whether DSYM parsing has already been attempted to avoid re-parsing. 42a4de589dSCyndy Ishida bool ParsedDSYM{false}; 43a4de589dSCyndy Ishida 44a4de589dSCyndy Ishida // Lookup table for source locations by symbol name. 45a4de589dSCyndy Ishida DylibReader::SymbolToSourceLocMap SourceLocs{}; 46a4de589dSCyndy Ishida }; 47a4de589dSCyndy Ishida 489f168591SCyndy Ishida static bool isCppMangled(StringRef Name) { 499f168591SCyndy Ishida // InstallAPI currently only supports itanium manglings. 509f168591SCyndy Ishida return (Name.starts_with("_Z") || Name.starts_with("__Z") || 519f168591SCyndy Ishida Name.starts_with("___Z")); 529f168591SCyndy Ishida } 539f168591SCyndy Ishida 549f168591SCyndy Ishida static std::string demangle(StringRef Name) { 559f168591SCyndy Ishida // InstallAPI currently only supports itanium manglings. 569f168591SCyndy Ishida if (!isCppMangled(Name)) 579f168591SCyndy Ishida return Name.str(); 589f168591SCyndy Ishida char *Result = llvm::itaniumDemangle(Name); 599f168591SCyndy Ishida if (!Result) 609f168591SCyndy Ishida return Name.str(); 619f168591SCyndy Ishida 629f168591SCyndy Ishida std::string Demangled(Result); 639f168591SCyndy Ishida free(Result); 649f168591SCyndy Ishida return Demangled; 659f168591SCyndy Ishida } 669f168591SCyndy Ishida 679f168591SCyndy Ishida std::string DylibVerifier::getAnnotatedName(const Record *R, 689f168591SCyndy Ishida SymbolContext &SymCtx, 699f168591SCyndy Ishida bool ValidSourceLoc) { 709f168591SCyndy Ishida assert(!SymCtx.SymbolName.empty() && "Expected symbol name"); 719f168591SCyndy Ishida 729f168591SCyndy Ishida const StringRef SymbolName = SymCtx.SymbolName; 739f168591SCyndy Ishida std::string PrettyName = 749f168591SCyndy Ishida (Demangle && (SymCtx.Kind == EncodeKind::GlobalSymbol)) 759f168591SCyndy Ishida ? demangle(SymbolName) 769f168591SCyndy Ishida : SymbolName.str(); 77f2794cceSCyndy Ishida 78f2794cceSCyndy Ishida std::string Annotation; 79f2794cceSCyndy Ishida if (R->isWeakDefined()) 80f2794cceSCyndy Ishida Annotation += "(weak-def) "; 81f2794cceSCyndy Ishida if (R->isWeakReferenced()) 82f2794cceSCyndy Ishida Annotation += "(weak-ref) "; 83f2794cceSCyndy Ishida if (R->isThreadLocalValue()) 84f2794cceSCyndy Ishida Annotation += "(tlv) "; 85f2794cceSCyndy Ishida 86f2794cceSCyndy Ishida // Check if symbol represents only part of a @interface declaration. 87e470ca89SCyndy Ishida switch (SymCtx.ObjCIFKind) { 88e470ca89SCyndy Ishida default: 89e470ca89SCyndy Ishida break; 90e470ca89SCyndy Ishida case ObjCIFSymbolKind::EHType: 91e470ca89SCyndy Ishida return Annotation + "Exception Type of " + PrettyName; 92e470ca89SCyndy Ishida case ObjCIFSymbolKind::MetaClass: 93e470ca89SCyndy Ishida return Annotation + "Metaclass of " + PrettyName; 94e470ca89SCyndy Ishida case ObjCIFSymbolKind::Class: 95e470ca89SCyndy Ishida return Annotation + "Class of " + PrettyName; 96f2794cceSCyndy Ishida } 97f2794cceSCyndy Ishida 98f2794cceSCyndy Ishida // Only print symbol type prefix or leading "_" if there is no source location 99f2794cceSCyndy Ishida // tied to it. This can only ever happen when the location has to come from 100f2794cceSCyndy Ishida // debug info. 101f2794cceSCyndy Ishida if (ValidSourceLoc) { 1029f168591SCyndy Ishida StringRef PrettyNameRef(PrettyName); 1039f168591SCyndy Ishida if ((SymCtx.Kind == EncodeKind::GlobalSymbol) && 1049f168591SCyndy Ishida !isCppMangled(SymbolName) && PrettyNameRef.starts_with("_")) 1059f168591SCyndy Ishida return Annotation + PrettyNameRef.drop_front(1).str(); 1069f168591SCyndy Ishida return Annotation + PrettyName; 107f2794cceSCyndy Ishida } 108f2794cceSCyndy Ishida 1099f168591SCyndy Ishida switch (SymCtx.Kind) { 110f2794cceSCyndy Ishida case EncodeKind::GlobalSymbol: 1119f168591SCyndy Ishida return Annotation + PrettyName; 112f2794cceSCyndy Ishida case EncodeKind::ObjectiveCInstanceVariable: 1139f168591SCyndy Ishida return Annotation + "(ObjC IVar) " + PrettyName; 114f2794cceSCyndy Ishida case EncodeKind::ObjectiveCClass: 1159f168591SCyndy Ishida return Annotation + "(ObjC Class) " + PrettyName; 116f2794cceSCyndy Ishida case EncodeKind::ObjectiveCClassEHType: 1179f168591SCyndy Ishida return Annotation + "(ObjC Class EH) " + PrettyName; 118f2794cceSCyndy Ishida } 119f2794cceSCyndy Ishida 120f2794cceSCyndy Ishida llvm_unreachable("unexpected case for EncodeKind"); 121f2794cceSCyndy Ishida } 122f2794cceSCyndy Ishida 123f2794cceSCyndy Ishida static DylibVerifier::Result updateResult(const DylibVerifier::Result Prev, 124f2794cceSCyndy Ishida const DylibVerifier::Result Curr) { 125f2794cceSCyndy Ishida if (Prev == Curr) 126f2794cceSCyndy Ishida return Prev; 127f2794cceSCyndy Ishida 128f2794cceSCyndy Ishida // Never update from invalid or noverify state. 129f2794cceSCyndy Ishida if ((Prev == DylibVerifier::Result::Invalid) || 130f2794cceSCyndy Ishida (Prev == DylibVerifier::Result::NoVerify)) 131f2794cceSCyndy Ishida return Prev; 132f2794cceSCyndy Ishida 133f2794cceSCyndy Ishida // Don't let an ignored verification remove a valid one. 134f2794cceSCyndy Ishida if (Prev == DylibVerifier::Result::Valid && 135f2794cceSCyndy Ishida Curr == DylibVerifier::Result::Ignore) 136f2794cceSCyndy Ishida return Prev; 137f2794cceSCyndy Ishida 138f2794cceSCyndy Ishida return Curr; 139f2794cceSCyndy Ishida } 140936519f2SCyndy Ishida // __private_extern__ is a deprecated specifier that clang does not 141936519f2SCyndy Ishida // respect in all contexts, it should just be considered hidden for InstallAPI. 142936519f2SCyndy Ishida static bool shouldIgnorePrivateExternAttr(const Decl *D) { 143936519f2SCyndy Ishida if (const FunctionDecl *FD = cast<FunctionDecl>(D)) 144936519f2SCyndy Ishida return FD->getStorageClass() == StorageClass::SC_PrivateExtern; 145936519f2SCyndy Ishida if (const VarDecl *VD = cast<VarDecl>(D)) 146936519f2SCyndy Ishida return VD->getStorageClass() == StorageClass::SC_PrivateExtern; 147936519f2SCyndy Ishida 148936519f2SCyndy Ishida return false; 149936519f2SCyndy Ishida } 150936519f2SCyndy Ishida 151936519f2SCyndy Ishida Record *findRecordFromSlice(const RecordsSlice *Slice, StringRef Name, 152936519f2SCyndy Ishida EncodeKind Kind) { 153936519f2SCyndy Ishida switch (Kind) { 154936519f2SCyndy Ishida case EncodeKind::GlobalSymbol: 155936519f2SCyndy Ishida return Slice->findGlobal(Name); 156936519f2SCyndy Ishida case EncodeKind::ObjectiveCInstanceVariable: 157936519f2SCyndy Ishida return Slice->findObjCIVar(Name.contains('.'), Name); 158936519f2SCyndy Ishida case EncodeKind::ObjectiveCClass: 159936519f2SCyndy Ishida case EncodeKind::ObjectiveCClassEHType: 160936519f2SCyndy Ishida return Slice->findObjCInterface(Name); 161936519f2SCyndy Ishida } 162936519f2SCyndy Ishida llvm_unreachable("unexpected end when finding record"); 163936519f2SCyndy Ishida } 164f2794cceSCyndy Ishida 165f2794cceSCyndy Ishida void DylibVerifier::updateState(Result State) { 166f2794cceSCyndy Ishida Ctx.FrontendState = updateResult(Ctx.FrontendState, State); 167f2794cceSCyndy Ishida } 168f2794cceSCyndy Ishida 169f2794cceSCyndy Ishida void DylibVerifier::addSymbol(const Record *R, SymbolContext &SymCtx, 170f2794cceSCyndy Ishida TargetList &&Targets) { 171f2794cceSCyndy Ishida if (Targets.empty()) 172f2794cceSCyndy Ishida Targets = {Ctx.Target}; 173f2794cceSCyndy Ishida 174f2794cceSCyndy Ishida Exports->addGlobal(SymCtx.Kind, SymCtx.SymbolName, R->getFlags(), Targets); 175f2794cceSCyndy Ishida } 176f2794cceSCyndy Ishida 177936519f2SCyndy Ishida bool DylibVerifier::shouldIgnoreObsolete(const Record *R, SymbolContext &SymCtx, 178936519f2SCyndy Ishida const Record *DR) { 179c24efffaSCyndy Ishida if (!SymCtx.FA->Avail.isObsoleted()) 180c24efffaSCyndy Ishida return false; 181c24efffaSCyndy Ishida 182c24efffaSCyndy Ishida if (Zippered) 183c24efffaSCyndy Ishida DeferredZipperedSymbols[SymCtx.SymbolName].emplace_back(ZipperedDeclSource{ 184c24efffaSCyndy Ishida SymCtx.FA, &Ctx.Diag->getSourceManager(), Ctx.Target}); 185c24efffaSCyndy Ishida return true; 186936519f2SCyndy Ishida } 187936519f2SCyndy Ishida 18827b2d7d4SCyndy Ishida bool DylibVerifier::shouldIgnoreReexport(const Record *R, 18927b2d7d4SCyndy Ishida SymbolContext &SymCtx) const { 190*a4a8d36bSCyndy Ishida StringRef SymName = SymCtx.SymbolName; 191*a4a8d36bSCyndy Ishida // Linker directive symbols can never be ignored. 192*a4a8d36bSCyndy Ishida if (SymName.starts_with("$ld$")) 193*a4a8d36bSCyndy Ishida return false; 194*a4a8d36bSCyndy Ishida 19527b2d7d4SCyndy Ishida if (Reexports.empty()) 19627b2d7d4SCyndy Ishida return false; 19727b2d7d4SCyndy Ishida 19827b2d7d4SCyndy Ishida for (const InterfaceFile &Lib : Reexports) { 19927b2d7d4SCyndy Ishida if (!Lib.hasTarget(Ctx.Target)) 20027b2d7d4SCyndy Ishida continue; 201*a4a8d36bSCyndy Ishida if (auto Sym = Lib.getSymbol(SymCtx.Kind, SymName, SymCtx.ObjCIFKind)) 20227b2d7d4SCyndy Ishida if ((*Sym)->hasTarget(Ctx.Target)) 20327b2d7d4SCyndy Ishida return true; 20427b2d7d4SCyndy Ishida } 20527b2d7d4SCyndy Ishida return false; 20627b2d7d4SCyndy Ishida } 20727b2d7d4SCyndy Ishida 208c24efffaSCyndy Ishida bool DylibVerifier::shouldIgnoreInternalZipperedSymbol( 209c24efffaSCyndy Ishida const Record *R, const SymbolContext &SymCtx) const { 210c24efffaSCyndy Ishida if (!Zippered) 211c24efffaSCyndy Ishida return false; 212c24efffaSCyndy Ishida 213c24efffaSCyndy Ishida return Exports->findSymbol(SymCtx.Kind, SymCtx.SymbolName, 214c24efffaSCyndy Ishida SymCtx.ObjCIFKind) != nullptr; 215c24efffaSCyndy Ishida } 216c24efffaSCyndy Ishida 217c24efffaSCyndy Ishida bool DylibVerifier::shouldIgnoreZipperedAvailability(const Record *R, 218c24efffaSCyndy Ishida SymbolContext &SymCtx) { 219c24efffaSCyndy Ishida if (!(Zippered && SymCtx.FA->Avail.isUnavailable())) 220c24efffaSCyndy Ishida return false; 221c24efffaSCyndy Ishida 222c24efffaSCyndy Ishida // Collect source location incase there is an exported symbol to diagnose 223c24efffaSCyndy Ishida // during `verifyRemainingSymbols`. 224c24efffaSCyndy Ishida DeferredZipperedSymbols[SymCtx.SymbolName].emplace_back( 225c24efffaSCyndy Ishida ZipperedDeclSource{SymCtx.FA, SourceManagers.back().get(), Ctx.Target}); 226c24efffaSCyndy Ishida 227c24efffaSCyndy Ishida return true; 228c24efffaSCyndy Ishida } 229c24efffaSCyndy Ishida 230936519f2SCyndy Ishida bool DylibVerifier::compareObjCInterfaceSymbols(const Record *R, 231936519f2SCyndy Ishida SymbolContext &SymCtx, 232936519f2SCyndy Ishida const ObjCInterfaceRecord *DR) { 233936519f2SCyndy Ishida const bool IsDeclVersionComplete = 234936519f2SCyndy Ishida ((SymCtx.ObjCIFKind & ObjCIFSymbolKind::Class) == 235936519f2SCyndy Ishida ObjCIFSymbolKind::Class) && 236936519f2SCyndy Ishida ((SymCtx.ObjCIFKind & ObjCIFSymbolKind::MetaClass) == 237936519f2SCyndy Ishida ObjCIFSymbolKind::MetaClass); 238936519f2SCyndy Ishida 239936519f2SCyndy Ishida const bool IsDylibVersionComplete = DR->isCompleteInterface(); 240936519f2SCyndy Ishida 241936519f2SCyndy Ishida // The common case, a complete ObjCInterface. 242936519f2SCyndy Ishida if (IsDeclVersionComplete && IsDylibVersionComplete) 243936519f2SCyndy Ishida return true; 244936519f2SCyndy Ishida 245936519f2SCyndy Ishida auto PrintDiagnostic = [&](auto SymLinkage, const Record *Record, 246936519f2SCyndy Ishida StringRef SymName, bool PrintAsWarning = false) { 247936519f2SCyndy Ishida if (SymLinkage == RecordLinkage::Unknown) 248936519f2SCyndy Ishida Ctx.emitDiag([&]() { 249f04452deSCyndy Ishida Ctx.Diag->Report(SymCtx.FA->Loc, PrintAsWarning 250f04452deSCyndy Ishida ? diag::warn_library_missing_symbol 251936519f2SCyndy Ishida : diag::err_library_missing_symbol) 252936519f2SCyndy Ishida << SymName; 253936519f2SCyndy Ishida }); 254936519f2SCyndy Ishida else 255936519f2SCyndy Ishida Ctx.emitDiag([&]() { 256f04452deSCyndy Ishida Ctx.Diag->Report(SymCtx.FA->Loc, PrintAsWarning 257f04452deSCyndy Ishida ? diag::warn_library_hidden_symbol 258936519f2SCyndy Ishida : diag::err_library_hidden_symbol) 259936519f2SCyndy Ishida << SymName; 260936519f2SCyndy Ishida }); 261936519f2SCyndy Ishida }; 262936519f2SCyndy Ishida 263936519f2SCyndy Ishida if (IsDeclVersionComplete) { 264936519f2SCyndy Ishida // The decl represents a complete ObjCInterface, but the symbols in the 265936519f2SCyndy Ishida // dylib do not. Determine which symbol is missing. To keep older projects 266936519f2SCyndy Ishida // building, treat this as a warning. 2679f168591SCyndy Ishida if (!DR->isExportedSymbol(ObjCIFSymbolKind::Class)) { 2689f168591SCyndy Ishida SymCtx.ObjCIFKind = ObjCIFSymbolKind::Class; 269936519f2SCyndy Ishida PrintDiagnostic(DR->getLinkageForSymbol(ObjCIFSymbolKind::Class), R, 2709f168591SCyndy Ishida getAnnotatedName(R, SymCtx), 271936519f2SCyndy Ishida /*PrintAsWarning=*/true); 2729f168591SCyndy Ishida } 2739f168591SCyndy Ishida if (!DR->isExportedSymbol(ObjCIFSymbolKind::MetaClass)) { 2749f168591SCyndy Ishida SymCtx.ObjCIFKind = ObjCIFSymbolKind::MetaClass; 275936519f2SCyndy Ishida PrintDiagnostic(DR->getLinkageForSymbol(ObjCIFSymbolKind::MetaClass), R, 2769f168591SCyndy Ishida getAnnotatedName(R, SymCtx), 277936519f2SCyndy Ishida /*PrintAsWarning=*/true); 2789f168591SCyndy Ishida } 279936519f2SCyndy Ishida return true; 280936519f2SCyndy Ishida } 281936519f2SCyndy Ishida 282936519f2SCyndy Ishida if (DR->isExportedSymbol(SymCtx.ObjCIFKind)) { 283936519f2SCyndy Ishida if (!IsDylibVersionComplete) { 284936519f2SCyndy Ishida // Both the declaration and dylib have a non-complete interface. 285936519f2SCyndy Ishida SymCtx.Kind = EncodeKind::GlobalSymbol; 286936519f2SCyndy Ishida SymCtx.SymbolName = R->getName(); 287936519f2SCyndy Ishida } 288936519f2SCyndy Ishida return true; 289936519f2SCyndy Ishida } 290936519f2SCyndy Ishida 291936519f2SCyndy Ishida // At this point that means there was not a matching class symbol 292936519f2SCyndy Ishida // to represent the one discovered as a declaration. 293936519f2SCyndy Ishida PrintDiagnostic(DR->getLinkageForSymbol(SymCtx.ObjCIFKind), R, 2949f168591SCyndy Ishida SymCtx.SymbolName); 295936519f2SCyndy Ishida return false; 296936519f2SCyndy Ishida } 297936519f2SCyndy Ishida 298936519f2SCyndy Ishida DylibVerifier::Result DylibVerifier::compareVisibility(const Record *R, 299936519f2SCyndy Ishida SymbolContext &SymCtx, 300936519f2SCyndy Ishida const Record *DR) { 301936519f2SCyndy Ishida 302936519f2SCyndy Ishida if (R->isExported()) { 303936519f2SCyndy Ishida if (!DR) { 304936519f2SCyndy Ishida Ctx.emitDiag([&]() { 305f04452deSCyndy Ishida Ctx.Diag->Report(SymCtx.FA->Loc, diag::err_library_missing_symbol) 3069f168591SCyndy Ishida << getAnnotatedName(R, SymCtx); 307936519f2SCyndy Ishida }); 308936519f2SCyndy Ishida return Result::Invalid; 309936519f2SCyndy Ishida } 310936519f2SCyndy Ishida if (DR->isInternal()) { 311936519f2SCyndy Ishida Ctx.emitDiag([&]() { 312f04452deSCyndy Ishida Ctx.Diag->Report(SymCtx.FA->Loc, diag::err_library_hidden_symbol) 3139f168591SCyndy Ishida << getAnnotatedName(R, SymCtx); 314936519f2SCyndy Ishida }); 315936519f2SCyndy Ishida return Result::Invalid; 316936519f2SCyndy Ishida } 317936519f2SCyndy Ishida } 318936519f2SCyndy Ishida 319936519f2SCyndy Ishida // Emit a diagnostic for hidden declarations with external symbols, except 320936519f2SCyndy Ishida // when theres an inlined attribute. 321936519f2SCyndy Ishida if ((R->isInternal() && !SymCtx.Inlined) && DR && DR->isExported()) { 322936519f2SCyndy Ishida 323936519f2SCyndy Ishida if (Mode == VerificationMode::ErrorsOnly) 324936519f2SCyndy Ishida return Result::Ignore; 325936519f2SCyndy Ishida 326936519f2SCyndy Ishida if (shouldIgnorePrivateExternAttr(SymCtx.FA->D)) 327936519f2SCyndy Ishida return Result::Ignore; 328936519f2SCyndy Ishida 329c24efffaSCyndy Ishida if (shouldIgnoreInternalZipperedSymbol(R, SymCtx)) 330c24efffaSCyndy Ishida return Result::Ignore; 331c24efffaSCyndy Ishida 332936519f2SCyndy Ishida unsigned ID; 333936519f2SCyndy Ishida Result Outcome; 334936519f2SCyndy Ishida if (Mode == VerificationMode::ErrorsAndWarnings) { 335936519f2SCyndy Ishida ID = diag::warn_header_hidden_symbol; 336936519f2SCyndy Ishida Outcome = Result::Ignore; 337936519f2SCyndy Ishida } else { 338936519f2SCyndy Ishida ID = diag::err_header_hidden_symbol; 339936519f2SCyndy Ishida Outcome = Result::Invalid; 340936519f2SCyndy Ishida } 341936519f2SCyndy Ishida Ctx.emitDiag([&]() { 342f04452deSCyndy Ishida Ctx.Diag->Report(SymCtx.FA->Loc, ID) << getAnnotatedName(R, SymCtx); 343936519f2SCyndy Ishida }); 344936519f2SCyndy Ishida return Outcome; 345936519f2SCyndy Ishida } 346936519f2SCyndy Ishida 347936519f2SCyndy Ishida if (R->isInternal()) 348936519f2SCyndy Ishida return Result::Ignore; 349936519f2SCyndy Ishida 350936519f2SCyndy Ishida return Result::Valid; 351936519f2SCyndy Ishida } 352936519f2SCyndy Ishida 353936519f2SCyndy Ishida DylibVerifier::Result DylibVerifier::compareAvailability(const Record *R, 354936519f2SCyndy Ishida SymbolContext &SymCtx, 355936519f2SCyndy Ishida const Record *DR) { 356936519f2SCyndy Ishida if (!SymCtx.FA->Avail.isUnavailable()) 357936519f2SCyndy Ishida return Result::Valid; 358936519f2SCyndy Ishida 359c24efffaSCyndy Ishida if (shouldIgnoreZipperedAvailability(R, SymCtx)) 360c24efffaSCyndy Ishida return Result::Ignore; 361c24efffaSCyndy Ishida 362936519f2SCyndy Ishida const bool IsDeclAvailable = SymCtx.FA->Avail.isUnavailable(); 363936519f2SCyndy Ishida 364936519f2SCyndy Ishida switch (Mode) { 365936519f2SCyndy Ishida case VerificationMode::ErrorsAndWarnings: 366936519f2SCyndy Ishida Ctx.emitDiag([&]() { 367f04452deSCyndy Ishida Ctx.Diag->Report(SymCtx.FA->Loc, diag::warn_header_availability_mismatch) 3689f168591SCyndy Ishida << getAnnotatedName(R, SymCtx) << IsDeclAvailable << IsDeclAvailable; 369936519f2SCyndy Ishida }); 370936519f2SCyndy Ishida return Result::Ignore; 371936519f2SCyndy Ishida case VerificationMode::Pedantic: 372936519f2SCyndy Ishida Ctx.emitDiag([&]() { 373f04452deSCyndy Ishida Ctx.Diag->Report(SymCtx.FA->Loc, diag::err_header_availability_mismatch) 3749f168591SCyndy Ishida << getAnnotatedName(R, SymCtx) << IsDeclAvailable << IsDeclAvailable; 375936519f2SCyndy Ishida }); 376936519f2SCyndy Ishida return Result::Invalid; 377936519f2SCyndy Ishida case VerificationMode::ErrorsOnly: 378936519f2SCyndy Ishida return Result::Ignore; 379936519f2SCyndy Ishida case VerificationMode::Invalid: 380936519f2SCyndy Ishida llvm_unreachable("Unexpected verification mode symbol verification"); 381936519f2SCyndy Ishida } 382936519f2SCyndy Ishida llvm_unreachable("Unexpected verification mode symbol verification"); 383936519f2SCyndy Ishida } 384936519f2SCyndy Ishida 385936519f2SCyndy Ishida bool DylibVerifier::compareSymbolFlags(const Record *R, SymbolContext &SymCtx, 386936519f2SCyndy Ishida const Record *DR) { 387936519f2SCyndy Ishida if (DR->isThreadLocalValue() && !R->isThreadLocalValue()) { 388936519f2SCyndy Ishida Ctx.emitDiag([&]() { 389f04452deSCyndy Ishida Ctx.Diag->Report(SymCtx.FA->Loc, diag::err_dylib_symbol_flags_mismatch) 3909f168591SCyndy Ishida << getAnnotatedName(DR, SymCtx) << DR->isThreadLocalValue(); 391936519f2SCyndy Ishida }); 392936519f2SCyndy Ishida return false; 393936519f2SCyndy Ishida } 394936519f2SCyndy Ishida if (!DR->isThreadLocalValue() && R->isThreadLocalValue()) { 395936519f2SCyndy Ishida Ctx.emitDiag([&]() { 396f04452deSCyndy Ishida Ctx.Diag->Report(SymCtx.FA->Loc, diag::err_header_symbol_flags_mismatch) 397e470ca89SCyndy Ishida << getAnnotatedName(R, SymCtx) << R->isThreadLocalValue(); 398936519f2SCyndy Ishida }); 399936519f2SCyndy Ishida return false; 400936519f2SCyndy Ishida } 401936519f2SCyndy Ishida 402936519f2SCyndy Ishida if (DR->isWeakDefined() && !R->isWeakDefined()) { 403936519f2SCyndy Ishida Ctx.emitDiag([&]() { 404f04452deSCyndy Ishida Ctx.Diag->Report(SymCtx.FA->Loc, diag::err_dylib_symbol_flags_mismatch) 4059f168591SCyndy Ishida << getAnnotatedName(DR, SymCtx) << R->isWeakDefined(); 406936519f2SCyndy Ishida }); 407936519f2SCyndy Ishida return false; 408936519f2SCyndy Ishida } 409936519f2SCyndy Ishida if (!DR->isWeakDefined() && R->isWeakDefined()) { 410936519f2SCyndy Ishida Ctx.emitDiag([&]() { 411f04452deSCyndy Ishida Ctx.Diag->Report(SymCtx.FA->Loc, diag::err_header_symbol_flags_mismatch) 4129f168591SCyndy Ishida << getAnnotatedName(R, SymCtx) << R->isWeakDefined(); 413936519f2SCyndy Ishida }); 414936519f2SCyndy Ishida return false; 415936519f2SCyndy Ishida } 416936519f2SCyndy Ishida 417936519f2SCyndy Ishida return true; 418936519f2SCyndy Ishida } 419936519f2SCyndy Ishida 420f2794cceSCyndy Ishida DylibVerifier::Result DylibVerifier::verifyImpl(Record *R, 421f2794cceSCyndy Ishida SymbolContext &SymCtx) { 422f2794cceSCyndy Ishida R->setVerify(); 423f2794cceSCyndy Ishida if (!canVerify()) { 424f2794cceSCyndy Ishida // Accumulate symbols when not in verifying against dylib. 425936519f2SCyndy Ishida if (R->isExported() && !SymCtx.FA->Avail.isUnavailable() && 426f2794cceSCyndy Ishida !SymCtx.FA->Avail.isObsoleted()) { 427f2794cceSCyndy Ishida addSymbol(R, SymCtx); 428f2794cceSCyndy Ishida } 429f2794cceSCyndy Ishida return Ctx.FrontendState; 430f2794cceSCyndy Ishida } 431936519f2SCyndy Ishida 43227b2d7d4SCyndy Ishida if (shouldIgnoreReexport(R, SymCtx)) { 43327b2d7d4SCyndy Ishida updateState(Result::Ignore); 43427b2d7d4SCyndy Ishida return Ctx.FrontendState; 43527b2d7d4SCyndy Ishida } 43627b2d7d4SCyndy Ishida 437936519f2SCyndy Ishida Record *DR = 438936519f2SCyndy Ishida findRecordFromSlice(Ctx.DylibSlice, SymCtx.SymbolName, SymCtx.Kind); 439936519f2SCyndy Ishida if (DR) 440936519f2SCyndy Ishida DR->setVerify(); 441936519f2SCyndy Ishida 442936519f2SCyndy Ishida if (shouldIgnoreObsolete(R, SymCtx, DR)) { 443936519f2SCyndy Ishida updateState(Result::Ignore); 444936519f2SCyndy Ishida return Ctx.FrontendState; 445936519f2SCyndy Ishida } 446936519f2SCyndy Ishida 447936519f2SCyndy Ishida // Unavailable declarations don't need matching symbols. 448936519f2SCyndy Ishida if (SymCtx.FA->Avail.isUnavailable() && (!DR || DR->isInternal())) { 449936519f2SCyndy Ishida updateState(Result::Valid); 450936519f2SCyndy Ishida return Ctx.FrontendState; 451936519f2SCyndy Ishida } 452936519f2SCyndy Ishida 453936519f2SCyndy Ishida Result VisibilityCheck = compareVisibility(R, SymCtx, DR); 454936519f2SCyndy Ishida if (VisibilityCheck != Result::Valid) { 455936519f2SCyndy Ishida updateState(VisibilityCheck); 456936519f2SCyndy Ishida return Ctx.FrontendState; 457936519f2SCyndy Ishida } 458936519f2SCyndy Ishida 459936519f2SCyndy Ishida // All missing symbol cases to diagnose have been handled now. 460936519f2SCyndy Ishida if (!DR) { 461936519f2SCyndy Ishida updateState(Result::Ignore); 462936519f2SCyndy Ishida return Ctx.FrontendState; 463936519f2SCyndy Ishida } 464936519f2SCyndy Ishida 465936519f2SCyndy Ishida // Check for mismatching ObjC interfaces. 466936519f2SCyndy Ishida if (SymCtx.ObjCIFKind != ObjCIFSymbolKind::None) { 467936519f2SCyndy Ishida if (!compareObjCInterfaceSymbols( 468936519f2SCyndy Ishida R, SymCtx, Ctx.DylibSlice->findObjCInterface(DR->getName()))) { 469936519f2SCyndy Ishida updateState(Result::Invalid); 470936519f2SCyndy Ishida return Ctx.FrontendState; 471936519f2SCyndy Ishida } 472936519f2SCyndy Ishida } 473936519f2SCyndy Ishida 474936519f2SCyndy Ishida Result AvailabilityCheck = compareAvailability(R, SymCtx, DR); 475936519f2SCyndy Ishida if (AvailabilityCheck != Result::Valid) { 476936519f2SCyndy Ishida updateState(AvailabilityCheck); 477936519f2SCyndy Ishida return Ctx.FrontendState; 478936519f2SCyndy Ishida } 479936519f2SCyndy Ishida 480936519f2SCyndy Ishida if (!compareSymbolFlags(R, SymCtx, DR)) { 481936519f2SCyndy Ishida updateState(Result::Invalid); 482936519f2SCyndy Ishida return Ctx.FrontendState; 483936519f2SCyndy Ishida } 484936519f2SCyndy Ishida 485936519f2SCyndy Ishida addSymbol(R, SymCtx); 486936519f2SCyndy Ishida updateState(Result::Valid); 487f2794cceSCyndy Ishida return Ctx.FrontendState; 488f2794cceSCyndy Ishida } 489f2794cceSCyndy Ishida 490f2794cceSCyndy Ishida bool DylibVerifier::canVerify() { 491f2794cceSCyndy Ishida return Ctx.FrontendState != Result::NoVerify; 492f2794cceSCyndy Ishida } 493f2794cceSCyndy Ishida 494936519f2SCyndy Ishida void DylibVerifier::assignSlice(const Target &T) { 495936519f2SCyndy Ishida assert(T == Ctx.Target && "Active targets should match."); 496936519f2SCyndy Ishida if (Dylib.empty()) 497936519f2SCyndy Ishida return; 498936519f2SCyndy Ishida 499936519f2SCyndy Ishida // Note: there are no reexport slices with binaries, as opposed to TBD files, 500936519f2SCyndy Ishida // so it can be assumed that the target match is the active top-level library. 501936519f2SCyndy Ishida auto It = find_if( 502936519f2SCyndy Ishida Dylib, [&T](const auto &Slice) { return T == Slice->getTarget(); }); 503936519f2SCyndy Ishida 504936519f2SCyndy Ishida assert(It != Dylib.end() && "Target slice should always exist."); 505936519f2SCyndy Ishida Ctx.DylibSlice = It->get(); 506936519f2SCyndy Ishida } 507936519f2SCyndy Ishida 508f2794cceSCyndy Ishida void DylibVerifier::setTarget(const Target &T) { 509f2794cceSCyndy Ishida Ctx.Target = T; 510f2794cceSCyndy Ishida Ctx.DiscoveredFirstError = false; 511936519f2SCyndy Ishida if (Dylib.empty()) { 512936519f2SCyndy Ishida updateState(Result::NoVerify); 513936519f2SCyndy Ishida return; 514936519f2SCyndy Ishida } 515936519f2SCyndy Ishida updateState(Result::Ignore); 516936519f2SCyndy Ishida assignSlice(T); 517f2794cceSCyndy Ishida } 518f2794cceSCyndy Ishida 519f04452deSCyndy Ishida void DylibVerifier::setSourceManager( 520f04452deSCyndy Ishida IntrusiveRefCntPtr<SourceManager> SourceMgr) { 521f04452deSCyndy Ishida if (!Ctx.Diag) 522f04452deSCyndy Ishida return; 523f04452deSCyndy Ishida SourceManagers.push_back(std::move(SourceMgr)); 524f04452deSCyndy Ishida Ctx.Diag->setSourceManager(SourceManagers.back().get()); 525f04452deSCyndy Ishida } 526f04452deSCyndy Ishida 527f2794cceSCyndy Ishida DylibVerifier::Result DylibVerifier::verify(ObjCIVarRecord *R, 528f2794cceSCyndy Ishida const FrontendAttrs *FA, 529f2794cceSCyndy Ishida const StringRef SuperClass) { 530f2794cceSCyndy Ishida if (R->isVerified()) 531f2794cceSCyndy Ishida return getState(); 532f2794cceSCyndy Ishida 533f2794cceSCyndy Ishida std::string FullName = 534f2794cceSCyndy Ishida ObjCIVarRecord::createScopedName(SuperClass, R->getName()); 5359f168591SCyndy Ishida SymbolContext SymCtx{FullName, EncodeKind::ObjectiveCInstanceVariable, FA}; 536f2794cceSCyndy Ishida return verifyImpl(R, SymCtx); 537f2794cceSCyndy Ishida } 538f2794cceSCyndy Ishida 539f2794cceSCyndy Ishida static ObjCIFSymbolKind assignObjCIFSymbolKind(const ObjCInterfaceRecord *R) { 540f2794cceSCyndy Ishida ObjCIFSymbolKind Result = ObjCIFSymbolKind::None; 541f2794cceSCyndy Ishida if (R->getLinkageForSymbol(ObjCIFSymbolKind::Class) != RecordLinkage::Unknown) 542f2794cceSCyndy Ishida Result |= ObjCIFSymbolKind::Class; 543f2794cceSCyndy Ishida if (R->getLinkageForSymbol(ObjCIFSymbolKind::MetaClass) != 544f2794cceSCyndy Ishida RecordLinkage::Unknown) 545f2794cceSCyndy Ishida Result |= ObjCIFSymbolKind::MetaClass; 546f2794cceSCyndy Ishida if (R->getLinkageForSymbol(ObjCIFSymbolKind::EHType) != 547f2794cceSCyndy Ishida RecordLinkage::Unknown) 548f2794cceSCyndy Ishida Result |= ObjCIFSymbolKind::EHType; 549f2794cceSCyndy Ishida return Result; 550f2794cceSCyndy Ishida } 551f2794cceSCyndy Ishida 552f2794cceSCyndy Ishida DylibVerifier::Result DylibVerifier::verify(ObjCInterfaceRecord *R, 553f2794cceSCyndy Ishida const FrontendAttrs *FA) { 554f2794cceSCyndy Ishida if (R->isVerified()) 555f2794cceSCyndy Ishida return getState(); 556f2794cceSCyndy Ishida SymbolContext SymCtx; 557f2794cceSCyndy Ishida SymCtx.SymbolName = R->getName(); 558f2794cceSCyndy Ishida SymCtx.ObjCIFKind = assignObjCIFSymbolKind(R); 559f2794cceSCyndy Ishida 560f2794cceSCyndy Ishida SymCtx.Kind = R->hasExceptionAttribute() ? EncodeKind::ObjectiveCClassEHType 561f2794cceSCyndy Ishida : EncodeKind::ObjectiveCClass; 562f2794cceSCyndy Ishida SymCtx.FA = FA; 563f2794cceSCyndy Ishida 564f2794cceSCyndy Ishida return verifyImpl(R, SymCtx); 565f2794cceSCyndy Ishida } 566f2794cceSCyndy Ishida 567f2794cceSCyndy Ishida DylibVerifier::Result DylibVerifier::verify(GlobalRecord *R, 568f2794cceSCyndy Ishida const FrontendAttrs *FA) { 569f2794cceSCyndy Ishida if (R->isVerified()) 570f2794cceSCyndy Ishida return getState(); 571f2794cceSCyndy Ishida 572f2794cceSCyndy Ishida // Global classifications could be obfusciated with `asm`. 573f2794cceSCyndy Ishida SimpleSymbol Sym = parseSymbol(R->getName()); 574f2794cceSCyndy Ishida SymbolContext SymCtx; 575f2794cceSCyndy Ishida SymCtx.SymbolName = Sym.Name; 576f2794cceSCyndy Ishida SymCtx.Kind = Sym.Kind; 577f2794cceSCyndy Ishida SymCtx.FA = FA; 578936519f2SCyndy Ishida SymCtx.Inlined = R->isInlined(); 579f2794cceSCyndy Ishida return verifyImpl(R, SymCtx); 580f2794cceSCyndy Ishida } 581f2794cceSCyndy Ishida 582a4de589dSCyndy Ishida void DylibVerifier::VerifierContext::emitDiag(llvm::function_ref<void()> Report, 583a4de589dSCyndy Ishida RecordLoc *Loc) { 584936519f2SCyndy Ishida if (!DiscoveredFirstError) { 585936519f2SCyndy Ishida Diag->Report(diag::warn_target) 586936519f2SCyndy Ishida << (PrintArch ? getArchitectureName(Target.Arch) 587936519f2SCyndy Ishida : getTargetTripleName(Target)); 588936519f2SCyndy Ishida DiscoveredFirstError = true; 589936519f2SCyndy Ishida } 590a4de589dSCyndy Ishida if (Loc && Loc->isValid()) 591a4de589dSCyndy Ishida llvm::errs() << Loc->File << ":" << Loc->Line << ":" << 0 << ": "; 592936519f2SCyndy Ishida 593936519f2SCyndy Ishida Report(); 594936519f2SCyndy Ishida } 595936519f2SCyndy Ishida 596e470ca89SCyndy Ishida // The existence of weak-defined RTTI can not always be inferred from the 597e470ca89SCyndy Ishida // header files because they can be generated as part of an implementation 598e470ca89SCyndy Ishida // file. 599e470ca89SCyndy Ishida // InstallAPI doesn't warn about weak-defined RTTI, because this doesn't affect 600e470ca89SCyndy Ishida // static linking and so can be ignored for text-api files. 601e470ca89SCyndy Ishida static bool shouldIgnoreCpp(StringRef Name, bool IsWeakDef) { 602e470ca89SCyndy Ishida return (IsWeakDef && 603e470ca89SCyndy Ishida (Name.starts_with("__ZTI") || Name.starts_with("__ZTS"))); 604e470ca89SCyndy Ishida } 605e470ca89SCyndy Ishida void DylibVerifier::visitSymbolInDylib(const Record &R, SymbolContext &SymCtx) { 606e470ca89SCyndy Ishida // Undefined symbols should not be in InstallAPI generated text-api files. 607e470ca89SCyndy Ishida if (R.isUndefined()) { 608e470ca89SCyndy Ishida updateState(Result::Valid); 609e470ca89SCyndy Ishida return; 610e470ca89SCyndy Ishida } 611e470ca89SCyndy Ishida 612e470ca89SCyndy Ishida // Internal symbols should not be in InstallAPI generated text-api files. 613e470ca89SCyndy Ishida if (R.isInternal()) { 614e470ca89SCyndy Ishida updateState(Result::Valid); 615e470ca89SCyndy Ishida return; 616e470ca89SCyndy Ishida } 617e470ca89SCyndy Ishida 618e470ca89SCyndy Ishida // Allow zippered symbols with potentially mismatching availability 619e470ca89SCyndy Ishida // between macOS and macCatalyst in the final text-api file. 620e470ca89SCyndy Ishida const StringRef SymbolName(SymCtx.SymbolName); 621e470ca89SCyndy Ishida if (const Symbol *Sym = Exports->findSymbol(SymCtx.Kind, SymCtx.SymbolName, 622e470ca89SCyndy Ishida SymCtx.ObjCIFKind)) { 623e470ca89SCyndy Ishida if (Sym->hasArchitecture(Ctx.Target.Arch)) { 624e470ca89SCyndy Ishida updateState(Result::Ignore); 625e470ca89SCyndy Ishida return; 626e470ca89SCyndy Ishida } 627e470ca89SCyndy Ishida } 628e470ca89SCyndy Ishida 629c24efffaSCyndy Ishida const bool IsLinkerSymbol = SymbolName.starts_with("$ld$"); 630c24efffaSCyndy Ishida 631c24efffaSCyndy Ishida if (R.isVerified()) { 632c24efffaSCyndy Ishida // Check for unavailable symbols. 633c24efffaSCyndy Ishida // This should only occur in the zippered case where we ignored 634c24efffaSCyndy Ishida // availability until all headers have been parsed. 635c24efffaSCyndy Ishida auto It = DeferredZipperedSymbols.find(SymCtx.SymbolName); 636c24efffaSCyndy Ishida if (It == DeferredZipperedSymbols.end()) { 637e470ca89SCyndy Ishida updateState(Result::Valid); 638e470ca89SCyndy Ishida return; 639e470ca89SCyndy Ishida } 640e470ca89SCyndy Ishida 641c24efffaSCyndy Ishida ZipperedDeclSources Locs; 642c24efffaSCyndy Ishida for (const ZipperedDeclSource &ZSource : It->second) { 643c24efffaSCyndy Ishida if (ZSource.FA->Avail.isObsoleted()) { 644c24efffaSCyndy Ishida updateState(Result::Ignore); 645c24efffaSCyndy Ishida return; 646c24efffaSCyndy Ishida } 647c24efffaSCyndy Ishida if (ZSource.T.Arch != Ctx.Target.Arch) 648c24efffaSCyndy Ishida continue; 649c24efffaSCyndy Ishida Locs.emplace_back(ZSource); 650c24efffaSCyndy Ishida } 651c24efffaSCyndy Ishida assert(Locs.size() == 2 && "Expected two decls for zippered symbol"); 652c24efffaSCyndy Ishida 653c24efffaSCyndy Ishida // Print violating declarations per platform. 654c24efffaSCyndy Ishida for (const ZipperedDeclSource &ZSource : Locs) { 655c24efffaSCyndy Ishida unsigned DiagID = 0; 656c24efffaSCyndy Ishida if (Mode == VerificationMode::Pedantic || IsLinkerSymbol) { 657c24efffaSCyndy Ishida updateState(Result::Invalid); 658c24efffaSCyndy Ishida DiagID = diag::err_header_availability_mismatch; 659c24efffaSCyndy Ishida } else if (Mode == VerificationMode::ErrorsAndWarnings) { 660c24efffaSCyndy Ishida updateState(Result::Ignore); 661c24efffaSCyndy Ishida DiagID = diag::warn_header_availability_mismatch; 662c24efffaSCyndy Ishida } else { 663c24efffaSCyndy Ishida updateState(Result::Ignore); 664c24efffaSCyndy Ishida return; 665c24efffaSCyndy Ishida } 666c24efffaSCyndy Ishida // Bypass emitDiag banner and print the target everytime. 667c24efffaSCyndy Ishida Ctx.Diag->setSourceManager(ZSource.SrcMgr); 668c24efffaSCyndy Ishida Ctx.Diag->Report(diag::warn_target) << getTargetTripleName(ZSource.T); 669c24efffaSCyndy Ishida Ctx.Diag->Report(ZSource.FA->Loc, DiagID) 670c24efffaSCyndy Ishida << getAnnotatedName(&R, SymCtx) << ZSource.FA->Avail.isUnavailable() 671c24efffaSCyndy Ishida << ZSource.FA->Avail.isUnavailable(); 672c24efffaSCyndy Ishida } 673c24efffaSCyndy Ishida return; 674c24efffaSCyndy Ishida } 675c24efffaSCyndy Ishida 676c24efffaSCyndy Ishida if (shouldIgnoreCpp(SymbolName, R.isWeakDefined())) { 677c24efffaSCyndy Ishida updateState(Result::Valid); 678c24efffaSCyndy Ishida return; 679c24efffaSCyndy Ishida } 680a4de589dSCyndy Ishida 6814c18681aSCyndy Ishida if (Aliases.count({SymbolName.str(), SymCtx.Kind})) { 6824c18681aSCyndy Ishida updateState(Result::Valid); 6834c18681aSCyndy Ishida return; 6844c18681aSCyndy Ishida } 6854c18681aSCyndy Ishida 686a4de589dSCyndy Ishida // All checks at this point classify as some kind of violation. 687a4de589dSCyndy Ishida // The different verification modes dictate whether they are reported to the 688a4de589dSCyndy Ishida // user. 689a4de589dSCyndy Ishida if (IsLinkerSymbol || (Mode > VerificationMode::ErrorsOnly)) 690a4de589dSCyndy Ishida accumulateSrcLocForDylibSymbols(); 691a4de589dSCyndy Ishida RecordLoc Loc = DWARFCtx->SourceLocs.lookup(SymCtx.SymbolName); 692e470ca89SCyndy Ishida 693e470ca89SCyndy Ishida // Regardless of verification mode, error out on mismatched special linker 694e470ca89SCyndy Ishida // symbols. 695a4de589dSCyndy Ishida if (IsLinkerSymbol) { 696a4de589dSCyndy Ishida Ctx.emitDiag( 697a4de589dSCyndy Ishida [&]() { 698e470ca89SCyndy Ishida Ctx.Diag->Report(diag::err_header_symbol_missing) 699a4de589dSCyndy Ishida << getAnnotatedName(&R, SymCtx, Loc.isValid()); 700a4de589dSCyndy Ishida }, 701a4de589dSCyndy Ishida &Loc); 702e470ca89SCyndy Ishida updateState(Result::Invalid); 703e470ca89SCyndy Ishida return; 704e470ca89SCyndy Ishida } 705e470ca89SCyndy Ishida 706e470ca89SCyndy Ishida // Missing declarations for exported symbols are hard errors on Pedantic mode. 707e470ca89SCyndy Ishida if (Mode == VerificationMode::Pedantic) { 708a4de589dSCyndy Ishida Ctx.emitDiag( 709a4de589dSCyndy Ishida [&]() { 710e470ca89SCyndy Ishida Ctx.Diag->Report(diag::err_header_symbol_missing) 711a4de589dSCyndy Ishida << getAnnotatedName(&R, SymCtx, Loc.isValid()); 712a4de589dSCyndy Ishida }, 713a4de589dSCyndy Ishida &Loc); 714e470ca89SCyndy Ishida updateState(Result::Invalid); 715e470ca89SCyndy Ishida return; 716e470ca89SCyndy Ishida } 717e470ca89SCyndy Ishida 718e470ca89SCyndy Ishida // Missing declarations for exported symbols are warnings on ErrorsAndWarnings 719e470ca89SCyndy Ishida // mode. 720e470ca89SCyndy Ishida if (Mode == VerificationMode::ErrorsAndWarnings) { 721a4de589dSCyndy Ishida Ctx.emitDiag( 722a4de589dSCyndy Ishida [&]() { 723e470ca89SCyndy Ishida Ctx.Diag->Report(diag::warn_header_symbol_missing) 724a4de589dSCyndy Ishida << getAnnotatedName(&R, SymCtx, Loc.isValid()); 725a4de589dSCyndy Ishida }, 726a4de589dSCyndy Ishida &Loc); 727e470ca89SCyndy Ishida updateState(Result::Ignore); 728e470ca89SCyndy Ishida return; 729e470ca89SCyndy Ishida } 730e470ca89SCyndy Ishida 731e470ca89SCyndy Ishida // Missing declarations are dropped for ErrorsOnly mode. It is the last 732e470ca89SCyndy Ishida // remaining mode. 733e470ca89SCyndy Ishida updateState(Result::Ignore); 734e470ca89SCyndy Ishida return; 735e470ca89SCyndy Ishida } 736e470ca89SCyndy Ishida 737e470ca89SCyndy Ishida void DylibVerifier::visitGlobal(const GlobalRecord &R) { 738e470ca89SCyndy Ishida SymbolContext SymCtx; 739e470ca89SCyndy Ishida SimpleSymbol Sym = parseSymbol(R.getName()); 740e470ca89SCyndy Ishida SymCtx.SymbolName = Sym.Name; 741e470ca89SCyndy Ishida SymCtx.Kind = Sym.Kind; 742e470ca89SCyndy Ishida visitSymbolInDylib(R, SymCtx); 743e470ca89SCyndy Ishida } 744e470ca89SCyndy Ishida 745e470ca89SCyndy Ishida void DylibVerifier::visitObjCIVar(const ObjCIVarRecord &R, 746e470ca89SCyndy Ishida const StringRef Super) { 747e470ca89SCyndy Ishida SymbolContext SymCtx; 748e470ca89SCyndy Ishida SymCtx.SymbolName = ObjCIVarRecord::createScopedName(Super, R.getName()); 749e470ca89SCyndy Ishida SymCtx.Kind = EncodeKind::ObjectiveCInstanceVariable; 750e470ca89SCyndy Ishida visitSymbolInDylib(R, SymCtx); 751e470ca89SCyndy Ishida } 752e470ca89SCyndy Ishida 753a4de589dSCyndy Ishida void DylibVerifier::accumulateSrcLocForDylibSymbols() { 754a4de589dSCyndy Ishida if (DSYMPath.empty()) 755a4de589dSCyndy Ishida return; 756a4de589dSCyndy Ishida 757a4de589dSCyndy Ishida assert(DWARFCtx != nullptr && "Expected an initialized DWARFContext"); 758a4de589dSCyndy Ishida if (DWARFCtx->ParsedDSYM) 759a4de589dSCyndy Ishida return; 760a4de589dSCyndy Ishida DWARFCtx->ParsedDSYM = true; 761a4de589dSCyndy Ishida DWARFCtx->SourceLocs = 762a4de589dSCyndy Ishida DylibReader::accumulateSourceLocFromDSYM(DSYMPath, Ctx.Target); 763a4de589dSCyndy Ishida } 764a4de589dSCyndy Ishida 765e470ca89SCyndy Ishida void DylibVerifier::visitObjCInterface(const ObjCInterfaceRecord &R) { 766e470ca89SCyndy Ishida SymbolContext SymCtx; 767e470ca89SCyndy Ishida SymCtx.SymbolName = R.getName(); 768e470ca89SCyndy Ishida SymCtx.ObjCIFKind = assignObjCIFSymbolKind(&R); 769e470ca89SCyndy Ishida if (SymCtx.ObjCIFKind > ObjCIFSymbolKind::EHType) { 770e470ca89SCyndy Ishida if (R.hasExceptionAttribute()) { 771e470ca89SCyndy Ishida SymCtx.Kind = EncodeKind::ObjectiveCClassEHType; 772e470ca89SCyndy Ishida visitSymbolInDylib(R, SymCtx); 773e470ca89SCyndy Ishida } 774e470ca89SCyndy Ishida SymCtx.Kind = EncodeKind::ObjectiveCClass; 775e470ca89SCyndy Ishida visitSymbolInDylib(R, SymCtx); 776e470ca89SCyndy Ishida } else { 777e470ca89SCyndy Ishida SymCtx.Kind = R.hasExceptionAttribute() ? EncodeKind::ObjectiveCClassEHType 778e470ca89SCyndy Ishida : EncodeKind::ObjectiveCClass; 779e470ca89SCyndy Ishida visitSymbolInDylib(R, SymCtx); 780e470ca89SCyndy Ishida } 781e470ca89SCyndy Ishida 782e470ca89SCyndy Ishida for (const ObjCIVarRecord *IV : R.getObjCIVars()) 783e470ca89SCyndy Ishida visitObjCIVar(*IV, R.getName()); 784e470ca89SCyndy Ishida } 785e470ca89SCyndy Ishida 786e470ca89SCyndy Ishida void DylibVerifier::visitObjCCategory(const ObjCCategoryRecord &R) { 787e470ca89SCyndy Ishida for (const ObjCIVarRecord *IV : R.getObjCIVars()) 788e470ca89SCyndy Ishida visitObjCIVar(*IV, R.getSuperClassName()); 789e470ca89SCyndy Ishida } 790e470ca89SCyndy Ishida 791e470ca89SCyndy Ishida DylibVerifier::Result DylibVerifier::verifyRemainingSymbols() { 792e470ca89SCyndy Ishida if (getState() == Result::NoVerify) 793e470ca89SCyndy Ishida return Result::NoVerify; 794e470ca89SCyndy Ishida assert(!Dylib.empty() && "No binary to verify against"); 795e470ca89SCyndy Ishida 796a4de589dSCyndy Ishida DWARFContext DWARFInfo; 797a4de589dSCyndy Ishida DWARFCtx = &DWARFInfo; 798c24efffaSCyndy Ishida Ctx.Target = Target(Architecture::AK_unknown, PlatformType::PLATFORM_UNKNOWN); 799c24efffaSCyndy Ishida for (std::shared_ptr<RecordsSlice> Slice : Dylib) { 800c24efffaSCyndy Ishida if (Ctx.Target.Arch == Slice->getTarget().Arch) 801c24efffaSCyndy Ishida continue; 802e470ca89SCyndy Ishida Ctx.DiscoveredFirstError = false; 803e470ca89SCyndy Ishida Ctx.PrintArch = true; 804e470ca89SCyndy Ishida Ctx.Target = Slice->getTarget(); 805e470ca89SCyndy Ishida Ctx.DylibSlice = Slice.get(); 806e470ca89SCyndy Ishida Slice->visit(*this); 807e470ca89SCyndy Ishida } 808e470ca89SCyndy Ishida return getState(); 809e470ca89SCyndy Ishida } 810e470ca89SCyndy Ishida 81127b2d7d4SCyndy Ishida bool DylibVerifier::verifyBinaryAttrs(const ArrayRef<Target> ProvidedTargets, 81227b2d7d4SCyndy Ishida const BinaryAttrs &ProvidedBA, 81327b2d7d4SCyndy Ishida const LibAttrs &ProvidedReexports, 81427b2d7d4SCyndy Ishida const LibAttrs &ProvidedClients, 81527b2d7d4SCyndy Ishida const LibAttrs &ProvidedRPaths, 81627b2d7d4SCyndy Ishida const FileType &FT) { 81727b2d7d4SCyndy Ishida assert(!Dylib.empty() && "Need dylib to verify."); 81827b2d7d4SCyndy Ishida 81927b2d7d4SCyndy Ishida // Pickup any load commands that can differ per slice to compare. 82027b2d7d4SCyndy Ishida TargetList DylibTargets; 82127b2d7d4SCyndy Ishida LibAttrs DylibReexports; 82227b2d7d4SCyndy Ishida LibAttrs DylibClients; 82327b2d7d4SCyndy Ishida LibAttrs DylibRPaths; 82427b2d7d4SCyndy Ishida for (const std::shared_ptr<RecordsSlice> &RS : Dylib) { 82527b2d7d4SCyndy Ishida DylibTargets.push_back(RS->getTarget()); 82627b2d7d4SCyndy Ishida const BinaryAttrs &BinInfo = RS->getBinaryAttrs(); 82727b2d7d4SCyndy Ishida for (const StringRef LibName : BinInfo.RexportedLibraries) 82827b2d7d4SCyndy Ishida DylibReexports[LibName].set(DylibTargets.back().Arch); 82927b2d7d4SCyndy Ishida for (const StringRef LibName : BinInfo.AllowableClients) 83027b2d7d4SCyndy Ishida DylibClients[LibName].set(DylibTargets.back().Arch); 83127b2d7d4SCyndy Ishida // Compare attributes that are only representable in >= TBD_V5. 83227b2d7d4SCyndy Ishida if (FT >= FileType::TBD_V5) 83327b2d7d4SCyndy Ishida for (const StringRef Name : BinInfo.RPaths) 83427b2d7d4SCyndy Ishida DylibRPaths[Name].set(DylibTargets.back().Arch); 83527b2d7d4SCyndy Ishida } 83627b2d7d4SCyndy Ishida 83727b2d7d4SCyndy Ishida // Check targets first. 83827b2d7d4SCyndy Ishida ArchitectureSet ProvidedArchs = mapToArchitectureSet(ProvidedTargets); 83927b2d7d4SCyndy Ishida ArchitectureSet DylibArchs = mapToArchitectureSet(DylibTargets); 84027b2d7d4SCyndy Ishida if (ProvidedArchs != DylibArchs) { 84127b2d7d4SCyndy Ishida Ctx.Diag->Report(diag::err_architecture_mismatch) 84227b2d7d4SCyndy Ishida << ProvidedArchs << DylibArchs; 84327b2d7d4SCyndy Ishida return false; 84427b2d7d4SCyndy Ishida } 84527b2d7d4SCyndy Ishida auto ProvidedPlatforms = mapToPlatformVersionSet(ProvidedTargets); 84627b2d7d4SCyndy Ishida auto DylibPlatforms = mapToPlatformVersionSet(DylibTargets); 84727b2d7d4SCyndy Ishida if (ProvidedPlatforms != DylibPlatforms) { 84827b2d7d4SCyndy Ishida const bool DiffMinOS = 84927b2d7d4SCyndy Ishida mapToPlatformSet(ProvidedTargets) == mapToPlatformSet(DylibTargets); 85027b2d7d4SCyndy Ishida if (DiffMinOS) 85127b2d7d4SCyndy Ishida Ctx.Diag->Report(diag::warn_platform_mismatch) 85227b2d7d4SCyndy Ishida << ProvidedPlatforms << DylibPlatforms; 85327b2d7d4SCyndy Ishida else { 85427b2d7d4SCyndy Ishida Ctx.Diag->Report(diag::err_platform_mismatch) 85527b2d7d4SCyndy Ishida << ProvidedPlatforms << DylibPlatforms; 85627b2d7d4SCyndy Ishida return false; 85727b2d7d4SCyndy Ishida } 85827b2d7d4SCyndy Ishida } 85927b2d7d4SCyndy Ishida 86027b2d7d4SCyndy Ishida // Because InstallAPI requires certain attributes to match across architecture 86127b2d7d4SCyndy Ishida // slices, take the first one to compare those with. 86227b2d7d4SCyndy Ishida const BinaryAttrs &DylibBA = (*Dylib.begin())->getBinaryAttrs(); 86327b2d7d4SCyndy Ishida 86427b2d7d4SCyndy Ishida if (ProvidedBA.InstallName != DylibBA.InstallName) { 86527b2d7d4SCyndy Ishida Ctx.Diag->Report(diag::err_install_name_mismatch) 86627b2d7d4SCyndy Ishida << ProvidedBA.InstallName << DylibBA.InstallName; 86727b2d7d4SCyndy Ishida return false; 86827b2d7d4SCyndy Ishida } 86927b2d7d4SCyndy Ishida 87027b2d7d4SCyndy Ishida if (ProvidedBA.CurrentVersion != DylibBA.CurrentVersion) { 87127b2d7d4SCyndy Ishida Ctx.Diag->Report(diag::err_current_version_mismatch) 87227b2d7d4SCyndy Ishida << ProvidedBA.CurrentVersion << DylibBA.CurrentVersion; 87327b2d7d4SCyndy Ishida return false; 87427b2d7d4SCyndy Ishida } 87527b2d7d4SCyndy Ishida 87627b2d7d4SCyndy Ishida if (ProvidedBA.CompatVersion != DylibBA.CompatVersion) { 87727b2d7d4SCyndy Ishida Ctx.Diag->Report(diag::err_compatibility_version_mismatch) 87827b2d7d4SCyndy Ishida << ProvidedBA.CompatVersion << DylibBA.CompatVersion; 87927b2d7d4SCyndy Ishida return false; 88027b2d7d4SCyndy Ishida } 88127b2d7d4SCyndy Ishida 88227b2d7d4SCyndy Ishida if (ProvidedBA.AppExtensionSafe != DylibBA.AppExtensionSafe) { 88327b2d7d4SCyndy Ishida Ctx.Diag->Report(diag::err_appextension_safe_mismatch) 88427b2d7d4SCyndy Ishida << (ProvidedBA.AppExtensionSafe ? "true" : "false") 88527b2d7d4SCyndy Ishida << (DylibBA.AppExtensionSafe ? "true" : "false"); 88627b2d7d4SCyndy Ishida return false; 88727b2d7d4SCyndy Ishida } 88827b2d7d4SCyndy Ishida 88927b2d7d4SCyndy Ishida if (!DylibBA.TwoLevelNamespace) { 89027b2d7d4SCyndy Ishida Ctx.Diag->Report(diag::err_no_twolevel_namespace); 89127b2d7d4SCyndy Ishida return false; 89227b2d7d4SCyndy Ishida } 89327b2d7d4SCyndy Ishida 89427b2d7d4SCyndy Ishida if (ProvidedBA.OSLibNotForSharedCache != DylibBA.OSLibNotForSharedCache) { 89527b2d7d4SCyndy Ishida Ctx.Diag->Report(diag::err_shared_cache_eligiblity_mismatch) 89627b2d7d4SCyndy Ishida << (ProvidedBA.OSLibNotForSharedCache ? "true" : "false") 89727b2d7d4SCyndy Ishida << (DylibBA.OSLibNotForSharedCache ? "true" : "false"); 89827b2d7d4SCyndy Ishida return false; 89927b2d7d4SCyndy Ishida } 90027b2d7d4SCyndy Ishida 90127b2d7d4SCyndy Ishida if (ProvidedBA.ParentUmbrella.empty() && !DylibBA.ParentUmbrella.empty()) { 90227b2d7d4SCyndy Ishida Ctx.Diag->Report(diag::err_parent_umbrella_missing) 90327b2d7d4SCyndy Ishida << "installAPI option" << DylibBA.ParentUmbrella; 90427b2d7d4SCyndy Ishida return false; 90527b2d7d4SCyndy Ishida } 90627b2d7d4SCyndy Ishida 90727b2d7d4SCyndy Ishida if (!ProvidedBA.ParentUmbrella.empty() && DylibBA.ParentUmbrella.empty()) { 90827b2d7d4SCyndy Ishida Ctx.Diag->Report(diag::err_parent_umbrella_missing) 90927b2d7d4SCyndy Ishida << "binary file" << ProvidedBA.ParentUmbrella; 91027b2d7d4SCyndy Ishida return false; 91127b2d7d4SCyndy Ishida } 91227b2d7d4SCyndy Ishida 91327b2d7d4SCyndy Ishida if ((!ProvidedBA.ParentUmbrella.empty()) && 91427b2d7d4SCyndy Ishida (ProvidedBA.ParentUmbrella != DylibBA.ParentUmbrella)) { 91527b2d7d4SCyndy Ishida Ctx.Diag->Report(diag::err_parent_umbrella_mismatch) 91627b2d7d4SCyndy Ishida << ProvidedBA.ParentUmbrella << DylibBA.ParentUmbrella; 91727b2d7d4SCyndy Ishida return false; 91827b2d7d4SCyndy Ishida } 91927b2d7d4SCyndy Ishida 92027b2d7d4SCyndy Ishida auto CompareLibraries = [&](const LibAttrs &Provided, const LibAttrs &Dylib, 92127b2d7d4SCyndy Ishida unsigned DiagID_missing, unsigned DiagID_mismatch, 92227b2d7d4SCyndy Ishida bool Fatal = true) { 92327b2d7d4SCyndy Ishida if (Provided == Dylib) 92427b2d7d4SCyndy Ishida return true; 92527b2d7d4SCyndy Ishida 92627b2d7d4SCyndy Ishida for (const llvm::StringMapEntry<ArchitectureSet> &PAttr : Provided) { 92727b2d7d4SCyndy Ishida const auto DAttrIt = Dylib.find(PAttr.getKey()); 92827b2d7d4SCyndy Ishida if (DAttrIt == Dylib.end()) { 92927b2d7d4SCyndy Ishida Ctx.Diag->Report(DiagID_missing) << "binary file" << PAttr; 93027b2d7d4SCyndy Ishida if (Fatal) 93127b2d7d4SCyndy Ishida return false; 93227b2d7d4SCyndy Ishida } 93327b2d7d4SCyndy Ishida 93427b2d7d4SCyndy Ishida if (PAttr.getValue() != DAttrIt->getValue()) { 93527b2d7d4SCyndy Ishida Ctx.Diag->Report(DiagID_mismatch) << PAttr << *DAttrIt; 93627b2d7d4SCyndy Ishida if (Fatal) 93727b2d7d4SCyndy Ishida return false; 93827b2d7d4SCyndy Ishida } 93927b2d7d4SCyndy Ishida } 94027b2d7d4SCyndy Ishida 94127b2d7d4SCyndy Ishida for (const llvm::StringMapEntry<ArchitectureSet> &DAttr : Dylib) { 94227b2d7d4SCyndy Ishida const auto PAttrIt = Provided.find(DAttr.getKey()); 94327b2d7d4SCyndy Ishida if (PAttrIt == Provided.end()) { 94427b2d7d4SCyndy Ishida Ctx.Diag->Report(DiagID_missing) << "installAPI option" << DAttr; 94527b2d7d4SCyndy Ishida if (!Fatal) 94627b2d7d4SCyndy Ishida continue; 94727b2d7d4SCyndy Ishida return false; 94827b2d7d4SCyndy Ishida } 94927b2d7d4SCyndy Ishida 95027b2d7d4SCyndy Ishida if (PAttrIt->getValue() != DAttr.getValue()) { 95127b2d7d4SCyndy Ishida if (Fatal) 95227b2d7d4SCyndy Ishida llvm_unreachable("this case was already covered above."); 95327b2d7d4SCyndy Ishida } 95427b2d7d4SCyndy Ishida } 95527b2d7d4SCyndy Ishida return true; 95627b2d7d4SCyndy Ishida }; 95727b2d7d4SCyndy Ishida 95827b2d7d4SCyndy Ishida if (!CompareLibraries(ProvidedReexports, DylibReexports, 95927b2d7d4SCyndy Ishida diag::err_reexported_libraries_missing, 96027b2d7d4SCyndy Ishida diag::err_reexported_libraries_mismatch)) 96127b2d7d4SCyndy Ishida return false; 96227b2d7d4SCyndy Ishida 96327b2d7d4SCyndy Ishida if (!CompareLibraries(ProvidedClients, DylibClients, 96427b2d7d4SCyndy Ishida diag::err_allowable_clients_missing, 96527b2d7d4SCyndy Ishida diag::err_allowable_clients_mismatch)) 96627b2d7d4SCyndy Ishida return false; 96727b2d7d4SCyndy Ishida 96827b2d7d4SCyndy Ishida if (FT >= FileType::TBD_V5) { 96927b2d7d4SCyndy Ishida // Ignore rpath differences if building an asan variant, since the 97027b2d7d4SCyndy Ishida // compiler injects additional paths. 97127b2d7d4SCyndy Ishida // FIXME: Building with sanitizers does not always change the install 97227b2d7d4SCyndy Ishida // name, so this is not a foolproof solution. 97327b2d7d4SCyndy Ishida if (!ProvidedBA.InstallName.ends_with("_asan")) { 97427b2d7d4SCyndy Ishida if (!CompareLibraries(ProvidedRPaths, DylibRPaths, 97527b2d7d4SCyndy Ishida diag::warn_rpaths_missing, 97627b2d7d4SCyndy Ishida diag::warn_rpaths_mismatch, 97727b2d7d4SCyndy Ishida /*Fatal=*/false)) 97827b2d7d4SCyndy Ishida return true; 97927b2d7d4SCyndy Ishida } 98027b2d7d4SCyndy Ishida } 98127b2d7d4SCyndy Ishida 98227b2d7d4SCyndy Ishida return true; 98327b2d7d4SCyndy Ishida } 98427b2d7d4SCyndy Ishida 9854c18681aSCyndy Ishida std::unique_ptr<SymbolSet> DylibVerifier::takeExports() { 9864c18681aSCyndy Ishida for (const auto &[Alias, Base] : Aliases) { 9874c18681aSCyndy Ishida TargetList Targets; 9884c18681aSCyndy Ishida SymbolFlags Flags = SymbolFlags::None; 9894c18681aSCyndy Ishida if (const Symbol *Sym = Exports->findSymbol(Base.second, Base.first)) { 9904c18681aSCyndy Ishida Flags = Sym->getFlags(); 9914c18681aSCyndy Ishida Targets = {Sym->targets().begin(), Sym->targets().end()}; 9924c18681aSCyndy Ishida } 9934c18681aSCyndy Ishida 9944c18681aSCyndy Ishida Record R(Alias.first, RecordLinkage::Exported, Flags); 9954c18681aSCyndy Ishida SymbolContext SymCtx; 9964c18681aSCyndy Ishida SymCtx.SymbolName = Alias.first; 9974c18681aSCyndy Ishida SymCtx.Kind = Alias.second; 9984c18681aSCyndy Ishida addSymbol(&R, SymCtx, std::move(Targets)); 9994c18681aSCyndy Ishida } 10004c18681aSCyndy Ishida 10014c18681aSCyndy Ishida return std::move(Exports); 10024c18681aSCyndy Ishida } 10034c18681aSCyndy Ishida 1004f2794cceSCyndy Ishida } // namespace installapi 1005f2794cceSCyndy Ishida } // namespace clang 1006