[llvm-branch-commits] [clang] [llvm] [InstallAPI] Introduce Basic Verifier (PR #85106)
Cyndy Ishida via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Thu Mar 14 12:10:44 PDT 2024
https://github.com/cyndyishida updated https://github.com/llvm/llvm-project/pull/85106
>From 192f306f8d693af77e267e1caa52799a353f4064 Mon Sep 17 00:00:00 2001
From: Cyndy Ishida <cyndy_ishida at apple.com>
Date: Tue, 12 Mar 2024 20:56:23 -0700
Subject: [PATCH 1/3] [InstallAPI] Introduce Basic Verifier
This adds basic support for calling the verifier on global declarations
that are expected to represent symbol exports. The driver now
exclusively uses this for knowing what symbols make up a TBD file.
Future patches will actually check against the dylib's symbol table.
---
clang/include/clang/AST/Availability.h | 3 +
clang/include/clang/InstallAPI/Context.h | 4 +
.../include/clang/InstallAPI/DylibVerifier.h | 79 ++++++-
clang/include/clang/InstallAPI/Frontend.h | 1 -
.../clang/InstallAPI/FrontendRecords.h | 49 ++--
clang/include/clang/InstallAPI/MachO.h | 3 +
clang/lib/InstallAPI/CMakeLists.txt | 2 +
clang/lib/InstallAPI/DylibVerifier.cpp | 212 ++++++++++++++++++
clang/lib/InstallAPI/Frontend.cpp | 49 ++--
clang/lib/InstallAPI/Visitor.cpp | 101 +++++----
clang/test/InstallAPI/asm.test | 90 ++++++++
.../clang-installapi/ClangInstallAPI.cpp | 12 +-
clang/tools/clang-installapi/Options.cpp | 9 +-
llvm/include/llvm/TextAPI/Record.h | 7 +-
14 files changed, 524 insertions(+), 97 deletions(-)
create mode 100644 clang/lib/InstallAPI/DylibVerifier.cpp
create mode 100644 clang/test/InstallAPI/asm.test
diff --git a/clang/include/clang/AST/Availability.h b/clang/include/clang/AST/Availability.h
index ae3acbeffe7f18..5cfbaf0cdfbd21 100644
--- a/clang/include/clang/AST/Availability.h
+++ b/clang/include/clang/AST/Availability.h
@@ -75,6 +75,9 @@ struct AvailabilityInfo {
/// Determine if this AvailabilityInfo represents the default availability.
bool isDefault() const { return *this == AvailabilityInfo(); }
+ /// Check if the symbol has been obsoleted.
+ bool isObsoleted() const { return !Obsoleted.empty(); }
+
/// Check if the symbol is unconditionally deprecated.
///
/// i.e. \code __attribute__((deprecated)) \endcode
diff --git a/clang/include/clang/InstallAPI/Context.h b/clang/include/clang/InstallAPI/Context.h
index bdb576d7d85fb6..074ff6f969773c 100644
--- a/clang/include/clang/InstallAPI/Context.h
+++ b/clang/include/clang/InstallAPI/Context.h
@@ -18,6 +18,7 @@
namespace clang {
namespace installapi {
class FrontendRecordsSlice;
+class DylibVerifier;
/// Struct used for generating validating InstallAPI.
/// The attributes captured represent all necessary information
@@ -45,6 +46,9 @@ struct InstallAPIContext {
/// DiagnosticsEngine for all error reporting.
DiagnosticsEngine *Diags = nullptr;
+ /// Verifier when binary dylib is passed as input.
+ std::unique_ptr<DylibVerifier> Verifier = nullptr;
+
/// File Path of output location.
llvm::StringRef OutputLoc{};
diff --git a/clang/include/clang/InstallAPI/DylibVerifier.h b/clang/include/clang/InstallAPI/DylibVerifier.h
index 1a6121b3a258b5..72c4743fdf65e0 100644
--- a/clang/include/clang/InstallAPI/DylibVerifier.h
+++ b/clang/include/clang/InstallAPI/DylibVerifier.h
@@ -9,10 +9,12 @@
#ifndef LLVM_CLANG_INSTALLAPI_DYLIBVERIFIER_H
#define LLVM_CLANG_INSTALLAPI_DYLIBVERIFIER_H
-#include "llvm/TextAPI/Target.h"
+#include "clang/Basic/Diagnostic.h"
+#include "clang/InstallAPI/MachO.h"
namespace clang {
namespace installapi {
+struct FrontendAttrs;
/// A list of InstallAPI verification modes.
enum class VerificationMode {
@@ -22,6 +24,81 @@ enum class VerificationMode {
Pedantic,
};
+/// Service responsible to tracking state of verification across the
+/// lifetime of InstallAPI.
+/// As declarations are collected during AST traversal, they are
+/// compared as symbols against what is available in the binary dylib.
+class DylibVerifier {
+private:
+ struct SymbolContext;
+
+public:
+ enum class Result { NoVerify, Ignore, Valid, Invalid };
+ struct VerifierContext {
+ // Current target being verified against the AST.
+ llvm::MachO::Target Target;
+
+ // Query state of verification after AST has been traversed.
+ Result FrontendState;
+
+ // First error for AST traversal, which is tied to the target triple.
+ bool DiscoveredFirstError;
+ };
+
+ DylibVerifier() = default;
+
+ DylibVerifier(llvm::MachO::Records &&Dylib, DiagnosticsEngine *Diag,
+ VerificationMode Mode, bool Demangle)
+ : Dylib(std::move(Dylib)), Diag(Diag), Mode(Mode), Demangle(Demangle),
+ Exports(std::make_unique<SymbolSet>()) {}
+
+ Result verify(GlobalRecord *R, const FrontendAttrs *FA);
+ Result verify(ObjCInterfaceRecord *R, const FrontendAttrs *FA);
+ Result verify(ObjCIVarRecord *R, const FrontendAttrs *FA,
+ const StringRef SuperClass);
+
+ /// Initialize target for verification.
+ void setTarget(const Target &T);
+
+ /// Release ownership over exports.
+ std::unique_ptr<SymbolSet> getExports() { return std::move(Exports); }
+
+ /// Get result of verification.
+ Result getState() const { return Ctx.FrontendState; }
+
+private:
+ /// Determine whether to compare declaration to symbol in binary.
+ bool canVerify();
+
+ /// Shared implementation for verifying exported symbols.
+ Result verifyImpl(Record *R, SymbolContext &SymCtx);
+
+ /// Update result state on each call to `verify`.
+ void updateState(Result State);
+
+ /// Add verified exported symbol.
+ void addSymbol(const Record *R, SymbolContext &SymCtx,
+ TargetList &&Targets = {});
+
+ // Symbols in dylib.
+ llvm::MachO::Records Dylib;
+
+ // Engine for reporting violations.
+ [[maybe_unused]] DiagnosticsEngine *Diag = nullptr;
+
+ // Controls what class of violations to report.
+ [[maybe_unused]] VerificationMode Mode = VerificationMode::Invalid;
+
+ // Attempt to demangle when reporting violations.
+ bool Demangle = false;
+
+ // Valid symbols in final text file.
+ std::unique_ptr<SymbolSet> Exports = std::make_unique<SymbolSet>();
+
+ // Track current state of verification while traversing AST.
+ VerifierContext Ctx;
+};
+
} // namespace installapi
} // namespace clang
#endif // LLVM_CLANG_INSTALLAPI_DYLIBVERIFIER_H
diff --git a/clang/include/clang/InstallAPI/Frontend.h b/clang/include/clang/InstallAPI/Frontend.h
index 873cb50d60a542..660fc8cd69a59d 100644
--- a/clang/include/clang/InstallAPI/Frontend.h
+++ b/clang/include/clang/InstallAPI/Frontend.h
@@ -14,7 +14,6 @@
#define LLVM_CLANG_INSTALLAPI_FRONTEND_H
#include "clang/AST/ASTConsumer.h"
-#include "clang/AST/Availability.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendActions.h"
#include "clang/InstallAPI/Context.h"
diff --git a/clang/include/clang/InstallAPI/FrontendRecords.h b/clang/include/clang/InstallAPI/FrontendRecords.h
index 1f5bc37798befd..59271e81e230c2 100644
--- a/clang/include/clang/InstallAPI/FrontendRecords.h
+++ b/clang/include/clang/InstallAPI/FrontendRecords.h
@@ -43,13 +43,13 @@ class FrontendRecordsSlice : public llvm::MachO::RecordsSlice {
/// \param Flags The flags that describe attributes of the symbol.
/// \param Inlined Whether declaration is inlined, only applicable to
/// functions.
- /// \return The non-owning pointer to added record in slice.
- GlobalRecord *addGlobal(StringRef Name, RecordLinkage Linkage,
- GlobalRecord::Kind GV,
- const clang::AvailabilityInfo Avail, const Decl *D,
- const HeaderType Access,
- SymbolFlags Flags = SymbolFlags::None,
- bool Inlined = false);
+ /// \return The non-owning pointer to added record in slice with it's frontend
+ /// attributes.
+ std::pair<GlobalRecord *, FrontendAttrs *>
+ addGlobal(StringRef Name, RecordLinkage Linkage, GlobalRecord::Kind GV,
+ const clang::AvailabilityInfo Avail, const Decl *D,
+ const HeaderType Access, SymbolFlags Flags = SymbolFlags::None,
+ bool Inlined = false);
/// Add ObjC Class record with attributes from AST.
///
@@ -60,11 +60,12 @@ class FrontendRecordsSlice : public llvm::MachO::RecordsSlice {
/// \param D The pointer to the declaration from traversing AST.
/// \param Access The intended access level of symbol.
/// \param IsEHType Whether declaration has an exception attribute.
- /// \return The non-owning pointer to added record in slice.
- ObjCInterfaceRecord *addObjCInterface(StringRef Name, RecordLinkage Linkage,
- const clang::AvailabilityInfo Avail,
- const Decl *D, HeaderType Access,
- bool IsEHType);
+ /// \return The non-owning pointer to added record in slice with it's frontend
+ /// attributes.
+ std::pair<ObjCInterfaceRecord *, FrontendAttrs *>
+ addObjCInterface(StringRef Name, RecordLinkage Linkage,
+ const clang::AvailabilityInfo Avail, const Decl *D,
+ HeaderType Access, bool IsEHType);
/// Add ObjC Category record with attributes from AST.
///
@@ -75,11 +76,12 @@ class FrontendRecordsSlice : public llvm::MachO::RecordsSlice {
/// to the active target triple.
/// \param D The pointer to the declaration from traversing AST.
/// \param Access The intended access level of symbol.
- /// \return The non-owning pointer to added record in slice.
- ObjCCategoryRecord *addObjCCategory(StringRef ClassToExtend,
- StringRef CategoryName,
- const clang::AvailabilityInfo Avail,
- const Decl *D, HeaderType Access);
+ /// \return The non-owning pointer to added record in slice with it's frontend
+ /// attributes.
+ std::pair<ObjCCategoryRecord *, FrontendAttrs *>
+ addObjCCategory(StringRef ClassToExtend, StringRef CategoryName,
+ const clang::AvailabilityInfo Avail, const Decl *D,
+ HeaderType Access);
/// Add ObjC IVar record with attributes from AST.
///
@@ -91,12 +93,13 @@ class FrontendRecordsSlice : public llvm::MachO::RecordsSlice {
/// \param D The pointer to the declaration from traversing AST.
/// \param Access The intended access level of symbol.
/// \param AC The access control tied to the ivar declaration.
- /// \return The non-owning pointer to added record in slice.
- ObjCIVarRecord *addObjCIVar(ObjCContainerRecord *Container,
- StringRef IvarName, RecordLinkage Linkage,
- const clang::AvailabilityInfo Avail,
- const Decl *D, HeaderType Access,
- const clang::ObjCIvarDecl::AccessControl AC);
+ /// \return The non-owning pointer to added record in slice with it's frontend
+ /// attributes.
+ std::pair<ObjCIVarRecord *, FrontendAttrs *>
+ addObjCIVar(ObjCContainerRecord *Container, StringRef IvarName,
+ RecordLinkage Linkage, const clang::AvailabilityInfo Avail,
+ const Decl *D, HeaderType Access,
+ const clang::ObjCIvarDecl::AccessControl AC);
private:
/// Mapping of records stored in slice to their frontend attributes.
diff --git a/clang/include/clang/InstallAPI/MachO.h b/clang/include/clang/InstallAPI/MachO.h
index 55e5591389ce1f..6dee6f22420381 100644
--- a/clang/include/clang/InstallAPI/MachO.h
+++ b/clang/include/clang/InstallAPI/MachO.h
@@ -18,6 +18,7 @@
#include "llvm/TextAPI/PackedVersion.h"
#include "llvm/TextAPI/Platform.h"
#include "llvm/TextAPI/RecordVisitor.h"
+#include "llvm/TextAPI/Symbol.h"
#include "llvm/TextAPI/Target.h"
#include "llvm/TextAPI/TextAPIWriter.h"
#include "llvm/TextAPI/Utils.h"
@@ -33,8 +34,10 @@ using ObjCIVarRecord = llvm::MachO::ObjCIVarRecord;
using Records = llvm::MachO::Records;
using BinaryAttrs = llvm::MachO::RecordsSlice::BinaryAttrs;
using SymbolSet = llvm::MachO::SymbolSet;
+using SimpleSymbol = llvm::MachO::SimpleSymbol;
using FileType = llvm::MachO::FileType;
using PackedVersion = llvm::MachO::PackedVersion;
using Target = llvm::MachO::Target;
+using TargetList = llvm::MachO::TargetList;
#endif // LLVM_CLANG_INSTALLAPI_MACHO_H
diff --git a/clang/lib/InstallAPI/CMakeLists.txt b/clang/lib/InstallAPI/CMakeLists.txt
index dc90d6370de418..cba1c3fcafdf20 100644
--- a/clang/lib/InstallAPI/CMakeLists.txt
+++ b/clang/lib/InstallAPI/CMakeLists.txt
@@ -1,10 +1,12 @@
set(LLVM_LINK_COMPONENTS
Support
TextAPI
+ Demangle
Core
)
add_clang_library(clangInstallAPI
+ DylibVerifier.cpp
FileList.cpp
Frontend.cpp
HeaderFile.cpp
diff --git a/clang/lib/InstallAPI/DylibVerifier.cpp b/clang/lib/InstallAPI/DylibVerifier.cpp
new file mode 100644
index 00000000000000..b7dd85d63fa14f
--- /dev/null
+++ b/clang/lib/InstallAPI/DylibVerifier.cpp
@@ -0,0 +1,212 @@
+#include "clang/InstallAPI/DylibVerifier.h"
+#include "clang/InstallAPI/FrontendRecords.h"
+#include "llvm/Demangle/Demangle.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 printing in diagnostics.
+ std::string PrettyPrintName{""};
+
+ // 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;
+};
+
+static std::string
+getAnnotatedName(const Record *R, EncodeKind Kind, StringRef Name,
+ bool ValidSourceLoc = true,
+ ObjCIFSymbolKind ObjCIF = ObjCIFSymbolKind::None) {
+ assert(!Name.empty() && "Need symbol name for printing");
+
+ 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.
+ const bool IsAnnotatedObjCClass = ((ObjCIF != ObjCIFSymbolKind::None) &&
+ (ObjCIF <= ObjCIFSymbolKind::EHType));
+
+ if (IsAnnotatedObjCClass) {
+ if (ObjCIF == ObjCIFSymbolKind::EHType)
+ Annotation += "Exception Type of ";
+ if (ObjCIF == ObjCIFSymbolKind::MetaClass)
+ Annotation += "Metaclass of ";
+ if (ObjCIF == ObjCIFSymbolKind::Class)
+ Annotation += "Class of ";
+ }
+
+ // 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) {
+ if ((Kind == EncodeKind::GlobalSymbol) && Name.starts_with("_"))
+ return Annotation + Name.drop_front(1).str();
+ return Annotation + Name.str();
+ }
+
+ if (IsAnnotatedObjCClass)
+ return Annotation + Name.str();
+
+ switch (Kind) {
+ case EncodeKind::GlobalSymbol:
+ return Annotation + Name.str();
+ case EncodeKind::ObjectiveCInstanceVariable:
+ return Annotation + "(ObjC IVar) " + Name.str();
+ case EncodeKind::ObjectiveCClass:
+ return Annotation + "(ObjC Class) " + Name.str();
+ case EncodeKind::ObjectiveCClassEHType:
+ return Annotation + "(ObjC Class EH) " + Name.str();
+ }
+
+ llvm_unreachable("unexpected case for EncodeKind");
+}
+
+static std::string demangle(StringRef Name) {
+ // Itanium encoding requires 1 or 3 leading underscores, followed by 'Z'.
+ if (!(Name.starts_with("_Z") || Name.starts_with("___Z")))
+ return Name.str();
+ char *Result = llvm::itaniumDemangle(Name.data());
+ if (!Result)
+ return Name.str();
+
+ std::string Demangled(Result);
+ free(Result);
+ return Demangled;
+}
+
+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;
+}
+
+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);
+}
+
+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.isUnconditionallyUnavailable() &&
+ !SymCtx.FA->Avail.isObsoleted()) {
+ addSymbol(R, SymCtx);
+ }
+ return Ctx.FrontendState;
+ }
+ return Ctx.FrontendState;
+}
+
+bool DylibVerifier::canVerify() {
+ return Ctx.FrontendState != Result::NoVerify;
+}
+
+void DylibVerifier::setTarget(const Target &T) {
+ Ctx.Target = T;
+ Ctx.DiscoveredFirstError = false;
+ updateState(Dylib.empty() ? Result::NoVerify : Result::Ignore);
+}
+
+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{
+ getAnnotatedName(R, EncodeKind::ObjectiveCInstanceVariable,
+ Demangle ? demangle(FullName) : FullName),
+ 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);
+
+ std::string DisplayName =
+ Demangle ? demangle(SymCtx.SymbolName) : SymCtx.SymbolName;
+ SymCtx.Kind = R->hasExceptionAttribute() ? EncodeKind::ObjectiveCClassEHType
+ : EncodeKind::ObjectiveCClass;
+ SymCtx.PrettyPrintName = getAnnotatedName(R, SymCtx.Kind, DisplayName);
+ 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.PrettyPrintName =
+ getAnnotatedName(R, Sym.Kind, Demangle ? demangle(Sym.Name) : Sym.Name);
+ SymCtx.Kind = Sym.Kind;
+ SymCtx.FA = FA;
+ return verifyImpl(R, SymCtx);
+}
+
+} // namespace installapi
+} // namespace clang
diff --git a/clang/lib/InstallAPI/Frontend.cpp b/clang/lib/InstallAPI/Frontend.cpp
index 707aeb17dc8906..12cd5fcbc22bf7 100644
--- a/clang/lib/InstallAPI/Frontend.cpp
+++ b/clang/lib/InstallAPI/Frontend.cpp
@@ -16,41 +16,47 @@ using namespace llvm;
using namespace llvm::MachO;
namespace clang::installapi {
-
-GlobalRecord *FrontendRecordsSlice::addGlobal(
+std::pair<GlobalRecord *, FrontendAttrs *> FrontendRecordsSlice::addGlobal(
StringRef Name, RecordLinkage Linkage, GlobalRecord::Kind GV,
const clang::AvailabilityInfo Avail, const Decl *D, const HeaderType Access,
SymbolFlags Flags, bool Inlined) {
- auto *GR =
+ GlobalRecord *GR =
llvm::MachO::RecordsSlice::addGlobal(Name, Linkage, GV, Flags, Inlined);
- FrontendRecords.insert({GR, FrontendAttrs{Avail, D, Access}});
- return GR;
+ auto Result = FrontendRecords.insert({GR, FrontendAttrs{Avail, D, Access}});
+ return {GR, &(Result.first->second)};
}
-ObjCInterfaceRecord *FrontendRecordsSlice::addObjCInterface(
- StringRef Name, RecordLinkage Linkage, const clang::AvailabilityInfo Avail,
- const Decl *D, HeaderType Access, bool IsEHType) {
+std::pair<ObjCInterfaceRecord *, FrontendAttrs *>
+FrontendRecordsSlice::addObjCInterface(StringRef Name, RecordLinkage Linkage,
+ const clang::AvailabilityInfo Avail,
+ const Decl *D, HeaderType Access,
+ bool IsEHType) {
ObjCIFSymbolKind SymType =
ObjCIFSymbolKind::Class | ObjCIFSymbolKind::MetaClass;
if (IsEHType)
SymType |= ObjCIFSymbolKind::EHType;
- auto *ObjCR =
+
+ ObjCInterfaceRecord *ObjCR =
llvm::MachO::RecordsSlice::addObjCInterface(Name, Linkage, SymType);
- FrontendRecords.insert({ObjCR, FrontendAttrs{Avail, D, Access}});
- return ObjCR;
+ auto Result =
+ FrontendRecords.insert({ObjCR, FrontendAttrs{Avail, D, Access}});
+ return {ObjCR, &(Result.first->second)};
}
-ObjCCategoryRecord *FrontendRecordsSlice::addObjCCategory(
- StringRef ClassToExtend, StringRef CategoryName,
- const clang::AvailabilityInfo Avail, const Decl *D, HeaderType Access) {
- auto *ObjCR =
+std::pair<ObjCCategoryRecord *, FrontendAttrs *>
+FrontendRecordsSlice::addObjCCategory(StringRef ClassToExtend,
+ StringRef CategoryName,
+ const clang::AvailabilityInfo Avail,
+ const Decl *D, HeaderType Access) {
+ ObjCCategoryRecord *ObjCR =
llvm::MachO::RecordsSlice::addObjCCategory(ClassToExtend, CategoryName);
- FrontendRecords.insert({ObjCR, FrontendAttrs{Avail, D, Access}});
- return ObjCR;
+ auto Result =
+ FrontendRecords.insert({ObjCR, FrontendAttrs{Avail, D, Access}});
+ return {ObjCR, &(Result.first->second)};
}
-ObjCIVarRecord *FrontendRecordsSlice::addObjCIVar(
+std::pair<ObjCIVarRecord *, FrontendAttrs *> FrontendRecordsSlice::addObjCIVar(
ObjCContainerRecord *Container, StringRef IvarName, RecordLinkage Linkage,
const clang::AvailabilityInfo Avail, const Decl *D, HeaderType Access,
const clang::ObjCIvarDecl::AccessControl AC) {
@@ -59,11 +65,12 @@ ObjCIVarRecord *FrontendRecordsSlice::addObjCIVar(
if ((Linkage == RecordLinkage::Exported) &&
((AC == ObjCIvarDecl::Private) || (AC == ObjCIvarDecl::Package)))
Linkage = RecordLinkage::Internal;
- auto *ObjCR =
+ ObjCIVarRecord *ObjCR =
llvm::MachO::RecordsSlice::addObjCIVar(Container, IvarName, Linkage);
- FrontendRecords.insert({ObjCR, FrontendAttrs{Avail, D, Access}});
+ auto Result =
+ FrontendRecords.insert({ObjCR, FrontendAttrs{Avail, D, Access}});
- return nullptr;
+ return {ObjCR, &(Result.first->second)};
}
std::optional<HeaderType>
diff --git a/clang/lib/InstallAPI/Visitor.cpp b/clang/lib/InstallAPI/Visitor.cpp
index b4ed5974a05704..187afe59309d37 100644
--- a/clang/lib/InstallAPI/Visitor.cpp
+++ b/clang/lib/InstallAPI/Visitor.cpp
@@ -11,6 +11,7 @@
#include "clang/AST/ParentMapContext.h"
#include "clang/AST/VTableBuilder.h"
#include "clang/Basic/Linkage.h"
+#include "clang/InstallAPI/DylibVerifier.h"
#include "clang/InstallAPI/FrontendRecords.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringRef.h"
@@ -156,7 +157,9 @@ void InstallAPIVisitor::recordObjCInstanceVariables(
StringRef Name = IV->getName();
const AvailabilityInfo Avail = AvailabilityInfo::createFromDecl(IV);
auto AC = IV->getCanonicalAccessControl();
- Ctx.Slice->addObjCIVar(Record, Name, Linkage, Avail, IV, *Access, AC);
+ auto [ObjCIVR, FA] =
+ Ctx.Slice->addObjCIVar(Record, Name, Linkage, Avail, IV, *Access, AC);
+ Ctx.Verifier->verify(ObjCIVR, FA, SuperClass);
}
}
@@ -178,15 +181,16 @@ bool InstallAPIVisitor::VisitObjCInterfaceDecl(const ObjCInterfaceDecl *D) {
(!D->getASTContext().getLangOpts().ObjCRuntime.isFragile() &&
hasObjCExceptionAttribute(D));
- ObjCInterfaceRecord *Class =
+ auto [Class, FA] =
Ctx.Slice->addObjCInterface(Name, Linkage, Avail, D, *Access, IsEHType);
+ Ctx.Verifier->verify(Class, FA);
// Get base class.
StringRef SuperClassName;
if (const auto *SuperClass = D->getSuperClass())
SuperClassName = SuperClass->getObjCRuntimeNameAsString();
- recordObjCInstanceVariables(D->getASTContext(), Class, SuperClassName,
+ recordObjCInstanceVariables(D->getASTContext(), Class, Class->getName(),
D->ivars());
return true;
}
@@ -201,8 +205,8 @@ bool InstallAPIVisitor::VisitObjCCategoryDecl(const ObjCCategoryDecl *D) {
const ObjCInterfaceDecl *InterfaceD = D->getClassInterface();
const StringRef InterfaceName = InterfaceD->getName();
- ObjCCategoryRecord *Category = Ctx.Slice->addObjCCategory(
- InterfaceName, CategoryName, Avail, D, *Access);
+ auto [Category, FA] = Ctx.Slice->addObjCCategory(InterfaceName, CategoryName,
+ Avail, D, *Access);
recordObjCInstanceVariables(D->getASTContext(), Category, InterfaceName,
D->ivars());
return true;
@@ -236,8 +240,10 @@ bool InstallAPIVisitor::VisitVarDecl(const VarDecl *D) {
const bool WeakDef = D->hasAttr<WeakAttr>();
const bool ThreadLocal = D->getTLSKind() != VarDecl::TLS_None;
const AvailabilityInfo Avail = AvailabilityInfo::createFromDecl(D);
- Ctx.Slice->addGlobal(getMangledName(D), Linkage, GlobalRecord::Kind::Variable,
- Avail, D, *Access, getFlags(WeakDef, ThreadLocal));
+ auto [GR, FA] = Ctx.Slice->addGlobal(getMangledName(D), Linkage,
+ GlobalRecord::Kind::Variable, Avail, D,
+ *Access, getFlags(WeakDef, ThreadLocal));
+ Ctx.Verifier->verify(GR, FA);
return true;
}
@@ -287,8 +293,10 @@ bool InstallAPIVisitor::VisitFunctionDecl(const FunctionDecl *D) {
const RecordLinkage Linkage = (Inlined || !isExported(D))
? RecordLinkage::Internal
: RecordLinkage::Exported;
- Ctx.Slice->addGlobal(Name, Linkage, GlobalRecord::Kind::Function, Avail, D,
- *Access, getFlags(WeakDef), Inlined);
+ auto [GR, FA] =
+ Ctx.Slice->addGlobal(Name, Linkage, GlobalRecord::Kind::Function, Avail,
+ D, *Access, getFlags(WeakDef), Inlined);
+ Ctx.Verifier->verify(GR, FA);
return true;
}
@@ -478,9 +486,10 @@ void InstallAPIVisitor::emitVTableSymbols(const CXXRecordDecl *D,
VTableLinkage == CXXLinkage::WeakODRLinkage) {
const std::string Name = getMangledCXXVTableName(D);
const bool WeakDef = VTableLinkage == CXXLinkage::WeakODRLinkage;
- Ctx.Slice->addGlobal(Name, RecordLinkage::Exported,
- GlobalRecord::Kind::Variable, Avail, D, Access,
- getFlags(WeakDef));
+ auto [GR, FA] = Ctx.Slice->addGlobal(Name, RecordLinkage::Exported,
+ GlobalRecord::Kind::Variable, Avail,
+ D, Access, getFlags(WeakDef));
+ Ctx.Verifier->verify(GR, FA);
if (!D->getDescribedClassTemplate() && !D->isInvalidDecl()) {
VTableContextBase *VTable = D->getASTContext().getVTableContext();
auto AddThunk = [&](GlobalDecl GD) {
@@ -491,9 +500,10 @@ void InstallAPIVisitor::emitVTableSymbols(const CXXRecordDecl *D,
for (const auto &Thunk : *Thunks) {
const std::string Name = getMangledCXXThunk(GD, Thunk);
- Ctx.Slice->addGlobal(Name, RecordLinkage::Exported,
- GlobalRecord::Kind::Function, Avail,
- GD.getDecl(), Access);
+ auto [GR, FA] = Ctx.Slice->addGlobal(Name, RecordLinkage::Exported,
+ GlobalRecord::Kind::Function,
+ Avail, GD.getDecl(), Access);
+ Ctx.Verifier->verify(GR, FA);
}
};
@@ -519,12 +529,16 @@ void InstallAPIVisitor::emitVTableSymbols(const CXXRecordDecl *D,
if (hasRTTI(D)) {
std::string Name = getMangledCXXRTTI(D);
- Ctx.Slice->addGlobal(Name, RecordLinkage::Exported,
- GlobalRecord::Kind::Variable, Avail, D, Access);
+ auto [GR, FA] =
+ Ctx.Slice->addGlobal(Name, RecordLinkage::Exported,
+ GlobalRecord::Kind::Variable, Avail, D, Access);
+ Ctx.Verifier->verify(GR, FA);
Name = getMangledCXXRTTIName(D);
- Ctx.Slice->addGlobal(Name, RecordLinkage::Exported,
- GlobalRecord::Kind::Variable, Avail, D, Access);
+ auto [NamedGR, NamedFA] =
+ Ctx.Slice->addGlobal(Name, RecordLinkage::Exported,
+ GlobalRecord::Kind::Variable, Avail, D, Access);
+ Ctx.Verifier->verify(NamedGR, NamedFA);
}
for (const auto &It : D->bases()) {
@@ -615,15 +629,17 @@ bool InstallAPIVisitor::VisitCXXRecordDecl(const CXXRecordDecl *D) {
continue;
std::string Name = getMangledCtorDtor(M, Ctor_Base);
- Ctx.Slice->addGlobal(Name, RecordLinkage::Exported,
- GlobalRecord::Kind::Function, Avail, D, *Access,
- getFlags(WeakDef));
+ auto [GR, FA] = Ctx.Slice->addGlobal(Name, RecordLinkage::Exported,
+ GlobalRecord::Kind::Function, Avail,
+ D, *Access, getFlags(WeakDef));
+ Ctx.Verifier->verify(GR, FA);
if (!D->isAbstract()) {
std::string Name = getMangledCtorDtor(M, Ctor_Complete);
- Ctx.Slice->addGlobal(Name, RecordLinkage::Exported,
- GlobalRecord::Kind::Function, Avail, D, *Access,
- getFlags(WeakDef));
+ auto [GR, FA] = Ctx.Slice->addGlobal(
+ Name, RecordLinkage::Exported, GlobalRecord::Kind::Function, Avail,
+ D, *Access, getFlags(WeakDef));
+ Ctx.Verifier->verify(GR, FA);
}
continue;
@@ -635,20 +651,23 @@ bool InstallAPIVisitor::VisitCXXRecordDecl(const CXXRecordDecl *D) {
continue;
std::string Name = getMangledCtorDtor(M, Dtor_Base);
- Ctx.Slice->addGlobal(Name, RecordLinkage::Exported,
- GlobalRecord::Kind::Function, Avail, D, *Access,
- getFlags(WeakDef));
+ auto [GR, FA] = Ctx.Slice->addGlobal(Name, RecordLinkage::Exported,
+ GlobalRecord::Kind::Function, Avail,
+ D, *Access, getFlags(WeakDef));
+ Ctx.Verifier->verify(GR, FA);
Name = getMangledCtorDtor(M, Dtor_Complete);
- Ctx.Slice->addGlobal(Name, RecordLinkage::Exported,
- GlobalRecord::Kind::Function, Avail, D, *Access,
- getFlags(WeakDef));
+ auto [CompleteGR, CompleteFA] = Ctx.Slice->addGlobal(
+ Name, RecordLinkage::Exported, GlobalRecord::Kind::Function, Avail, D,
+ *Access, getFlags(WeakDef));
+ Ctx.Verifier->verify(CompleteGR, CompleteFA);
if (Dtor->isVirtual()) {
Name = getMangledCtorDtor(M, Dtor_Deleting);
- Ctx.Slice->addGlobal(Name, RecordLinkage::Exported,
- GlobalRecord::Kind::Function, Avail, D, *Access,
- getFlags(WeakDef));
+ auto [VirtualGR, VirtualFA] = Ctx.Slice->addGlobal(
+ Name, RecordLinkage::Exported, GlobalRecord::Kind::Function, Avail,
+ D, *Access, getFlags(WeakDef));
+ Ctx.Verifier->verify(VirtualGR, VirtualFA);
}
continue;
@@ -661,9 +680,10 @@ bool InstallAPIVisitor::VisitCXXRecordDecl(const CXXRecordDecl *D) {
continue;
std::string Name = getMangledName(M);
- Ctx.Slice->addGlobal(Name, RecordLinkage::Exported,
- GlobalRecord::Kind::Function, Avail, D, *Access,
- getFlags(WeakDef));
+ auto [GR, FA] = Ctx.Slice->addGlobal(Name, RecordLinkage::Exported,
+ GlobalRecord::Kind::Function, Avail, D,
+ *Access, getFlags(WeakDef));
+ Ctx.Verifier->verify(GR, FA);
}
if (auto *Templ = dyn_cast<ClassTemplateSpecializationDecl>(D)) {
@@ -694,9 +714,10 @@ bool InstallAPIVisitor::VisitCXXRecordDecl(const CXXRecordDecl *D) {
const AvailabilityInfo Avail = AvailabilityInfo::createFromDecl(Var);
const bool WeakDef = Var->hasAttr<WeakAttr>() || KeepInlineAsWeak;
- Ctx.Slice->addGlobal(Name, RecordLinkage::Exported,
- GlobalRecord::Kind::Variable, Avail, D, *Access,
- getFlags(WeakDef));
+ auto [GR, FA] = Ctx.Slice->addGlobal(Name, RecordLinkage::Exported,
+ GlobalRecord::Kind::Variable, Avail, D,
+ *Access, getFlags(WeakDef));
+ Ctx.Verifier->verify(GR, FA);
}
return true;
diff --git a/clang/test/InstallAPI/asm.test b/clang/test/InstallAPI/asm.test
new file mode 100644
index 00000000000000..b6af7f643d72f2
--- /dev/null
+++ b/clang/test/InstallAPI/asm.test
@@ -0,0 +1,90 @@
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+// RUN: sed -e "s|DSTROOT|%/t|g" %t/inputs.json.in > %t/inputs.json
+
+// RUN: clang-installapi -target arm64-apple-macos13.1 \
+// RUN: -I%t/usr/include \
+// RUN: -install_name @rpath/lib/libasm.dylib \
+// RUN: %t/inputs.json -o %t/output.tbd 2>&1 | FileCheck %s --allow-empty
+// RUN: llvm-readtapi -compare %t/output.tbd %t/expected.tbd 2>&1 | FileCheck %s --allow-empty
+
+// CHECK-NOT: error:
+// CHECK-NOT: warning:
+
+//--- usr/include/asm.h
+#ifndef ASM_H
+#define ASM_H
+
+extern int ivar __asm("_OBJC_IVAR_$_SomeClass._ivar1");
+extern int objcClass1 __asm("_OBJC_CLASS_$_SomeClass");
+extern int objcClass2 __asm("_OBJC_METACLASS_$_SomeClass");
+extern int objcClass3 __asm("_OBJC_EHTYPE_$_SomeClass");
+extern int objcClass4 __asm(".objc_class_name_SomeClass");
+
+__attribute__((visibility("hidden")))
+ at interface NSString {
+}
+ at end
+
+extern int ivarExtra __asm("_OBJC_IVAR_$_NSString._ivar1");
+#endif // ASM_H
+
+//--- inputs.json.in
+{
+ "headers": [ {
+ "path" : "DSTROOT/usr/include/asm.h",
+ "type" : "public"
+ }],
+ "version": "3"
+}
+
+//--- expected.tbd
+{
+ "main_library": {
+ "compatibility_versions": [
+ {
+ "version": "0"
+ }
+ ],
+ "current_versions": [
+ {
+ "version": "0"
+ }
+ ],
+ "exported_symbols": [
+ {
+ "data": {
+ "objc_class": [
+ "SomeClass"
+ ],
+ "objc_eh_type": [
+ "SomeClass"
+ ],
+ "objc_ivar": [
+ "NSString._ivar1",
+ "SomeClass._ivar1"
+ ]
+ }
+ }
+ ],
+ "flags": [
+ {
+ "attributes": [
+ "not_app_extension_safe"
+ ]
+ }
+ ],
+ "install_names": [
+ {
+ "name": "@rpath/lib/libasm.dylib"
+ }
+ ],
+ "target_info": [
+ {
+ "min_deployment": "13.1",
+ "target": "arm64-macos"
+ }
+ ]
+ },
+ "tapi_tbd_version": 5
+}
diff --git a/clang/tools/clang-installapi/ClangInstallAPI.cpp b/clang/tools/clang-installapi/ClangInstallAPI.cpp
index fdf7628cabd807..d758f731179008 100644
--- a/clang/tools/clang-installapi/ClangInstallAPI.cpp
+++ b/clang/tools/clang-installapi/ClangInstallAPI.cpp
@@ -116,6 +116,7 @@ static bool run(ArrayRef<const char *> Args, const char *ProgName) {
for (const HeaderType Type :
{HeaderType::Public, HeaderType::Private, HeaderType::Project}) {
Ctx.Slice = std::make_shared<FrontendRecordsSlice>(Trip);
+ Ctx.Verifier->setTarget(Targ);
Ctx.Type = Type;
if (!runFrontend(ProgName, Opts.DriverOpts.Verbose, Ctx,
InMemoryFileSystem.get(), Opts.getClangFrontendArgs()))
@@ -124,6 +125,9 @@ static bool run(ArrayRef<const char *> Args, const char *ProgName) {
}
}
+ if (Ctx.Verifier->getState() == DylibVerifier::Result::Invalid)
+ return EXIT_FAILURE;
+
// After symbols have been collected, prepare to write output.
auto Out = CI->createOutputFile(Ctx.OutputLoc, /*Binary=*/false,
/*RemoveFileOnSignal=*/false,
@@ -133,13 +137,7 @@ static bool run(ArrayRef<const char *> Args, const char *ProgName) {
return EXIT_FAILURE;
// Assign attributes for serialization.
- auto Symbols = std::make_unique<SymbolSet>();
- for (const auto &FR : FrontendResults) {
- SymbolConverter Converter(Symbols.get(), FR->getTarget());
- FR->visit(Converter);
- }
-
- InterfaceFile IF(std::move(Symbols));
+ InterfaceFile IF(Ctx.Verifier->getExports());
for (const auto &TargetInfo : Opts.DriverOpts.Targets) {
IF.addTarget(TargetInfo.first);
IF.setFromBinaryAttrs(Ctx.BA, TargetInfo.first);
diff --git a/clang/tools/clang-installapi/Options.cpp b/clang/tools/clang-installapi/Options.cpp
index 7c8272a8ba7e05..e5ff8a1aaa4fe5 100644
--- a/clang/tools/clang-installapi/Options.cpp
+++ b/clang/tools/clang-installapi/Options.cpp
@@ -301,10 +301,11 @@ InstallAPIContext Options::createContext() {
}
}
- // Parse binary dylib.
- // TODO: Initialize verifier.
- if (DriverOpts.DylibToVerify.empty())
+ // Parse binary dylib and initialize verifier.
+ if (DriverOpts.DylibToVerify.empty()) {
+ Ctx.Verifier = std::make_unique<DylibVerifier>();
return Ctx;
+ }
auto Buffer = FM->getBufferForFile(DriverOpts.DylibToVerify);
if (auto Err = Buffer.getError()) {
@@ -322,6 +323,8 @@ InstallAPIContext Options::createContext() {
return Ctx;
}
+ Ctx.Verifier = std::make_unique<DylibVerifier>(
+ std::move(*Slices), Diags, DriverOpts.VerifyMode, DriverOpts.Demangle);
return Ctx;
}
diff --git a/llvm/include/llvm/TextAPI/Record.h b/llvm/include/llvm/TextAPI/Record.h
index 98639b064eaadd..ef152ce433877c 100644
--- a/llvm/include/llvm/TextAPI/Record.h
+++ b/llvm/include/llvm/TextAPI/Record.h
@@ -51,7 +51,8 @@ class Record {
public:
Record() = default;
Record(StringRef Name, RecordLinkage Linkage, SymbolFlags Flags)
- : Name(Name), Linkage(Linkage), Flags(mergeFlags(Flags, Linkage)) {}
+ : Name(Name), Linkage(Linkage), Flags(mergeFlags(Flags, Linkage)),
+ Verified(false) {}
bool isWeakDefined() const {
return (Flags & SymbolFlags::WeakDefined) == SymbolFlags::WeakDefined;
@@ -79,6 +80,9 @@ class Record {
bool isExported() const { return Linkage >= RecordLinkage::Rexported; }
bool isRexported() const { return Linkage == RecordLinkage::Rexported; }
+ bool isVerified() const { return Verified; }
+ void setVerify(bool V = true) { Verified = V; }
+
StringRef getName() const { return Name; }
SymbolFlags getFlags() const { return Flags; }
@@ -89,6 +93,7 @@ class Record {
StringRef Name;
RecordLinkage Linkage;
SymbolFlags Flags;
+ bool Verified;
friend class RecordsSlice;
};
>From 7080281691fcd4dd62c59773b3419ff6837b97a5 Mon Sep 17 00:00:00 2001
From: Cyndy Ishida <cyndy_ishida at apple.com>
Date: Wed, 13 Mar 2024 18:57:14 -0700
Subject: [PATCH 2/3] Add Verifier
---
.../clang/Basic/DiagnosticInstallAPIKinds.td | 13 ++
.../include/clang/InstallAPI/DylibVerifier.h | 44 +++++-
clang/include/clang/InstallAPI/Frontend.h | 3 +
clang/include/clang/InstallAPI/MachO.h | 1 +
clang/lib/InstallAPI/DylibVerifier.cpp | 146 +++++++++++++++++-
.../clang-installapi/ClangInstallAPI.cpp | 6 +-
6 files changed, 202 insertions(+), 11 deletions(-)
diff --git a/clang/include/clang/Basic/DiagnosticInstallAPIKinds.td b/clang/include/clang/Basic/DiagnosticInstallAPIKinds.td
index 31be4f09cf3a1c..be5cccd90e0eb9 100644
--- a/clang/include/clang/Basic/DiagnosticInstallAPIKinds.td
+++ b/clang/include/clang/Basic/DiagnosticInstallAPIKinds.td
@@ -17,4 +17,17 @@ def err_no_install_name : Error<"no install name specified: add -install_name <p
def err_no_output_file: Error<"no output file specified">;
} // end of command line category.
+let CategoryName = "Verification" in {
+def warn_target: Warning<"violations found for %0">;
+def err_library_missing_symbol : Error<"declaration has external linkage, but dynamic library doesn't have symbol '%0'">;
+def warn_library_missing_symbol : Warning<"declaration has external linkage, but dynamic library doesn't have symbol '%0'">;
+def err_library_hidden_symbol : Error<"declaration has external linkage, but symbol has internal linkage in dynamic library '%0'">;
+def warn_library_hidden_symbol : Warning<"declaration has external linkage, but symbol has internal linkage in dynamic library '%0'">;
+def warn_header_hidden_symbol : Warning<"symbol exported in dynamic library, but marked hidden in declaration '%0'">;
+def warn_header_symbol_flags_mismatch : Warning<"declaration '%0' is "
+ "%select{weak defined|thread local}1, but symbol is not in dynamic library">;
+def err_header_symbol_missing : Error<"no declaration found for exported symbol '%0' in dynamic library">;
+def err_header_hidden_symbol : Error<"symbol exported in dynamic library, but marked hidden in declaration '%0'">;
+} // end of Verification category.
+
} // end of InstallAPI component
diff --git a/clang/include/clang/InstallAPI/DylibVerifier.h b/clang/include/clang/InstallAPI/DylibVerifier.h
index 72c4743fdf65e0..dee52f81f2c368 100644
--- a/clang/include/clang/InstallAPI/DylibVerifier.h
+++ b/clang/include/clang/InstallAPI/DylibVerifier.h
@@ -38,19 +38,34 @@ class DylibVerifier {
// Current target being verified against the AST.
llvm::MachO::Target Target;
+ // Target specific API from binary.
+ RecordsSlice *DylibSlice;
+
// Query state of verification after AST has been traversed.
Result FrontendState;
// First error for AST traversal, which is tied to the target triple.
bool DiscoveredFirstError;
+
+ // Determines what kind of banner to print a violation for.
+ bool PrintArch = false;
+
+ // Engine for reporting violations.
+ DiagnosticsEngine *Diag = nullptr;
+
+ // Handle diagnostics reporting for target level violations.
+ void emitDiag(llvm::function_ref<void()> Report);
+
+ VerifierContext() = default;
+ VerifierContext(DiagnosticsEngine *Diag) : Diag(Diag) {}
};
DylibVerifier() = default;
DylibVerifier(llvm::MachO::Records &&Dylib, DiagnosticsEngine *Diag,
VerificationMode Mode, bool Demangle)
- : Dylib(std::move(Dylib)), Diag(Diag), Mode(Mode), Demangle(Demangle),
- Exports(std::make_unique<SymbolSet>()) {}
+ : Dylib(std::move(Dylib)), Mode(Mode), Demangle(Demangle),
+ Exports(std::make_unique<SymbolSet>()), Ctx(VerifierContext{Diag}) {}
Result verify(GlobalRecord *R, const FrontendAttrs *FA);
Result verify(ObjCInterfaceRecord *R, const FrontendAttrs *FA);
@@ -66,6 +81,13 @@ class DylibVerifier {
/// Get result of verification.
Result getState() const { return Ctx.FrontendState; }
+ /// Set different source managers to the same diagnostics engine.
+ void setSourceManager(SourceManager &SourceMgr) const {
+ if (!Ctx.Diag)
+ return;
+ Ctx.Diag->setSourceManager(&SourceMgr);
+ }
+
private:
/// Determine whether to compare declaration to symbol in binary.
bool canVerify();
@@ -73,6 +95,16 @@ class DylibVerifier {
/// Shared implementation for verifying exported symbols.
Result verifyImpl(Record *R, SymbolContext &SymCtx);
+ /// Check if declaration is marked as obsolete, they are
+ // expected to result in a symbol mismatch.
+ bool shouldIgnoreObsolete(const Record *R, SymbolContext &SymCtx,
+ const Record *DR);
+
+ /// Compare the visibility declarations to the linkage of symbol found in
+ /// dylib.
+ Result compareVisibility(const Record *R, SymbolContext &SymCtx,
+ const Record *DR);
+
/// Update result state on each call to `verify`.
void updateState(Result State);
@@ -80,14 +112,14 @@ class DylibVerifier {
void addSymbol(const Record *R, SymbolContext &SymCtx,
TargetList &&Targets = {});
+ /// Find matching dylib slice for target triple that is being parsed.
+ void assignSlice(const Target &T);
+
// Symbols in dylib.
llvm::MachO::Records Dylib;
- // Engine for reporting violations.
- [[maybe_unused]] DiagnosticsEngine *Diag = nullptr;
-
// Controls what class of violations to report.
- [[maybe_unused]] VerificationMode Mode = VerificationMode::Invalid;
+ VerificationMode Mode = VerificationMode::Invalid;
// Attempt to demangle when reporting violations.
bool Demangle = false;
diff --git a/clang/include/clang/InstallAPI/Frontend.h b/clang/include/clang/InstallAPI/Frontend.h
index 660fc8cd69a59d..5cccd891c58093 100644
--- a/clang/include/clang/InstallAPI/Frontend.h
+++ b/clang/include/clang/InstallAPI/Frontend.h
@@ -17,6 +17,7 @@
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendActions.h"
#include "clang/InstallAPI/Context.h"
+#include "clang/InstallAPI/DylibVerifier.h"
#include "clang/InstallAPI/Visitor.h"
#include "llvm/ADT/Twine.h"
#include "llvm/Support/MemoryBuffer.h"
@@ -34,6 +35,8 @@ class InstallAPIAction : public ASTFrontendAction {
std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
StringRef InFile) override {
+ Ctx.Diags->getClient()->BeginSourceFile(CI.getLangOpts());
+ Ctx.Verifier->setSourceManager(CI.getSourceManager());
return std::make_unique<InstallAPIVisitor>(
CI.getASTContext(), Ctx, CI.getSourceManager(), CI.getPreprocessor());
}
diff --git a/clang/include/clang/InstallAPI/MachO.h b/clang/include/clang/InstallAPI/MachO.h
index 6dee6f22420381..a77766511fa3e5 100644
--- a/clang/include/clang/InstallAPI/MachO.h
+++ b/clang/include/clang/InstallAPI/MachO.h
@@ -32,6 +32,7 @@ using ObjCInterfaceRecord = llvm::MachO::ObjCInterfaceRecord;
using ObjCCategoryRecord = llvm::MachO::ObjCCategoryRecord;
using ObjCIVarRecord = llvm::MachO::ObjCIVarRecord;
using Records = llvm::MachO::Records;
+using RecordsSlice = llvm::MachO::RecordsSlice;
using BinaryAttrs = llvm::MachO::RecordsSlice::BinaryAttrs;
using SymbolSet = llvm::MachO::SymbolSet;
using SimpleSymbol = llvm::MachO::SimpleSymbol;
diff --git a/clang/lib/InstallAPI/DylibVerifier.cpp b/clang/lib/InstallAPI/DylibVerifier.cpp
index b7dd85d63fa14f..e7dce16072238b 100644
--- a/clang/lib/InstallAPI/DylibVerifier.cpp
+++ b/clang/lib/InstallAPI/DylibVerifier.cpp
@@ -1,5 +1,6 @@
#include "clang/InstallAPI/DylibVerifier.h"
#include "clang/InstallAPI/FrontendRecords.h"
+#include "clang/InstallAPI/InstallAPIDiagnostic.h"
#include "llvm/Demangle/Demangle.h"
using namespace llvm::MachO;
@@ -24,6 +25,9 @@ struct DylibVerifier::SymbolContext {
// The ObjCInterface symbol type, if applicable.
ObjCIFSymbolKind ObjCIFKind = ObjCIFSymbolKind::None;
+
+ // Whether Decl is inlined.
+ bool Inlined = false;
};
static std::string
@@ -109,6 +113,30 @@ static DylibVerifier::Result updateResult(const DylibVerifier::Result 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);
@@ -122,6 +150,66 @@ void DylibVerifier::addSymbol(const Record *R, SymbolContext &SymCtx,
Exports->addGlobal(SymCtx.Kind, SymCtx.SymbolName, R->getFlags(), Targets);
}
+bool DylibVerifier::shouldIgnoreObsolete(const Record *R, SymbolContext &SymCtx,
+ const Record *DR) {
+ return SymCtx.FA->Avail.isObsoleted();
+}
+
+DylibVerifier::Result DylibVerifier::compareVisibility(const Record *R,
+ SymbolContext &SymCtx,
+ const Record *DR) {
+
+ if (R->isExported()) {
+ if (!DR) {
+ Ctx.emitDiag([&]() {
+ Ctx.Diag->Report(SymCtx.FA->D->getLocation(),
+ diag::err_library_missing_symbol)
+ << SymCtx.PrettyPrintName;
+ });
+ return Result::Invalid;
+ }
+ if (DR->isInternal()) {
+ Ctx.emitDiag([&]() {
+ Ctx.Diag->Report(SymCtx.FA->D->getLocation(),
+ diag::err_library_hidden_symbol)
+ << SymCtx.PrettyPrintName;
+ });
+ 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;
+
+ 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->D->getLocation(), ID)
+ << SymCtx.PrettyPrintName;
+ });
+ return Outcome;
+ }
+
+ if (R->isInternal())
+ return Result::Ignore;
+
+ return Result::Valid;
+}
+
DylibVerifier::Result DylibVerifier::verifyImpl(Record *R,
SymbolContext &SymCtx) {
R->setVerify();
@@ -133,6 +221,30 @@ DylibVerifier::Result DylibVerifier::verifyImpl(Record *R,
}
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.isUnconditionallyUnavailable() &&
+ (!DR || DR->isInternal())) {
+ updateState(Result::Valid);
+ return Ctx.FrontendState;
+ }
+
+ Result VisibilityCheck = compareVisibility(R, SymCtx, DR);
+ if (VisibilityCheck != Result::Valid) {
+ updateState(VisibilityCheck);
+ return Ctx.FrontendState;
+ }
+
return Ctx.FrontendState;
}
@@ -140,10 +252,29 @@ 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;
- updateState(Dylib.empty() ? Result::NoVerify : Result::Ignore);
+ if (Dylib.empty()) {
+ updateState(Result::NoVerify);
+ return;
+ }
+ updateState(Result::Ignore);
+ assignSlice(T);
}
DylibVerifier::Result DylibVerifier::verify(ObjCIVarRecord *R,
@@ -205,8 +336,21 @@ DylibVerifier::Result DylibVerifier::verify(GlobalRecord *R,
getAnnotatedName(R, Sym.Kind, Demangle ? demangle(Sym.Name) : 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) {
+ if (!DiscoveredFirstError) {
+ Diag->Report(diag::warn_target)
+ << (PrintArch ? getArchitectureName(Target.Arch)
+ : getTargetTripleName(Target));
+ DiscoveredFirstError = true;
+ }
+
+ Report();
+}
+
} // namespace installapi
} // namespace clang
diff --git a/clang/tools/clang-installapi/ClangInstallAPI.cpp b/clang/tools/clang-installapi/ClangInstallAPI.cpp
index d758f731179008..54e82d78d4d228 100644
--- a/clang/tools/clang-installapi/ClangInstallAPI.cpp
+++ b/clang/tools/clang-installapi/ClangInstallAPI.cpp
@@ -111,17 +111,15 @@ static bool run(ArrayRef<const char *> Args, const char *ProgName) {
// Execute and gather AST results.
// An invocation is ran for each unique target triple and for each header
// access level.
- Records FrontendResults;
for (const auto &[Targ, Trip] : Opts.DriverOpts.Targets) {
+ Ctx.Verifier->setTarget(Targ);
+ Ctx.Slice = std::make_shared<FrontendRecordsSlice>(Trip);
for (const HeaderType Type :
{HeaderType::Public, HeaderType::Private, HeaderType::Project}) {
- Ctx.Slice = std::make_shared<FrontendRecordsSlice>(Trip);
- Ctx.Verifier->setTarget(Targ);
Ctx.Type = Type;
if (!runFrontend(ProgName, Opts.DriverOpts.Verbose, Ctx,
InMemoryFileSystem.get(), Opts.getClangFrontendArgs()))
return EXIT_FAILURE;
- FrontendResults.emplace_back(std::move(Ctx.Slice));
}
}
>From 91fe39df5b948a0f1ccb35c50fc843f75fc601dd Mon Sep 17 00:00:00 2001
From: Cyndy Ishida <cyndy_ishida at apple.com>
Date: Wed, 13 Mar 2024 21:44:34 -0700
Subject: [PATCH 3/3] f
---
.../clang/Basic/DiagnosticInstallAPIKinds.td | 14 +-
.../include/clang/InstallAPI/DylibVerifier.h | 13 ++
clang/lib/InstallAPI/DylibVerifier.cpp | 172 ++++++++++++++++++
3 files changed, 197 insertions(+), 2 deletions(-)
diff --git a/clang/include/clang/Basic/DiagnosticInstallAPIKinds.td b/clang/include/clang/Basic/DiagnosticInstallAPIKinds.td
index be5cccd90e0eb9..5ed2e23425dc5f 100644
--- a/clang/include/clang/Basic/DiagnosticInstallAPIKinds.td
+++ b/clang/include/clang/Basic/DiagnosticInstallAPIKinds.td
@@ -24,10 +24,20 @@ def warn_library_missing_symbol : Warning<"declaration has external linkage, but
def err_library_hidden_symbol : Error<"declaration has external linkage, but symbol has internal linkage in dynamic library '%0'">;
def warn_library_hidden_symbol : Warning<"declaration has external linkage, but symbol has internal linkage in dynamic library '%0'">;
def warn_header_hidden_symbol : Warning<"symbol exported in dynamic library, but marked hidden in declaration '%0'">;
+def err_header_hidden_symbol : Error<"symbol exported in dynamic library, but marked hidden in declaration '%0'">;
+def err_header_symbol_missing : Error<"no declaration found for exported symbol '%0' in dynamic library">;
+def warn_header_availability_mismatch : Warning<"declaration '%0' is marked %select{available|unavailable}1,"
+ " but symbol is %select{not |}2exported in dynamic library">;
+def err_header_availability_mismatch : Error<"declaration '%0' is marked %select{available|unavailable}1,"
+ " but symbol is %select{not |}2exported in dynamic library">;
+def warn_dylib_symbol_flags_mismatch : Warning<"dynamic library symbol '%0' is "
+ "%select{weak defined|thread local}1, but its declaration is not">;
def warn_header_symbol_flags_mismatch : Warning<"declaration '%0' is "
"%select{weak defined|thread local}1, but symbol is not in dynamic library">;
-def err_header_symbol_missing : Error<"no declaration found for exported symbol '%0' in dynamic library">;
-def err_header_hidden_symbol : Error<"symbol exported in dynamic library, but marked hidden in declaration '%0'">;
+def err_dylib_symbol_flags_mismatch : Error<"dynamic library symbol '%0' is "
+ "%select{weak defined|thread local}1, but its declaration is not">;
+def err_header_symbol_flags_mismatch : Error<"declaration '%0' is "
+ "%select{weak defined|thread local}1, but symbol is not in dynamic library">;
} // end of Verification category.
} // end of InstallAPI component
diff --git a/clang/include/clang/InstallAPI/DylibVerifier.h b/clang/include/clang/InstallAPI/DylibVerifier.h
index dee52f81f2c368..2094548ced8d5f 100644
--- a/clang/include/clang/InstallAPI/DylibVerifier.h
+++ b/clang/include/clang/InstallAPI/DylibVerifier.h
@@ -105,6 +105,19 @@ class DylibVerifier {
Result compareVisibility(const Record *R, SymbolContext &SymCtx,
const Record *DR);
+ /// An ObjCInterfaceRecord can represent up to three symbols. When verifying,
+ // account for this granularity.
+ bool compareObjCInterfaceSymbols(const Record *R, SymbolContext &SymCtx,
+ const ObjCInterfaceRecord *DR);
+
+ /// Validate availability annotations against dylib.
+ Result compareAvailability(const Record *R, SymbolContext &SymCtx,
+ const Record *DR);
+
+ /// Compare and validate matching symbol flags.
+ bool compareSymbolFlags(const Record *R, SymbolContext &SymCtx,
+ const Record *DR);
+
/// Update result state on each call to `verify`.
void updateState(Result State);
diff --git a/clang/lib/InstallAPI/DylibVerifier.cpp b/clang/lib/InstallAPI/DylibVerifier.cpp
index e7dce16072238b..f0021b60bdebb9 100644
--- a/clang/lib/InstallAPI/DylibVerifier.cpp
+++ b/clang/lib/InstallAPI/DylibVerifier.cpp
@@ -155,6 +155,75 @@ bool DylibVerifier::shouldIgnoreObsolete(const Record *R, SymbolContext &SymCtx,
return SymCtx.FA->Avail.isObsoleted();
}
+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->D->getLocation(),
+ PrintAsWarning ? diag::warn_library_missing_symbol
+ : diag::err_library_missing_symbol)
+ << SymName;
+ });
+ else
+ Ctx.emitDiag([&]() {
+ Ctx.Diag->Report(SymCtx.FA->D->getLocation(),
+ 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))
+ PrintDiagnostic(DR->getLinkageForSymbol(ObjCIFSymbolKind::Class), R,
+ getAnnotatedName(R, SymCtx.Kind, SymCtx.PrettyPrintName,
+ /*ValidSourceLoc=*/true,
+ ObjCIFSymbolKind::Class),
+ /*PrintAsWarning=*/true);
+
+ if (!DR->isExportedSymbol(ObjCIFSymbolKind::MetaClass))
+ PrintDiagnostic(DR->getLinkageForSymbol(ObjCIFSymbolKind::MetaClass), R,
+ getAnnotatedName(R, SymCtx.Kind, SymCtx.PrettyPrintName,
+ /*ValidSourceLoc=*/true,
+ ObjCIFSymbolKind::MetaClass),
+ /*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.PrettyPrintName);
+ return false;
+}
+
DylibVerifier::Result DylibVerifier::compareVisibility(const Record *R,
SymbolContext &SymCtx,
const Record *DR) {
@@ -210,6 +279,81 @@ DylibVerifier::Result DylibVerifier::compareVisibility(const Record *R,
return Result::Valid;
}
+DylibVerifier::Result DylibVerifier::compareAvailability(const Record *R,
+ SymbolContext &SymCtx,
+ const Record *DR) {
+ if (!SymCtx.FA->Avail.isUnconditionallyUnavailable())
+ return Result::Valid;
+
+ const bool IsDeclAvailable = SymCtx.FA->Avail.isUnconditionallyUnavailable();
+
+ switch (Mode) {
+ case VerificationMode::ErrorsAndWarnings:
+ Ctx.emitDiag([&]() {
+ Ctx.Diag->Report(SymCtx.FA->D->getLocation(),
+ diag::warn_header_availability_mismatch)
+ << SymCtx.PrettyPrintName << IsDeclAvailable << IsDeclAvailable;
+ });
+ return Result::Ignore;
+ case VerificationMode::Pedantic:
+ Ctx.emitDiag([&]() {
+ Ctx.Diag->Report(SymCtx.FA->D->getLocation(),
+ diag::err_header_availability_mismatch)
+ << SymCtx.PrettyPrintName << 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) {
+ std::string DisplayName =
+ Demangle ? demangle(DR->getName()) : DR->getName().str();
+
+ if (DR->isThreadLocalValue() && !R->isThreadLocalValue()) {
+ Ctx.emitDiag([&]() {
+ Ctx.Diag->Report(SymCtx.FA->D->getLocation(),
+ diag::err_dylib_symbol_flags_mismatch)
+ << getAnnotatedName(DR, SymCtx.Kind, DisplayName)
+ << DR->isThreadLocalValue();
+ });
+ return false;
+ }
+ if (!DR->isThreadLocalValue() && R->isThreadLocalValue()) {
+ Ctx.emitDiag([&]() {
+ SymCtx.FA->D->getLocation(),
+ Ctx.Diag->Report(diag::err_header_symbol_flags_mismatch)
+ << SymCtx.PrettyPrintName << R->isThreadLocalValue();
+ });
+ return false;
+ }
+
+ if (DR->isWeakDefined() && !R->isWeakDefined()) {
+ Ctx.emitDiag([&]() {
+ Ctx.Diag->Report(SymCtx.FA->D->getLocation(),
+ diag::err_dylib_symbol_flags_mismatch)
+ << getAnnotatedName(DR, SymCtx.Kind, DisplayName)
+ << R->isWeakDefined();
+ });
+ return false;
+ }
+ if (!DR->isWeakDefined() && R->isWeakDefined()) {
+ Ctx.emitDiag([&]() {
+ Ctx.Diag->Report(SymCtx.FA->D->getLocation(),
+ diag::err_header_symbol_flags_mismatch)
+ << SymCtx.PrettyPrintName << R->isWeakDefined();
+ });
+ return false;
+ }
+
+ return true;
+}
+
DylibVerifier::Result DylibVerifier::verifyImpl(Record *R,
SymbolContext &SymCtx) {
R->setVerify();
@@ -245,6 +389,34 @@ DylibVerifier::Result DylibVerifier::verifyImpl(Record *R,
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;
}
More information about the llvm-branch-commits
mailing list