aboutsummaryrefslogtreecommitdiff
path: root/contrib/llvm-project/clang/lib/InstallAPI/DylibVerifier.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/llvm-project/clang/lib/InstallAPI/DylibVerifier.cpp')
-rw-r--r--contrib/llvm-project/clang/lib/InstallAPI/DylibVerifier.cpp1005
1 files changed, 1005 insertions, 0 deletions
diff --git a/contrib/llvm-project/clang/lib/InstallAPI/DylibVerifier.cpp b/contrib/llvm-project/clang/lib/InstallAPI/DylibVerifier.cpp
new file mode 100644
index 000000000000..d5d760767b41
--- /dev/null
+++ b/contrib/llvm-project/clang/lib/InstallAPI/DylibVerifier.cpp
@@ -0,0 +1,1005 @@
+//===- DylibVerifier.cpp ----------------------------------------*- C++--*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/InstallAPI/DylibVerifier.h"
+#include "DiagnosticBuilderWrappers.h"
+#include "clang/InstallAPI/FrontendRecords.h"
+#include "clang/InstallAPI/InstallAPIDiagnostic.h"
+#include "llvm/Demangle/Demangle.h"
+#include "llvm/TextAPI/DylibReader.h"
+
+using namespace llvm::MachO;
+
+namespace clang {
+namespace installapi {
+
+/// Metadata stored about a mapping of a declaration to a symbol.
+struct DylibVerifier::SymbolContext {
+ // Name to use for all querying and verification
+ // purposes.
+ std::string SymbolName{""};
+
+ // Kind to map symbol type against record.
+ EncodeKind Kind = EncodeKind::GlobalSymbol;
+
+ // Frontend Attributes tied to the AST.
+ const FrontendAttrs *FA = nullptr;
+
+ // The ObjCInterface symbol type, if applicable.
+ ObjCIFSymbolKind ObjCIFKind = ObjCIFSymbolKind::None;
+
+ // Whether Decl is inlined.
+ bool Inlined = false;
+};
+
+struct DylibVerifier::DWARFContext {
+ // Track whether DSYM parsing has already been attempted to avoid re-parsing.
+ bool ParsedDSYM{false};
+
+ // Lookup table for source locations by symbol name.
+ DylibReader::SymbolToSourceLocMap SourceLocs{};
+};
+
+static bool isCppMangled(StringRef Name) {
+ // InstallAPI currently only supports itanium manglings.
+ return (Name.starts_with("_Z") || Name.starts_with("__Z") ||
+ Name.starts_with("___Z"));
+}
+
+static std::string demangle(StringRef Name) {
+ // InstallAPI currently only supports itanium manglings.
+ if (!isCppMangled(Name))
+ return Name.str();
+ char *Result = llvm::itaniumDemangle(Name);
+ if (!Result)
+ return Name.str();
+
+ std::string Demangled(Result);
+ free(Result);
+ return Demangled;
+}
+
+std::string DylibVerifier::getAnnotatedName(const Record *R,
+ SymbolContext &SymCtx,
+ bool ValidSourceLoc) {
+ assert(!SymCtx.SymbolName.empty() && "Expected symbol name");
+
+ const StringRef SymbolName = SymCtx.SymbolName;
+ std::string PrettyName =
+ (Demangle && (SymCtx.Kind == EncodeKind::GlobalSymbol))
+ ? demangle(SymbolName)
+ : SymbolName.str();
+
+ std::string Annotation;
+ if (R->isWeakDefined())
+ Annotation += "(weak-def) ";
+ if (R->isWeakReferenced())
+ Annotation += "(weak-ref) ";
+ if (R->isThreadLocalValue())
+ Annotation += "(tlv) ";
+
+ // Check if symbol represents only part of a @interface declaration.
+ switch (SymCtx.ObjCIFKind) {
+ default:
+ break;
+ case ObjCIFSymbolKind::EHType:
+ return Annotation + "Exception Type of " + PrettyName;
+ case ObjCIFSymbolKind::MetaClass:
+ return Annotation + "Metaclass of " + PrettyName;
+ case ObjCIFSymbolKind::Class:
+ return Annotation + "Class of " + PrettyName;
+ }
+
+ // Only print symbol type prefix or leading "_" if there is no source location
+ // tied to it. This can only ever happen when the location has to come from
+ // debug info.
+ if (ValidSourceLoc) {
+ StringRef PrettyNameRef(PrettyName);
+ if ((SymCtx.Kind == EncodeKind::GlobalSymbol) &&
+ !isCppMangled(SymbolName) && PrettyNameRef.starts_with("_"))
+ return Annotation + PrettyNameRef.drop_front(1).str();
+ return Annotation + PrettyName;
+ }
+
+ switch (SymCtx.Kind) {
+ case EncodeKind::GlobalSymbol:
+ return Annotation + PrettyName;
+ case EncodeKind::ObjectiveCInstanceVariable:
+ return Annotation + "(ObjC IVar) " + PrettyName;
+ case EncodeKind::ObjectiveCClass:
+ return Annotation + "(ObjC Class) " + PrettyName;
+ case EncodeKind::ObjectiveCClassEHType:
+ return Annotation + "(ObjC Class EH) " + PrettyName;
+ }
+
+ llvm_unreachable("unexpected case for EncodeKind");
+}
+
+static DylibVerifier::Result updateResult(const DylibVerifier::Result Prev,
+ const DylibVerifier::Result Curr) {
+ if (Prev == Curr)
+ return Prev;
+
+ // Never update from invalid or noverify state.
+ if ((Prev == DylibVerifier::Result::Invalid) ||
+ (Prev == DylibVerifier::Result::NoVerify))
+ return Prev;
+
+ // Don't let an ignored verification remove a valid one.
+ if (Prev == DylibVerifier::Result::Valid &&
+ Curr == DylibVerifier::Result::Ignore)
+ return Prev;
+
+ return Curr;
+}
+// __private_extern__ is a deprecated specifier that clang does not
+// respect in all contexts, it should just be considered hidden for InstallAPI.
+static bool shouldIgnorePrivateExternAttr(const Decl *D) {
+ if (const FunctionDecl *FD = cast<FunctionDecl>(D))
+ return FD->getStorageClass() == StorageClass::SC_PrivateExtern;
+ if (const VarDecl *VD = cast<VarDecl>(D))
+ return VD->getStorageClass() == StorageClass::SC_PrivateExtern;
+
+ return false;
+}
+
+Record *findRecordFromSlice(const RecordsSlice *Slice, StringRef Name,
+ EncodeKind Kind) {
+ switch (Kind) {
+ case EncodeKind::GlobalSymbol:
+ return Slice->findGlobal(Name);
+ case EncodeKind::ObjectiveCInstanceVariable:
+ return Slice->findObjCIVar(Name.contains('.'), Name);
+ case EncodeKind::ObjectiveCClass:
+ case EncodeKind::ObjectiveCClassEHType:
+ return Slice->findObjCInterface(Name);
+ }
+ llvm_unreachable("unexpected end when finding record");
+}
+
+void DylibVerifier::updateState(Result State) {
+ Ctx.FrontendState = updateResult(Ctx.FrontendState, State);
+}
+
+void DylibVerifier::addSymbol(const Record *R, SymbolContext &SymCtx,
+ TargetList &&Targets) {
+ if (Targets.empty())
+ Targets = {Ctx.Target};
+
+ Exports->addGlobal(SymCtx.Kind, SymCtx.SymbolName, R->getFlags(), Targets);
+}
+
+bool DylibVerifier::shouldIgnoreObsolete(const Record *R, SymbolContext &SymCtx,
+ const Record *DR) {
+ if (!SymCtx.FA->Avail.isObsoleted())
+ return false;
+
+ if (Zippered)
+ DeferredZipperedSymbols[SymCtx.SymbolName].emplace_back(ZipperedDeclSource{
+ SymCtx.FA, &Ctx.Diag->getSourceManager(), Ctx.Target});
+ return true;
+}
+
+bool DylibVerifier::shouldIgnoreReexport(const Record *R,
+ SymbolContext &SymCtx) const {
+ StringRef SymName = SymCtx.SymbolName;
+ // Linker directive symbols can never be ignored.
+ if (SymName.starts_with("$ld$"))
+ return false;
+
+ if (Reexports.empty())
+ return false;
+
+ for (const InterfaceFile &Lib : Reexports) {
+ if (!Lib.hasTarget(Ctx.Target))
+ continue;
+ if (auto Sym = Lib.getSymbol(SymCtx.Kind, SymName, SymCtx.ObjCIFKind))
+ if ((*Sym)->hasTarget(Ctx.Target))
+ return true;
+ }
+ return false;
+}
+
+bool DylibVerifier::shouldIgnoreInternalZipperedSymbol(
+ const Record *R, const SymbolContext &SymCtx) const {
+ if (!Zippered)
+ return false;
+
+ return Exports->findSymbol(SymCtx.Kind, SymCtx.SymbolName,
+ SymCtx.ObjCIFKind) != nullptr;
+}
+
+bool DylibVerifier::shouldIgnoreZipperedAvailability(const Record *R,
+ SymbolContext &SymCtx) {
+ if (!(Zippered && SymCtx.FA->Avail.isUnavailable()))
+ return false;
+
+ // Collect source location incase there is an exported symbol to diagnose
+ // during `verifyRemainingSymbols`.
+ DeferredZipperedSymbols[SymCtx.SymbolName].emplace_back(
+ ZipperedDeclSource{SymCtx.FA, SourceManagers.back().get(), Ctx.Target});
+
+ return true;
+}
+
+bool DylibVerifier::compareObjCInterfaceSymbols(const Record *R,
+ SymbolContext &SymCtx,
+ const ObjCInterfaceRecord *DR) {
+ const bool IsDeclVersionComplete =
+ ((SymCtx.ObjCIFKind & ObjCIFSymbolKind::Class) ==
+ ObjCIFSymbolKind::Class) &&
+ ((SymCtx.ObjCIFKind & ObjCIFSymbolKind::MetaClass) ==
+ ObjCIFSymbolKind::MetaClass);
+
+ const bool IsDylibVersionComplete = DR->isCompleteInterface();
+
+ // The common case, a complete ObjCInterface.
+ if (IsDeclVersionComplete && IsDylibVersionComplete)
+ return true;
+
+ auto PrintDiagnostic = [&](auto SymLinkage, const Record *Record,
+ StringRef SymName, bool PrintAsWarning = false) {
+ if (SymLinkage == RecordLinkage::Unknown)
+ Ctx.emitDiag([&]() {
+ Ctx.Diag->Report(SymCtx.FA->Loc, PrintAsWarning
+ ? diag::warn_library_missing_symbol
+ : diag::err_library_missing_symbol)
+ << SymName;
+ });
+ else
+ Ctx.emitDiag([&]() {
+ Ctx.Diag->Report(SymCtx.FA->Loc, PrintAsWarning
+ ? diag::warn_library_hidden_symbol
+ : diag::err_library_hidden_symbol)
+ << SymName;
+ });
+ };
+
+ if (IsDeclVersionComplete) {
+ // The decl represents a complete ObjCInterface, but the symbols in the
+ // dylib do not. Determine which symbol is missing. To keep older projects
+ // building, treat this as a warning.
+ if (!DR->isExportedSymbol(ObjCIFSymbolKind::Class)) {
+ SymCtx.ObjCIFKind = ObjCIFSymbolKind::Class;
+ PrintDiagnostic(DR->getLinkageForSymbol(ObjCIFSymbolKind::Class), R,
+ getAnnotatedName(R, SymCtx),
+ /*PrintAsWarning=*/true);
+ }
+ if (!DR->isExportedSymbol(ObjCIFSymbolKind::MetaClass)) {
+ SymCtx.ObjCIFKind = ObjCIFSymbolKind::MetaClass;
+ PrintDiagnostic(DR->getLinkageForSymbol(ObjCIFSymbolKind::MetaClass), R,
+ getAnnotatedName(R, SymCtx),
+ /*PrintAsWarning=*/true);
+ }
+ return true;
+ }
+
+ if (DR->isExportedSymbol(SymCtx.ObjCIFKind)) {
+ if (!IsDylibVersionComplete) {
+ // Both the declaration and dylib have a non-complete interface.
+ SymCtx.Kind = EncodeKind::GlobalSymbol;
+ SymCtx.SymbolName = R->getName();
+ }
+ return true;
+ }
+
+ // At this point that means there was not a matching class symbol
+ // to represent the one discovered as a declaration.
+ PrintDiagnostic(DR->getLinkageForSymbol(SymCtx.ObjCIFKind), R,
+ SymCtx.SymbolName);
+ return false;
+}
+
+DylibVerifier::Result DylibVerifier::compareVisibility(const Record *R,
+ SymbolContext &SymCtx,
+ const Record *DR) {
+
+ if (R->isExported()) {
+ if (!DR) {
+ Ctx.emitDiag([&]() {
+ Ctx.Diag->Report(SymCtx.FA->Loc, diag::err_library_missing_symbol)
+ << getAnnotatedName(R, SymCtx);
+ });
+ return Result::Invalid;
+ }
+ if (DR->isInternal()) {
+ Ctx.emitDiag([&]() {
+ Ctx.Diag->Report(SymCtx.FA->Loc, diag::err_library_hidden_symbol)
+ << getAnnotatedName(R, SymCtx);
+ });
+ return Result::Invalid;
+ }
+ }
+
+ // Emit a diagnostic for hidden declarations with external symbols, except
+ // when theres an inlined attribute.
+ if ((R->isInternal() && !SymCtx.Inlined) && DR && DR->isExported()) {
+
+ if (Mode == VerificationMode::ErrorsOnly)
+ return Result::Ignore;
+
+ if (shouldIgnorePrivateExternAttr(SymCtx.FA->D))
+ return Result::Ignore;
+
+ if (shouldIgnoreInternalZipperedSymbol(R, SymCtx))
+ return Result::Ignore;
+
+ unsigned ID;
+ Result Outcome;
+ if (Mode == VerificationMode::ErrorsAndWarnings) {
+ ID = diag::warn_header_hidden_symbol;
+ Outcome = Result::Ignore;
+ } else {
+ ID = diag::err_header_hidden_symbol;
+ Outcome = Result::Invalid;
+ }
+ Ctx.emitDiag([&]() {
+ Ctx.Diag->Report(SymCtx.FA->Loc, ID) << getAnnotatedName(R, SymCtx);
+ });
+ return Outcome;
+ }
+
+ if (R->isInternal())
+ return Result::Ignore;
+
+ return Result::Valid;
+}
+
+DylibVerifier::Result DylibVerifier::compareAvailability(const Record *R,
+ SymbolContext &SymCtx,
+ const Record *DR) {
+ if (!SymCtx.FA->Avail.isUnavailable())
+ return Result::Valid;
+
+ if (shouldIgnoreZipperedAvailability(R, SymCtx))
+ return Result::Ignore;
+
+ const bool IsDeclAvailable = SymCtx.FA->Avail.isUnavailable();
+
+ switch (Mode) {
+ case VerificationMode::ErrorsAndWarnings:
+ Ctx.emitDiag([&]() {
+ Ctx.Diag->Report(SymCtx.FA->Loc, diag::warn_header_availability_mismatch)
+ << getAnnotatedName(R, SymCtx) << IsDeclAvailable << IsDeclAvailable;
+ });
+ return Result::Ignore;
+ case VerificationMode::Pedantic:
+ Ctx.emitDiag([&]() {
+ Ctx.Diag->Report(SymCtx.FA->Loc, diag::err_header_availability_mismatch)
+ << getAnnotatedName(R, SymCtx) << IsDeclAvailable << IsDeclAvailable;
+ });
+ return Result::Invalid;
+ case VerificationMode::ErrorsOnly:
+ return Result::Ignore;
+ case VerificationMode::Invalid:
+ llvm_unreachable("Unexpected verification mode symbol verification");
+ }
+ llvm_unreachable("Unexpected verification mode symbol verification");
+}
+
+bool DylibVerifier::compareSymbolFlags(const Record *R, SymbolContext &SymCtx,
+ const Record *DR) {
+ if (DR->isThreadLocalValue() && !R->isThreadLocalValue()) {
+ Ctx.emitDiag([&]() {
+ Ctx.Diag->Report(SymCtx.FA->Loc, diag::err_dylib_symbol_flags_mismatch)
+ << getAnnotatedName(DR, SymCtx) << DR->isThreadLocalValue();
+ });
+ return false;
+ }
+ if (!DR->isThreadLocalValue() && R->isThreadLocalValue()) {
+ Ctx.emitDiag([&]() {
+ Ctx.Diag->Report(SymCtx.FA->Loc, diag::err_header_symbol_flags_mismatch)
+ << getAnnotatedName(R, SymCtx) << R->isThreadLocalValue();
+ });
+ return false;
+ }
+
+ if (DR->isWeakDefined() && !R->isWeakDefined()) {
+ Ctx.emitDiag([&]() {
+ Ctx.Diag->Report(SymCtx.FA->Loc, diag::err_dylib_symbol_flags_mismatch)
+ << getAnnotatedName(DR, SymCtx) << R->isWeakDefined();
+ });
+ return false;
+ }
+ if (!DR->isWeakDefined() && R->isWeakDefined()) {
+ Ctx.emitDiag([&]() {
+ Ctx.Diag->Report(SymCtx.FA->Loc, diag::err_header_symbol_flags_mismatch)
+ << getAnnotatedName(R, SymCtx) << R->isWeakDefined();
+ });
+ return false;
+ }
+
+ return true;
+}
+
+DylibVerifier::Result DylibVerifier::verifyImpl(Record *R,
+ SymbolContext &SymCtx) {
+ R->setVerify();
+ if (!canVerify()) {
+ // Accumulate symbols when not in verifying against dylib.
+ if (R->isExported() && !SymCtx.FA->Avail.isUnavailable() &&
+ !SymCtx.FA->Avail.isObsoleted()) {
+ addSymbol(R, SymCtx);
+ }
+ return Ctx.FrontendState;
+ }
+
+ if (shouldIgnoreReexport(R, SymCtx)) {
+ updateState(Result::Ignore);
+ return Ctx.FrontendState;
+ }
+
+ Record *DR =
+ findRecordFromSlice(Ctx.DylibSlice, SymCtx.SymbolName, SymCtx.Kind);
+ if (DR)
+ DR->setVerify();
+
+ if (shouldIgnoreObsolete(R, SymCtx, DR)) {
+ updateState(Result::Ignore);
+ return Ctx.FrontendState;
+ }
+
+ // Unavailable declarations don't need matching symbols.
+ if (SymCtx.FA->Avail.isUnavailable() && (!DR || DR->isInternal())) {
+ updateState(Result::Valid);
+ return Ctx.FrontendState;
+ }
+
+ Result VisibilityCheck = compareVisibility(R, SymCtx, DR);
+ if (VisibilityCheck != Result::Valid) {
+ updateState(VisibilityCheck);
+ return Ctx.FrontendState;
+ }
+
+ // All missing symbol cases to diagnose have been handled now.
+ if (!DR) {
+ updateState(Result::Ignore);
+ return Ctx.FrontendState;
+ }
+
+ // Check for mismatching ObjC interfaces.
+ if (SymCtx.ObjCIFKind != ObjCIFSymbolKind::None) {
+ if (!compareObjCInterfaceSymbols(
+ R, SymCtx, Ctx.DylibSlice->findObjCInterface(DR->getName()))) {
+ updateState(Result::Invalid);
+ return Ctx.FrontendState;
+ }
+ }
+
+ Result AvailabilityCheck = compareAvailability(R, SymCtx, DR);
+ if (AvailabilityCheck != Result::Valid) {
+ updateState(AvailabilityCheck);
+ return Ctx.FrontendState;
+ }
+
+ if (!compareSymbolFlags(R, SymCtx, DR)) {
+ updateState(Result::Invalid);
+ return Ctx.FrontendState;
+ }
+
+ addSymbol(R, SymCtx);
+ updateState(Result::Valid);
+ return Ctx.FrontendState;
+}
+
+bool DylibVerifier::canVerify() {
+ return Ctx.FrontendState != Result::NoVerify;
+}
+
+void DylibVerifier::assignSlice(const Target &T) {
+ assert(T == Ctx.Target && "Active targets should match.");
+ if (Dylib.empty())
+ return;
+
+ // Note: there are no reexport slices with binaries, as opposed to TBD files,
+ // so it can be assumed that the target match is the active top-level library.
+ auto It = find_if(
+ Dylib, [&T](const auto &Slice) { return T == Slice->getTarget(); });
+
+ assert(It != Dylib.end() && "Target slice should always exist.");
+ Ctx.DylibSlice = It->get();
+}
+
+void DylibVerifier::setTarget(const Target &T) {
+ Ctx.Target = T;
+ Ctx.DiscoveredFirstError = false;
+ if (Dylib.empty()) {
+ updateState(Result::NoVerify);
+ return;
+ }
+ updateState(Result::Ignore);
+ assignSlice(T);
+}
+
+void DylibVerifier::setSourceManager(
+ IntrusiveRefCntPtr<SourceManager> SourceMgr) {
+ if (!Ctx.Diag)
+ return;
+ SourceManagers.push_back(std::move(SourceMgr));
+ Ctx.Diag->setSourceManager(SourceManagers.back().get());
+}
+
+DylibVerifier::Result DylibVerifier::verify(ObjCIVarRecord *R,
+ const FrontendAttrs *FA,
+ const StringRef SuperClass) {
+ if (R->isVerified())
+ return getState();
+
+ std::string FullName =
+ ObjCIVarRecord::createScopedName(SuperClass, R->getName());
+ SymbolContext SymCtx{FullName, EncodeKind::ObjectiveCInstanceVariable, FA};
+ return verifyImpl(R, SymCtx);
+}
+
+static ObjCIFSymbolKind assignObjCIFSymbolKind(const ObjCInterfaceRecord *R) {
+ ObjCIFSymbolKind Result = ObjCIFSymbolKind::None;
+ if (R->getLinkageForSymbol(ObjCIFSymbolKind::Class) != RecordLinkage::Unknown)
+ Result |= ObjCIFSymbolKind::Class;
+ if (R->getLinkageForSymbol(ObjCIFSymbolKind::MetaClass) !=
+ RecordLinkage::Unknown)
+ Result |= ObjCIFSymbolKind::MetaClass;
+ if (R->getLinkageForSymbol(ObjCIFSymbolKind::EHType) !=
+ RecordLinkage::Unknown)
+ Result |= ObjCIFSymbolKind::EHType;
+ return Result;
+}
+
+DylibVerifier::Result DylibVerifier::verify(ObjCInterfaceRecord *R,
+ const FrontendAttrs *FA) {
+ if (R->isVerified())
+ return getState();
+ SymbolContext SymCtx;
+ SymCtx.SymbolName = R->getName();
+ SymCtx.ObjCIFKind = assignObjCIFSymbolKind(R);
+
+ SymCtx.Kind = R->hasExceptionAttribute() ? EncodeKind::ObjectiveCClassEHType
+ : EncodeKind::ObjectiveCClass;
+ SymCtx.FA = FA;
+
+ return verifyImpl(R, SymCtx);
+}
+
+DylibVerifier::Result DylibVerifier::verify(GlobalRecord *R,
+ const FrontendAttrs *FA) {
+ if (R->isVerified())
+ return getState();
+
+ // Global classifications could be obfusciated with `asm`.
+ SimpleSymbol Sym = parseSymbol(R->getName());
+ SymbolContext SymCtx;
+ SymCtx.SymbolName = Sym.Name;
+ SymCtx.Kind = Sym.Kind;
+ SymCtx.FA = FA;
+ SymCtx.Inlined = R->isInlined();
+ return verifyImpl(R, SymCtx);
+}
+
+void DylibVerifier::VerifierContext::emitDiag(llvm::function_ref<void()> Report,
+ RecordLoc *Loc) {
+ if (!DiscoveredFirstError) {
+ Diag->Report(diag::warn_target)
+ << (PrintArch ? getArchitectureName(Target.Arch)
+ : getTargetTripleName(Target));
+ DiscoveredFirstError = true;
+ }
+ if (Loc && Loc->isValid())
+ llvm::errs() << Loc->File << ":" << Loc->Line << ":" << 0 << ": ";
+
+ Report();
+}
+
+// The existence of weak-defined RTTI can not always be inferred from the
+// header files because they can be generated as part of an implementation
+// file.
+// InstallAPI doesn't warn about weak-defined RTTI, because this doesn't affect
+// static linking and so can be ignored for text-api files.
+static bool shouldIgnoreCpp(StringRef Name, bool IsWeakDef) {
+ return (IsWeakDef &&
+ (Name.starts_with("__ZTI") || Name.starts_with("__ZTS")));
+}
+void DylibVerifier::visitSymbolInDylib(const Record &R, SymbolContext &SymCtx) {
+ // Undefined symbols should not be in InstallAPI generated text-api files.
+ if (R.isUndefined()) {
+ updateState(Result::Valid);
+ return;
+ }
+
+ // Internal symbols should not be in InstallAPI generated text-api files.
+ if (R.isInternal()) {
+ updateState(Result::Valid);
+ return;
+ }
+
+ // Allow zippered symbols with potentially mismatching availability
+ // between macOS and macCatalyst in the final text-api file.
+ const StringRef SymbolName(SymCtx.SymbolName);
+ if (const Symbol *Sym = Exports->findSymbol(SymCtx.Kind, SymCtx.SymbolName,
+ SymCtx.ObjCIFKind)) {
+ if (Sym->hasArchitecture(Ctx.Target.Arch)) {
+ updateState(Result::Ignore);
+ return;
+ }
+ }
+
+ const bool IsLinkerSymbol = SymbolName.starts_with("$ld$");
+
+ if (R.isVerified()) {
+ // Check for unavailable symbols.
+ // This should only occur in the zippered case where we ignored
+ // availability until all headers have been parsed.
+ auto It = DeferredZipperedSymbols.find(SymCtx.SymbolName);
+ if (It == DeferredZipperedSymbols.end()) {
+ updateState(Result::Valid);
+ return;
+ }
+
+ ZipperedDeclSources Locs;
+ for (const ZipperedDeclSource &ZSource : It->second) {
+ if (ZSource.FA->Avail.isObsoleted()) {
+ updateState(Result::Ignore);
+ return;
+ }
+ if (ZSource.T.Arch != Ctx.Target.Arch)
+ continue;
+ Locs.emplace_back(ZSource);
+ }
+ assert(Locs.size() == 2 && "Expected two decls for zippered symbol");
+
+ // Print violating declarations per platform.
+ for (const ZipperedDeclSource &ZSource : Locs) {
+ unsigned DiagID = 0;
+ if (Mode == VerificationMode::Pedantic || IsLinkerSymbol) {
+ updateState(Result::Invalid);
+ DiagID = diag::err_header_availability_mismatch;
+ } else if (Mode == VerificationMode::ErrorsAndWarnings) {
+ updateState(Result::Ignore);
+ DiagID = diag::warn_header_availability_mismatch;
+ } else {
+ updateState(Result::Ignore);
+ return;
+ }
+ // Bypass emitDiag banner and print the target everytime.
+ Ctx.Diag->setSourceManager(ZSource.SrcMgr);
+ Ctx.Diag->Report(diag::warn_target) << getTargetTripleName(ZSource.T);
+ Ctx.Diag->Report(ZSource.FA->Loc, DiagID)
+ << getAnnotatedName(&R, SymCtx) << ZSource.FA->Avail.isUnavailable()
+ << ZSource.FA->Avail.isUnavailable();
+ }
+ return;
+ }
+
+ if (shouldIgnoreCpp(SymbolName, R.isWeakDefined())) {
+ updateState(Result::Valid);
+ return;
+ }
+
+ if (Aliases.count({SymbolName.str(), SymCtx.Kind})) {
+ updateState(Result::Valid);
+ return;
+ }
+
+ // All checks at this point classify as some kind of violation.
+ // The different verification modes dictate whether they are reported to the
+ // user.
+ if (IsLinkerSymbol || (Mode > VerificationMode::ErrorsOnly))
+ accumulateSrcLocForDylibSymbols();
+ RecordLoc Loc = DWARFCtx->SourceLocs.lookup(SymCtx.SymbolName);
+
+ // Regardless of verification mode, error out on mismatched special linker
+ // symbols.
+ if (IsLinkerSymbol) {
+ Ctx.emitDiag(
+ [&]() {
+ Ctx.Diag->Report(diag::err_header_symbol_missing)
+ << getAnnotatedName(&R, SymCtx, Loc.isValid());
+ },
+ &Loc);
+ updateState(Result::Invalid);
+ return;
+ }
+
+ // Missing declarations for exported symbols are hard errors on Pedantic mode.
+ if (Mode == VerificationMode::Pedantic) {
+ Ctx.emitDiag(
+ [&]() {
+ Ctx.Diag->Report(diag::err_header_symbol_missing)
+ << getAnnotatedName(&R, SymCtx, Loc.isValid());
+ },
+ &Loc);
+ updateState(Result::Invalid);
+ return;
+ }
+
+ // Missing declarations for exported symbols are warnings on ErrorsAndWarnings
+ // mode.
+ if (Mode == VerificationMode::ErrorsAndWarnings) {
+ Ctx.emitDiag(
+ [&]() {
+ Ctx.Diag->Report(diag::warn_header_symbol_missing)
+ << getAnnotatedName(&R, SymCtx, Loc.isValid());
+ },
+ &Loc);
+ updateState(Result::Ignore);
+ return;
+ }
+
+ // Missing declarations are dropped for ErrorsOnly mode. It is the last
+ // remaining mode.
+ updateState(Result::Ignore);
+ return;
+}
+
+void DylibVerifier::visitGlobal(const GlobalRecord &R) {
+ SymbolContext SymCtx;
+ SimpleSymbol Sym = parseSymbol(R.getName());
+ SymCtx.SymbolName = Sym.Name;
+ SymCtx.Kind = Sym.Kind;
+ visitSymbolInDylib(R, SymCtx);
+}
+
+void DylibVerifier::visitObjCIVar(const ObjCIVarRecord &R,
+ const StringRef Super) {
+ SymbolContext SymCtx;
+ SymCtx.SymbolName = ObjCIVarRecord::createScopedName(Super, R.getName());
+ SymCtx.Kind = EncodeKind::ObjectiveCInstanceVariable;
+ visitSymbolInDylib(R, SymCtx);
+}
+
+void DylibVerifier::accumulateSrcLocForDylibSymbols() {
+ if (DSYMPath.empty())
+ return;
+
+ assert(DWARFCtx != nullptr && "Expected an initialized DWARFContext");
+ if (DWARFCtx->ParsedDSYM)
+ return;
+ DWARFCtx->ParsedDSYM = true;
+ DWARFCtx->SourceLocs =
+ DylibReader::accumulateSourceLocFromDSYM(DSYMPath, Ctx.Target);
+}
+
+void DylibVerifier::visitObjCInterface(const ObjCInterfaceRecord &R) {
+ SymbolContext SymCtx;
+ SymCtx.SymbolName = R.getName();
+ SymCtx.ObjCIFKind = assignObjCIFSymbolKind(&R);
+ if (SymCtx.ObjCIFKind > ObjCIFSymbolKind::EHType) {
+ if (R.hasExceptionAttribute()) {
+ SymCtx.Kind = EncodeKind::ObjectiveCClassEHType;
+ visitSymbolInDylib(R, SymCtx);
+ }
+ SymCtx.Kind = EncodeKind::ObjectiveCClass;
+ visitSymbolInDylib(R, SymCtx);
+ } else {
+ SymCtx.Kind = R.hasExceptionAttribute() ? EncodeKind::ObjectiveCClassEHType
+ : EncodeKind::ObjectiveCClass;
+ visitSymbolInDylib(R, SymCtx);
+ }
+
+ for (const ObjCIVarRecord *IV : R.getObjCIVars())
+ visitObjCIVar(*IV, R.getName());
+}
+
+void DylibVerifier::visitObjCCategory(const ObjCCategoryRecord &R) {
+ for (const ObjCIVarRecord *IV : R.getObjCIVars())
+ visitObjCIVar(*IV, R.getSuperClassName());
+}
+
+DylibVerifier::Result DylibVerifier::verifyRemainingSymbols() {
+ if (getState() == Result::NoVerify)
+ return Result::NoVerify;
+ assert(!Dylib.empty() && "No binary to verify against");
+
+ DWARFContext DWARFInfo;
+ DWARFCtx = &DWARFInfo;
+ Ctx.Target = Target(Architecture::AK_unknown, PlatformType::PLATFORM_UNKNOWN);
+ for (std::shared_ptr<RecordsSlice> Slice : Dylib) {
+ if (Ctx.Target.Arch == Slice->getTarget().Arch)
+ continue;
+ Ctx.DiscoveredFirstError = false;
+ Ctx.PrintArch = true;
+ Ctx.Target = Slice->getTarget();
+ Ctx.DylibSlice = Slice.get();
+ Slice->visit(*this);
+ }
+ return getState();
+}
+
+bool DylibVerifier::verifyBinaryAttrs(const ArrayRef<Target> ProvidedTargets,
+ const BinaryAttrs &ProvidedBA,
+ const LibAttrs &ProvidedReexports,
+ const LibAttrs &ProvidedClients,
+ const LibAttrs &ProvidedRPaths,
+ const FileType &FT) {
+ assert(!Dylib.empty() && "Need dylib to verify.");
+
+ // Pickup any load commands that can differ per slice to compare.
+ TargetList DylibTargets;
+ LibAttrs DylibReexports;
+ LibAttrs DylibClients;
+ LibAttrs DylibRPaths;
+ for (const std::shared_ptr<RecordsSlice> &RS : Dylib) {
+ DylibTargets.push_back(RS->getTarget());
+ const BinaryAttrs &BinInfo = RS->getBinaryAttrs();
+ for (const StringRef LibName : BinInfo.RexportedLibraries)
+ DylibReexports[LibName].set(DylibTargets.back().Arch);
+ for (const StringRef LibName : BinInfo.AllowableClients)
+ DylibClients[LibName].set(DylibTargets.back().Arch);
+ // Compare attributes that are only representable in >= TBD_V5.
+ if (FT >= FileType::TBD_V5)
+ for (const StringRef Name : BinInfo.RPaths)
+ DylibRPaths[Name].set(DylibTargets.back().Arch);
+ }
+
+ // Check targets first.
+ ArchitectureSet ProvidedArchs = mapToArchitectureSet(ProvidedTargets);
+ ArchitectureSet DylibArchs = mapToArchitectureSet(DylibTargets);
+ if (ProvidedArchs != DylibArchs) {
+ Ctx.Diag->Report(diag::err_architecture_mismatch)
+ << ProvidedArchs << DylibArchs;
+ return false;
+ }
+ auto ProvidedPlatforms = mapToPlatformVersionSet(ProvidedTargets);
+ auto DylibPlatforms = mapToPlatformVersionSet(DylibTargets);
+ if (ProvidedPlatforms != DylibPlatforms) {
+ const bool DiffMinOS =
+ mapToPlatformSet(ProvidedTargets) == mapToPlatformSet(DylibTargets);
+ if (DiffMinOS)
+ Ctx.Diag->Report(diag::warn_platform_mismatch)
+ << ProvidedPlatforms << DylibPlatforms;
+ else {
+ Ctx.Diag->Report(diag::err_platform_mismatch)
+ << ProvidedPlatforms << DylibPlatforms;
+ return false;
+ }
+ }
+
+ // Because InstallAPI requires certain attributes to match across architecture
+ // slices, take the first one to compare those with.
+ const BinaryAttrs &DylibBA = (*Dylib.begin())->getBinaryAttrs();
+
+ if (ProvidedBA.InstallName != DylibBA.InstallName) {
+ Ctx.Diag->Report(diag::err_install_name_mismatch)
+ << ProvidedBA.InstallName << DylibBA.InstallName;
+ return false;
+ }
+
+ if (ProvidedBA.CurrentVersion != DylibBA.CurrentVersion) {
+ Ctx.Diag->Report(diag::err_current_version_mismatch)
+ << ProvidedBA.CurrentVersion << DylibBA.CurrentVersion;
+ return false;
+ }
+
+ if (ProvidedBA.CompatVersion != DylibBA.CompatVersion) {
+ Ctx.Diag->Report(diag::err_compatibility_version_mismatch)
+ << ProvidedBA.CompatVersion << DylibBA.CompatVersion;
+ return false;
+ }
+
+ if (ProvidedBA.AppExtensionSafe != DylibBA.AppExtensionSafe) {
+ Ctx.Diag->Report(diag::err_appextension_safe_mismatch)
+ << (ProvidedBA.AppExtensionSafe ? "true" : "false")
+ << (DylibBA.AppExtensionSafe ? "true" : "false");
+ return false;
+ }
+
+ if (!DylibBA.TwoLevelNamespace) {
+ Ctx.Diag->Report(diag::err_no_twolevel_namespace);
+ return false;
+ }
+
+ if (ProvidedBA.OSLibNotForSharedCache != DylibBA.OSLibNotForSharedCache) {
+ Ctx.Diag->Report(diag::err_shared_cache_eligiblity_mismatch)
+ << (ProvidedBA.OSLibNotForSharedCache ? "true" : "false")
+ << (DylibBA.OSLibNotForSharedCache ? "true" : "false");
+ return false;
+ }
+
+ if (ProvidedBA.ParentUmbrella.empty() && !DylibBA.ParentUmbrella.empty()) {
+ Ctx.Diag->Report(diag::err_parent_umbrella_missing)
+ << "installAPI option" << DylibBA.ParentUmbrella;
+ return false;
+ }
+
+ if (!ProvidedBA.ParentUmbrella.empty() && DylibBA.ParentUmbrella.empty()) {
+ Ctx.Diag->Report(diag::err_parent_umbrella_missing)
+ << "binary file" << ProvidedBA.ParentUmbrella;
+ return false;
+ }
+
+ if ((!ProvidedBA.ParentUmbrella.empty()) &&
+ (ProvidedBA.ParentUmbrella != DylibBA.ParentUmbrella)) {
+ Ctx.Diag->Report(diag::err_parent_umbrella_mismatch)
+ << ProvidedBA.ParentUmbrella << DylibBA.ParentUmbrella;
+ return false;
+ }
+
+ auto CompareLibraries = [&](const LibAttrs &Provided, const LibAttrs &Dylib,
+ unsigned DiagID_missing, unsigned DiagID_mismatch,
+ bool Fatal = true) {
+ if (Provided == Dylib)
+ return true;
+
+ for (const llvm::StringMapEntry<ArchitectureSet> &PAttr : Provided) {
+ const auto DAttrIt = Dylib.find(PAttr.getKey());
+ if (DAttrIt == Dylib.end()) {
+ Ctx.Diag->Report(DiagID_missing) << "binary file" << PAttr;
+ if (Fatal)
+ return false;
+ }
+
+ if (PAttr.getValue() != DAttrIt->getValue()) {
+ Ctx.Diag->Report(DiagID_mismatch) << PAttr << *DAttrIt;
+ if (Fatal)
+ return false;
+ }
+ }
+
+ for (const llvm::StringMapEntry<ArchitectureSet> &DAttr : Dylib) {
+ const auto PAttrIt = Provided.find(DAttr.getKey());
+ if (PAttrIt == Provided.end()) {
+ Ctx.Diag->Report(DiagID_missing) << "installAPI option" << DAttr;
+ if (!Fatal)
+ continue;
+ return false;
+ }
+
+ if (PAttrIt->getValue() != DAttr.getValue()) {
+ if (Fatal)
+ llvm_unreachable("this case was already covered above.");
+ }
+ }
+ return true;
+ };
+
+ if (!CompareLibraries(ProvidedReexports, DylibReexports,
+ diag::err_reexported_libraries_missing,
+ diag::err_reexported_libraries_mismatch))
+ return false;
+
+ if (!CompareLibraries(ProvidedClients, DylibClients,
+ diag::err_allowable_clients_missing,
+ diag::err_allowable_clients_mismatch))
+ return false;
+
+ if (FT >= FileType::TBD_V5) {
+ // Ignore rpath differences if building an asan variant, since the
+ // compiler injects additional paths.
+ // FIXME: Building with sanitizers does not always change the install
+ // name, so this is not a foolproof solution.
+ if (!ProvidedBA.InstallName.ends_with("_asan")) {
+ if (!CompareLibraries(ProvidedRPaths, DylibRPaths,
+ diag::warn_rpaths_missing,
+ diag::warn_rpaths_mismatch,
+ /*Fatal=*/false))
+ return true;
+ }
+ }
+
+ return true;
+}
+
+std::unique_ptr<SymbolSet> DylibVerifier::takeExports() {
+ for (const auto &[Alias, Base] : Aliases) {
+ TargetList Targets;
+ SymbolFlags Flags = SymbolFlags::None;
+ if (const Symbol *Sym = Exports->findSymbol(Base.second, Base.first)) {
+ Flags = Sym->getFlags();
+ Targets = {Sym->targets().begin(), Sym->targets().end()};
+ }
+
+ Record R(Alias.first, RecordLinkage::Exported, Flags);
+ SymbolContext SymCtx;
+ SymCtx.SymbolName = Alias.first;
+ SymCtx.Kind = Alias.second;
+ addSymbol(&R, SymCtx, std::move(Targets));
+ }
+
+ return std::move(Exports);
+}
+
+} // namespace installapi
+} // namespace clang