[clang] f2794cc - [InstallAPI] Introduce Basic Verifier (#85106)
via cfe-commits
cfe-commits at lists.llvm.org
Sat Mar 16 10:37:56 PDT 2024
Author: Cyndy Ishida
Date: 2024-03-16T10:37:52-07:00
New Revision: f2794ccede6d32a6b5ef7a376ced420331e2be27
URL: https://github.com/llvm/llvm-project/commit/f2794ccede6d32a6b5ef7a376ced420331e2be27
DIFF: https://github.com/llvm/llvm-project/commit/f2794ccede6d32a6b5ef7a376ced420331e2be27.diff
LOG: [InstallAPI] Introduce Basic Verifier (#85106)
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 check against the dylib's symbol table.
Added:
clang/lib/InstallAPI/DylibVerifier.cpp
clang/test/InstallAPI/asm.test
Modified:
clang/include/clang/AST/Availability.h
clang/include/clang/InstallAPI/Context.h
clang/include/clang/InstallAPI/DylibVerifier.h
clang/include/clang/InstallAPI/Frontend.h
clang/include/clang/InstallAPI/FrontendRecords.h
clang/include/clang/InstallAPI/MachO.h
clang/lib/InstallAPI/CMakeLists.txt
clang/lib/InstallAPI/Frontend.cpp
clang/lib/InstallAPI/Visitor.cpp
clang/tools/clang-installapi/ClangInstallAPI.cpp
clang/tools/clang-installapi/Options.cpp
llvm/include/llvm/TextAPI/Record.h
Removed:
################################################################################
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..54e517544b8edf 100644
--- a/clang/include/clang/InstallAPI/Context.h
+++ b/clang/include/clang/InstallAPI/Context.h
@@ -11,6 +11,7 @@
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/FileManager.h"
+#include "clang/InstallAPI/DylibVerifier.h"
#include "clang/InstallAPI/HeaderFile.h"
#include "clang/InstallAPI/MachO.h"
#include "llvm/ADT/DenseMap.h"
@@ -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..894db699578f20 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;
};
More information about the cfe-commits
mailing list