Skip to content

Commit 936519f

Browse files
authored
[InstallAPI] Verify that declarations in headers map to exports found in dylib (#85348)
* This completes support for verifying every declaration found in a header is discovered in the dylib. Diagnostics are reported for each class for differences that are representable in TBD files. * This patch also now captures unavailable attributes that depend on target triples. This is needed for proper tbd file generation.
1 parent de159ae commit 936519f

File tree

15 files changed

+1782
-26
lines changed

15 files changed

+1782
-26
lines changed

clang/include/clang/AST/Availability.h

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ struct AvailabilityInfo {
6767
VersionTuple Introduced;
6868
VersionTuple Deprecated;
6969
VersionTuple Obsoleted;
70+
bool Unavailable = false;
7071
bool UnconditionallyDeprecated = false;
7172
bool UnconditionallyUnavailable = false;
7273

@@ -78,6 +79,12 @@ struct AvailabilityInfo {
7879
/// Check if the symbol has been obsoleted.
7980
bool isObsoleted() const { return !Obsoleted.empty(); }
8081

82+
/// Check if the symbol is unavailable unconditionally or
83+
/// on the active platform and os version.
84+
bool isUnavailable() const {
85+
return Unavailable || isUnconditionallyUnavailable();
86+
}
87+
8188
/// Check if the symbol is unconditionally deprecated.
8289
///
8390
/// i.e. \code __attribute__((deprecated)) \endcode
@@ -91,9 +98,10 @@ struct AvailabilityInfo {
9198
}
9299

93100
AvailabilityInfo(StringRef Domain, VersionTuple I, VersionTuple D,
94-
VersionTuple O, bool UD, bool UU)
101+
VersionTuple O, bool U, bool UD, bool UU)
95102
: Domain(Domain), Introduced(I), Deprecated(D), Obsoleted(O),
96-
UnconditionallyDeprecated(UD), UnconditionallyUnavailable(UU) {}
103+
Unavailable(U), UnconditionallyDeprecated(UD),
104+
UnconditionallyUnavailable(UU) {}
97105

98106
friend bool operator==(const AvailabilityInfo &Lhs,
99107
const AvailabilityInfo &Rhs);
@@ -105,10 +113,10 @@ struct AvailabilityInfo {
105113
inline bool operator==(const AvailabilityInfo &Lhs,
106114
const AvailabilityInfo &Rhs) {
107115
return std::tie(Lhs.Introduced, Lhs.Deprecated, Lhs.Obsoleted,
108-
Lhs.UnconditionallyDeprecated,
116+
Lhs.Unavailable, Lhs.UnconditionallyDeprecated,
109117
Lhs.UnconditionallyUnavailable) ==
110118
std::tie(Rhs.Introduced, Rhs.Deprecated, Rhs.Obsoleted,
111-
Rhs.UnconditionallyDeprecated,
119+
Rhs.Unavailable, Rhs.UnconditionallyDeprecated,
112120
Rhs.UnconditionallyUnavailable);
113121
}
114122

clang/include/clang/Basic/DiagnosticGroups.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1508,3 +1508,7 @@ def ReadOnlyPlacementChecks : DiagGroup<"read-only-types">;
15081508
// Warnings and fixes to support the "safe buffers" programming model.
15091509
def UnsafeBufferUsageInContainer : DiagGroup<"unsafe-buffer-usage-in-container">;
15101510
def UnsafeBufferUsage : DiagGroup<"unsafe-buffer-usage", [UnsafeBufferUsageInContainer]>;
1511+
1512+
// Warnings and notes InstallAPI verification.
1513+
def InstallAPIViolation : DiagGroup<"installapi-violation">;
1514+

clang/include/clang/Basic/DiagnosticInstallAPIKinds.td

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,27 @@ def err_no_install_name : Error<"no install name specified: add -install_name <p
1717
def err_no_output_file: Error<"no output file specified">;
1818
} // end of command line category.
1919

20+
let CategoryName = "Verification" in {
21+
def warn_target: Warning<"violations found for %0">, InGroup<InstallAPIViolation>;
22+
def err_library_missing_symbol : Error<"declaration has external linkage, but dynamic library doesn't have symbol '%0'">;
23+
def warn_library_missing_symbol : Warning<"declaration has external linkage, but dynamic library doesn't have symbol '%0'">, InGroup<InstallAPIViolation>;
24+
def err_library_hidden_symbol : Error<"declaration has external linkage, but symbol has internal linkage in dynamic library '%0'">;
25+
def warn_library_hidden_symbol : Warning<"declaration has external linkage, but symbol has internal linkage in dynamic library '%0'">, InGroup<InstallAPIViolation>;
26+
def warn_header_hidden_symbol : Warning<"symbol exported in dynamic library, but marked hidden in declaration '%0'">, InGroup<InstallAPIViolation>;
27+
def err_header_hidden_symbol : Error<"symbol exported in dynamic library, but marked hidden in declaration '%0'">;
28+
def err_header_symbol_missing : Error<"no declaration found for exported symbol '%0' in dynamic library">;
29+
def warn_header_availability_mismatch : Warning<"declaration '%0' is marked %select{available|unavailable}1,"
30+
" but symbol is %select{not |}2exported in dynamic library">, InGroup<InstallAPIViolation>;
31+
def err_header_availability_mismatch : Error<"declaration '%0' is marked %select{available|unavailable}1,"
32+
" but symbol is %select{not |}2exported in dynamic library">;
33+
def warn_dylib_symbol_flags_mismatch : Warning<"dynamic library symbol '%0' is "
34+
"%select{weak defined|thread local}1, but its declaration is not">, InGroup<InstallAPIViolation>;
35+
def warn_header_symbol_flags_mismatch : Warning<"declaration '%0' is "
36+
"%select{weak defined|thread local}1, but symbol is not in dynamic library">, InGroup<InstallAPIViolation>;
37+
def err_dylib_symbol_flags_mismatch : Error<"dynamic library symbol '%0' is "
38+
"%select{weak defined|thread local}1, but its declaration is not">;
39+
def err_header_symbol_flags_mismatch : Error<"declaration '%0' is "
40+
"%select{weak defined|thread local}1, but symbol is not in dynamic library">;
41+
} // end of Verification category.
42+
2043
} // end of InstallAPI component

clang/include/clang/InstallAPI/DylibVerifier.h

Lines changed: 53 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -38,19 +38,34 @@ class DylibVerifier {
3838
// Current target being verified against the AST.
3939
llvm::MachO::Target Target;
4040

41+
// Target specific API from binary.
42+
RecordsSlice *DylibSlice = nullptr;
43+
4144
// Query state of verification after AST has been traversed.
42-
Result FrontendState;
45+
Result FrontendState = Result::Ignore;
4346

4447
// First error for AST traversal, which is tied to the target triple.
45-
bool DiscoveredFirstError;
48+
bool DiscoveredFirstError = false;
49+
50+
// Determines what kind of banner to print a violation for.
51+
bool PrintArch = false;
52+
53+
// Engine for reporting violations.
54+
DiagnosticsEngine *Diag = nullptr;
55+
56+
// Handle diagnostics reporting for target level violations.
57+
void emitDiag(llvm::function_ref<void()> Report);
58+
59+
VerifierContext() = default;
60+
VerifierContext(DiagnosticsEngine *Diag) : Diag(Diag) {}
4661
};
4762

4863
DylibVerifier() = default;
4964

5065
DylibVerifier(llvm::MachO::Records &&Dylib, DiagnosticsEngine *Diag,
5166
VerificationMode Mode, bool Demangle)
52-
: Dylib(std::move(Dylib)), Diag(Diag), Mode(Mode), Demangle(Demangle),
53-
Exports(std::make_unique<SymbolSet>()) {}
67+
: Dylib(std::move(Dylib)), Mode(Mode), Demangle(Demangle),
68+
Exports(std::make_unique<SymbolSet>()), Ctx(VerifierContext{Diag}) {}
5469

5570
Result verify(GlobalRecord *R, const FrontendAttrs *FA);
5671
Result verify(ObjCInterfaceRecord *R, const FrontendAttrs *FA);
@@ -66,28 +81,58 @@ class DylibVerifier {
6681
/// Get result of verification.
6782
Result getState() const { return Ctx.FrontendState; }
6883

84+
/// Set different source managers to the same diagnostics engine.
85+
void setSourceManager(SourceManager &SourceMgr) const {
86+
if (!Ctx.Diag)
87+
return;
88+
Ctx.Diag->setSourceManager(&SourceMgr);
89+
}
90+
6991
private:
7092
/// Determine whether to compare declaration to symbol in binary.
7193
bool canVerify();
7294

7395
/// Shared implementation for verifying exported symbols.
7496
Result verifyImpl(Record *R, SymbolContext &SymCtx);
7597

98+
/// Check if declaration is marked as obsolete, they are
99+
// expected to result in a symbol mismatch.
100+
bool shouldIgnoreObsolete(const Record *R, SymbolContext &SymCtx,
101+
const Record *DR);
102+
103+
/// Compare the visibility declarations to the linkage of symbol found in
104+
/// dylib.
105+
Result compareVisibility(const Record *R, SymbolContext &SymCtx,
106+
const Record *DR);
107+
108+
/// An ObjCInterfaceRecord can represent up to three symbols. When verifying,
109+
// account for this granularity.
110+
bool compareObjCInterfaceSymbols(const Record *R, SymbolContext &SymCtx,
111+
const ObjCInterfaceRecord *DR);
112+
113+
/// Validate availability annotations against dylib.
114+
Result compareAvailability(const Record *R, SymbolContext &SymCtx,
115+
const Record *DR);
116+
117+
/// Compare and validate matching symbol flags.
118+
bool compareSymbolFlags(const Record *R, SymbolContext &SymCtx,
119+
const Record *DR);
120+
76121
/// Update result state on each call to `verify`.
77122
void updateState(Result State);
78123

79124
/// Add verified exported symbol.
80125
void addSymbol(const Record *R, SymbolContext &SymCtx,
81126
TargetList &&Targets = {});
82127

128+
/// Find matching dylib slice for target triple that is being parsed.
129+
void assignSlice(const Target &T);
130+
83131
// Symbols in dylib.
84132
llvm::MachO::Records Dylib;
85133

86-
// Engine for reporting violations.
87-
[[maybe_unused]] DiagnosticsEngine *Diag = nullptr;
88-
89134
// Controls what class of violations to report.
90-
[[maybe_unused]] VerificationMode Mode = VerificationMode::Invalid;
135+
VerificationMode Mode = VerificationMode::Invalid;
91136

92137
// Attempt to demangle when reporting violations.
93138
bool Demangle = false;

clang/include/clang/InstallAPI/Frontend.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "clang/Frontend/CompilerInstance.h"
1818
#include "clang/Frontend/FrontendActions.h"
1919
#include "clang/InstallAPI/Context.h"
20+
#include "clang/InstallAPI/DylibVerifier.h"
2021
#include "clang/InstallAPI/Visitor.h"
2122
#include "llvm/ADT/Twine.h"
2223
#include "llvm/Support/MemoryBuffer.h"
@@ -34,6 +35,8 @@ class InstallAPIAction : public ASTFrontendAction {
3435

3536
std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
3637
StringRef InFile) override {
38+
Ctx.Diags->getClient()->BeginSourceFile(CI.getLangOpts());
39+
Ctx.Verifier->setSourceManager(CI.getSourceManager());
3740
return std::make_unique<InstallAPIVisitor>(
3841
CI.getASTContext(), Ctx, CI.getSourceManager(), CI.getPreprocessor());
3942
}

clang/include/clang/InstallAPI/MachO.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ using ObjCInterfaceRecord = llvm::MachO::ObjCInterfaceRecord;
3232
using ObjCCategoryRecord = llvm::MachO::ObjCCategoryRecord;
3333
using ObjCIVarRecord = llvm::MachO::ObjCIVarRecord;
3434
using Records = llvm::MachO::Records;
35+
using RecordsSlice = llvm::MachO::RecordsSlice;
3536
using BinaryAttrs = llvm::MachO::RecordsSlice::BinaryAttrs;
3637
using SymbolSet = llvm::MachO::SymbolSet;
3738
using SimpleSymbol = llvm::MachO::SimpleSymbol;

clang/lib/AST/Availability.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@ AvailabilityInfo AvailabilityInfo::createFromDecl(const Decl *Decl) {
2828
for (const auto *A : RD->specific_attrs<AvailabilityAttr>()) {
2929
if (A->getPlatform()->getName() != PlatformName)
3030
continue;
31-
Availability =
32-
AvailabilityInfo(A->getPlatform()->getName(), A->getIntroduced(),
33-
A->getDeprecated(), A->getObsoleted(), false, false);
31+
Availability = AvailabilityInfo(
32+
A->getPlatform()->getName(), A->getIntroduced(), A->getDeprecated(),
33+
A->getObsoleted(), A->getUnavailable(), false, false);
3434
break;
3535
}
3636

0 commit comments

Comments
 (0)