Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/clang/lib/InstallAPI/DylibVerifier.cpp
35233 views
1
//===- DylibVerifier.cpp ----------------------------------------*- C++--*-===//
2
//
3
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4
// See https://llvm.org/LICENSE.txt for license information.
5
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6
//
7
//===----------------------------------------------------------------------===//
8
9
#include "clang/InstallAPI/DylibVerifier.h"
10
#include "DiagnosticBuilderWrappers.h"
11
#include "clang/InstallAPI/FrontendRecords.h"
12
#include "clang/InstallAPI/InstallAPIDiagnostic.h"
13
#include "llvm/Demangle/Demangle.h"
14
#include "llvm/TextAPI/DylibReader.h"
15
16
using namespace llvm::MachO;
17
18
namespace clang {
19
namespace installapi {
20
21
/// Metadata stored about a mapping of a declaration to a symbol.
22
struct DylibVerifier::SymbolContext {
23
// Name to use for all querying and verification
24
// purposes.
25
std::string SymbolName{""};
26
27
// Kind to map symbol type against record.
28
EncodeKind Kind = EncodeKind::GlobalSymbol;
29
30
// Frontend Attributes tied to the AST.
31
const FrontendAttrs *FA = nullptr;
32
33
// The ObjCInterface symbol type, if applicable.
34
ObjCIFSymbolKind ObjCIFKind = ObjCIFSymbolKind::None;
35
36
// Whether Decl is inlined.
37
bool Inlined = false;
38
};
39
40
struct DylibVerifier::DWARFContext {
41
// Track whether DSYM parsing has already been attempted to avoid re-parsing.
42
bool ParsedDSYM{false};
43
44
// Lookup table for source locations by symbol name.
45
DylibReader::SymbolToSourceLocMap SourceLocs{};
46
};
47
48
static bool isCppMangled(StringRef Name) {
49
// InstallAPI currently only supports itanium manglings.
50
return (Name.starts_with("_Z") || Name.starts_with("__Z") ||
51
Name.starts_with("___Z"));
52
}
53
54
static std::string demangle(StringRef Name) {
55
// InstallAPI currently only supports itanium manglings.
56
if (!isCppMangled(Name))
57
return Name.str();
58
char *Result = llvm::itaniumDemangle(Name);
59
if (!Result)
60
return Name.str();
61
62
std::string Demangled(Result);
63
free(Result);
64
return Demangled;
65
}
66
67
std::string DylibVerifier::getAnnotatedName(const Record *R,
68
SymbolContext &SymCtx,
69
bool ValidSourceLoc) {
70
assert(!SymCtx.SymbolName.empty() && "Expected symbol name");
71
72
const StringRef SymbolName = SymCtx.SymbolName;
73
std::string PrettyName =
74
(Demangle && (SymCtx.Kind == EncodeKind::GlobalSymbol))
75
? demangle(SymbolName)
76
: SymbolName.str();
77
78
std::string Annotation;
79
if (R->isWeakDefined())
80
Annotation += "(weak-def) ";
81
if (R->isWeakReferenced())
82
Annotation += "(weak-ref) ";
83
if (R->isThreadLocalValue())
84
Annotation += "(tlv) ";
85
86
// Check if symbol represents only part of a @interface declaration.
87
switch (SymCtx.ObjCIFKind) {
88
default:
89
break;
90
case ObjCIFSymbolKind::EHType:
91
return Annotation + "Exception Type of " + PrettyName;
92
case ObjCIFSymbolKind::MetaClass:
93
return Annotation + "Metaclass of " + PrettyName;
94
case ObjCIFSymbolKind::Class:
95
return Annotation + "Class of " + PrettyName;
96
}
97
98
// Only print symbol type prefix or leading "_" if there is no source location
99
// tied to it. This can only ever happen when the location has to come from
100
// debug info.
101
if (ValidSourceLoc) {
102
StringRef PrettyNameRef(PrettyName);
103
if ((SymCtx.Kind == EncodeKind::GlobalSymbol) &&
104
!isCppMangled(SymbolName) && PrettyNameRef.starts_with("_"))
105
return Annotation + PrettyNameRef.drop_front(1).str();
106
return Annotation + PrettyName;
107
}
108
109
switch (SymCtx.Kind) {
110
case EncodeKind::GlobalSymbol:
111
return Annotation + PrettyName;
112
case EncodeKind::ObjectiveCInstanceVariable:
113
return Annotation + "(ObjC IVar) " + PrettyName;
114
case EncodeKind::ObjectiveCClass:
115
return Annotation + "(ObjC Class) " + PrettyName;
116
case EncodeKind::ObjectiveCClassEHType:
117
return Annotation + "(ObjC Class EH) " + PrettyName;
118
}
119
120
llvm_unreachable("unexpected case for EncodeKind");
121
}
122
123
static DylibVerifier::Result updateResult(const DylibVerifier::Result Prev,
124
const DylibVerifier::Result Curr) {
125
if (Prev == Curr)
126
return Prev;
127
128
// Never update from invalid or noverify state.
129
if ((Prev == DylibVerifier::Result::Invalid) ||
130
(Prev == DylibVerifier::Result::NoVerify))
131
return Prev;
132
133
// Don't let an ignored verification remove a valid one.
134
if (Prev == DylibVerifier::Result::Valid &&
135
Curr == DylibVerifier::Result::Ignore)
136
return Prev;
137
138
return Curr;
139
}
140
// __private_extern__ is a deprecated specifier that clang does not
141
// respect in all contexts, it should just be considered hidden for InstallAPI.
142
static bool shouldIgnorePrivateExternAttr(const Decl *D) {
143
if (const FunctionDecl *FD = cast<FunctionDecl>(D))
144
return FD->getStorageClass() == StorageClass::SC_PrivateExtern;
145
if (const VarDecl *VD = cast<VarDecl>(D))
146
return VD->getStorageClass() == StorageClass::SC_PrivateExtern;
147
148
return false;
149
}
150
151
Record *findRecordFromSlice(const RecordsSlice *Slice, StringRef Name,
152
EncodeKind Kind) {
153
switch (Kind) {
154
case EncodeKind::GlobalSymbol:
155
return Slice->findGlobal(Name);
156
case EncodeKind::ObjectiveCInstanceVariable:
157
return Slice->findObjCIVar(Name.contains('.'), Name);
158
case EncodeKind::ObjectiveCClass:
159
case EncodeKind::ObjectiveCClassEHType:
160
return Slice->findObjCInterface(Name);
161
}
162
llvm_unreachable("unexpected end when finding record");
163
}
164
165
void DylibVerifier::updateState(Result State) {
166
Ctx.FrontendState = updateResult(Ctx.FrontendState, State);
167
}
168
169
void DylibVerifier::addSymbol(const Record *R, SymbolContext &SymCtx,
170
TargetList &&Targets) {
171
if (Targets.empty())
172
Targets = {Ctx.Target};
173
174
Exports->addGlobal(SymCtx.Kind, SymCtx.SymbolName, R->getFlags(), Targets);
175
}
176
177
bool DylibVerifier::shouldIgnoreObsolete(const Record *R, SymbolContext &SymCtx,
178
const Record *DR) {
179
if (!SymCtx.FA->Avail.isObsoleted())
180
return false;
181
182
if (Zippered)
183
DeferredZipperedSymbols[SymCtx.SymbolName].emplace_back(ZipperedDeclSource{
184
SymCtx.FA, &Ctx.Diag->getSourceManager(), Ctx.Target});
185
return true;
186
}
187
188
bool DylibVerifier::shouldIgnoreReexport(const Record *R,
189
SymbolContext &SymCtx) const {
190
StringRef SymName = SymCtx.SymbolName;
191
// Linker directive symbols can never be ignored.
192
if (SymName.starts_with("$ld$"))
193
return false;
194
195
if (Reexports.empty())
196
return false;
197
198
for (const InterfaceFile &Lib : Reexports) {
199
if (!Lib.hasTarget(Ctx.Target))
200
continue;
201
if (auto Sym = Lib.getSymbol(SymCtx.Kind, SymName, SymCtx.ObjCIFKind))
202
if ((*Sym)->hasTarget(Ctx.Target))
203
return true;
204
}
205
return false;
206
}
207
208
bool DylibVerifier::shouldIgnoreInternalZipperedSymbol(
209
const Record *R, const SymbolContext &SymCtx) const {
210
if (!Zippered)
211
return false;
212
213
return Exports->findSymbol(SymCtx.Kind, SymCtx.SymbolName,
214
SymCtx.ObjCIFKind) != nullptr;
215
}
216
217
bool DylibVerifier::shouldIgnoreZipperedAvailability(const Record *R,
218
SymbolContext &SymCtx) {
219
if (!(Zippered && SymCtx.FA->Avail.isUnavailable()))
220
return false;
221
222
// Collect source location incase there is an exported symbol to diagnose
223
// during `verifyRemainingSymbols`.
224
DeferredZipperedSymbols[SymCtx.SymbolName].emplace_back(
225
ZipperedDeclSource{SymCtx.FA, SourceManagers.back().get(), Ctx.Target});
226
227
return true;
228
}
229
230
bool DylibVerifier::compareObjCInterfaceSymbols(const Record *R,
231
SymbolContext &SymCtx,
232
const ObjCInterfaceRecord *DR) {
233
const bool IsDeclVersionComplete =
234
((SymCtx.ObjCIFKind & ObjCIFSymbolKind::Class) ==
235
ObjCIFSymbolKind::Class) &&
236
((SymCtx.ObjCIFKind & ObjCIFSymbolKind::MetaClass) ==
237
ObjCIFSymbolKind::MetaClass);
238
239
const bool IsDylibVersionComplete = DR->isCompleteInterface();
240
241
// The common case, a complete ObjCInterface.
242
if (IsDeclVersionComplete && IsDylibVersionComplete)
243
return true;
244
245
auto PrintDiagnostic = [&](auto SymLinkage, const Record *Record,
246
StringRef SymName, bool PrintAsWarning = false) {
247
if (SymLinkage == RecordLinkage::Unknown)
248
Ctx.emitDiag([&]() {
249
Ctx.Diag->Report(SymCtx.FA->Loc, PrintAsWarning
250
? diag::warn_library_missing_symbol
251
: diag::err_library_missing_symbol)
252
<< SymName;
253
});
254
else
255
Ctx.emitDiag([&]() {
256
Ctx.Diag->Report(SymCtx.FA->Loc, PrintAsWarning
257
? diag::warn_library_hidden_symbol
258
: diag::err_library_hidden_symbol)
259
<< SymName;
260
});
261
};
262
263
if (IsDeclVersionComplete) {
264
// The decl represents a complete ObjCInterface, but the symbols in the
265
// dylib do not. Determine which symbol is missing. To keep older projects
266
// building, treat this as a warning.
267
if (!DR->isExportedSymbol(ObjCIFSymbolKind::Class)) {
268
SymCtx.ObjCIFKind = ObjCIFSymbolKind::Class;
269
PrintDiagnostic(DR->getLinkageForSymbol(ObjCIFSymbolKind::Class), R,
270
getAnnotatedName(R, SymCtx),
271
/*PrintAsWarning=*/true);
272
}
273
if (!DR->isExportedSymbol(ObjCIFSymbolKind::MetaClass)) {
274
SymCtx.ObjCIFKind = ObjCIFSymbolKind::MetaClass;
275
PrintDiagnostic(DR->getLinkageForSymbol(ObjCIFSymbolKind::MetaClass), R,
276
getAnnotatedName(R, SymCtx),
277
/*PrintAsWarning=*/true);
278
}
279
return true;
280
}
281
282
if (DR->isExportedSymbol(SymCtx.ObjCIFKind)) {
283
if (!IsDylibVersionComplete) {
284
// Both the declaration and dylib have a non-complete interface.
285
SymCtx.Kind = EncodeKind::GlobalSymbol;
286
SymCtx.SymbolName = R->getName();
287
}
288
return true;
289
}
290
291
// At this point that means there was not a matching class symbol
292
// to represent the one discovered as a declaration.
293
PrintDiagnostic(DR->getLinkageForSymbol(SymCtx.ObjCIFKind), R,
294
SymCtx.SymbolName);
295
return false;
296
}
297
298
DylibVerifier::Result DylibVerifier::compareVisibility(const Record *R,
299
SymbolContext &SymCtx,
300
const Record *DR) {
301
302
if (R->isExported()) {
303
if (!DR) {
304
Ctx.emitDiag([&]() {
305
Ctx.Diag->Report(SymCtx.FA->Loc, diag::err_library_missing_symbol)
306
<< getAnnotatedName(R, SymCtx);
307
});
308
return Result::Invalid;
309
}
310
if (DR->isInternal()) {
311
Ctx.emitDiag([&]() {
312
Ctx.Diag->Report(SymCtx.FA->Loc, diag::err_library_hidden_symbol)
313
<< getAnnotatedName(R, SymCtx);
314
});
315
return Result::Invalid;
316
}
317
}
318
319
// Emit a diagnostic for hidden declarations with external symbols, except
320
// when theres an inlined attribute.
321
if ((R->isInternal() && !SymCtx.Inlined) && DR && DR->isExported()) {
322
323
if (Mode == VerificationMode::ErrorsOnly)
324
return Result::Ignore;
325
326
if (shouldIgnorePrivateExternAttr(SymCtx.FA->D))
327
return Result::Ignore;
328
329
if (shouldIgnoreInternalZipperedSymbol(R, SymCtx))
330
return Result::Ignore;
331
332
unsigned ID;
333
Result Outcome;
334
if (Mode == VerificationMode::ErrorsAndWarnings) {
335
ID = diag::warn_header_hidden_symbol;
336
Outcome = Result::Ignore;
337
} else {
338
ID = diag::err_header_hidden_symbol;
339
Outcome = Result::Invalid;
340
}
341
Ctx.emitDiag([&]() {
342
Ctx.Diag->Report(SymCtx.FA->Loc, ID) << getAnnotatedName(R, SymCtx);
343
});
344
return Outcome;
345
}
346
347
if (R->isInternal())
348
return Result::Ignore;
349
350
return Result::Valid;
351
}
352
353
DylibVerifier::Result DylibVerifier::compareAvailability(const Record *R,
354
SymbolContext &SymCtx,
355
const Record *DR) {
356
if (!SymCtx.FA->Avail.isUnavailable())
357
return Result::Valid;
358
359
if (shouldIgnoreZipperedAvailability(R, SymCtx))
360
return Result::Ignore;
361
362
const bool IsDeclAvailable = SymCtx.FA->Avail.isUnavailable();
363
364
switch (Mode) {
365
case VerificationMode::ErrorsAndWarnings:
366
Ctx.emitDiag([&]() {
367
Ctx.Diag->Report(SymCtx.FA->Loc, diag::warn_header_availability_mismatch)
368
<< getAnnotatedName(R, SymCtx) << IsDeclAvailable << IsDeclAvailable;
369
});
370
return Result::Ignore;
371
case VerificationMode::Pedantic:
372
Ctx.emitDiag([&]() {
373
Ctx.Diag->Report(SymCtx.FA->Loc, diag::err_header_availability_mismatch)
374
<< getAnnotatedName(R, SymCtx) << IsDeclAvailable << IsDeclAvailable;
375
});
376
return Result::Invalid;
377
case VerificationMode::ErrorsOnly:
378
return Result::Ignore;
379
case VerificationMode::Invalid:
380
llvm_unreachable("Unexpected verification mode symbol verification");
381
}
382
llvm_unreachable("Unexpected verification mode symbol verification");
383
}
384
385
bool DylibVerifier::compareSymbolFlags(const Record *R, SymbolContext &SymCtx,
386
const Record *DR) {
387
if (DR->isThreadLocalValue() && !R->isThreadLocalValue()) {
388
Ctx.emitDiag([&]() {
389
Ctx.Diag->Report(SymCtx.FA->Loc, diag::err_dylib_symbol_flags_mismatch)
390
<< getAnnotatedName(DR, SymCtx) << DR->isThreadLocalValue();
391
});
392
return false;
393
}
394
if (!DR->isThreadLocalValue() && R->isThreadLocalValue()) {
395
Ctx.emitDiag([&]() {
396
Ctx.Diag->Report(SymCtx.FA->Loc, diag::err_header_symbol_flags_mismatch)
397
<< getAnnotatedName(R, SymCtx) << R->isThreadLocalValue();
398
});
399
return false;
400
}
401
402
if (DR->isWeakDefined() && !R->isWeakDefined()) {
403
Ctx.emitDiag([&]() {
404
Ctx.Diag->Report(SymCtx.FA->Loc, diag::err_dylib_symbol_flags_mismatch)
405
<< getAnnotatedName(DR, SymCtx) << R->isWeakDefined();
406
});
407
return false;
408
}
409
if (!DR->isWeakDefined() && R->isWeakDefined()) {
410
Ctx.emitDiag([&]() {
411
Ctx.Diag->Report(SymCtx.FA->Loc, diag::err_header_symbol_flags_mismatch)
412
<< getAnnotatedName(R, SymCtx) << R->isWeakDefined();
413
});
414
return false;
415
}
416
417
return true;
418
}
419
420
DylibVerifier::Result DylibVerifier::verifyImpl(Record *R,
421
SymbolContext &SymCtx) {
422
R->setVerify();
423
if (!canVerify()) {
424
// Accumulate symbols when not in verifying against dylib.
425
if (R->isExported() && !SymCtx.FA->Avail.isUnavailable() &&
426
!SymCtx.FA->Avail.isObsoleted()) {
427
addSymbol(R, SymCtx);
428
}
429
return Ctx.FrontendState;
430
}
431
432
if (shouldIgnoreReexport(R, SymCtx)) {
433
updateState(Result::Ignore);
434
return Ctx.FrontendState;
435
}
436
437
Record *DR =
438
findRecordFromSlice(Ctx.DylibSlice, SymCtx.SymbolName, SymCtx.Kind);
439
if (DR)
440
DR->setVerify();
441
442
if (shouldIgnoreObsolete(R, SymCtx, DR)) {
443
updateState(Result::Ignore);
444
return Ctx.FrontendState;
445
}
446
447
// Unavailable declarations don't need matching symbols.
448
if (SymCtx.FA->Avail.isUnavailable() && (!DR || DR->isInternal())) {
449
updateState(Result::Valid);
450
return Ctx.FrontendState;
451
}
452
453
Result VisibilityCheck = compareVisibility(R, SymCtx, DR);
454
if (VisibilityCheck != Result::Valid) {
455
updateState(VisibilityCheck);
456
return Ctx.FrontendState;
457
}
458
459
// All missing symbol cases to diagnose have been handled now.
460
if (!DR) {
461
updateState(Result::Ignore);
462
return Ctx.FrontendState;
463
}
464
465
// Check for mismatching ObjC interfaces.
466
if (SymCtx.ObjCIFKind != ObjCIFSymbolKind::None) {
467
if (!compareObjCInterfaceSymbols(
468
R, SymCtx, Ctx.DylibSlice->findObjCInterface(DR->getName()))) {
469
updateState(Result::Invalid);
470
return Ctx.FrontendState;
471
}
472
}
473
474
Result AvailabilityCheck = compareAvailability(R, SymCtx, DR);
475
if (AvailabilityCheck != Result::Valid) {
476
updateState(AvailabilityCheck);
477
return Ctx.FrontendState;
478
}
479
480
if (!compareSymbolFlags(R, SymCtx, DR)) {
481
updateState(Result::Invalid);
482
return Ctx.FrontendState;
483
}
484
485
addSymbol(R, SymCtx);
486
updateState(Result::Valid);
487
return Ctx.FrontendState;
488
}
489
490
bool DylibVerifier::canVerify() {
491
return Ctx.FrontendState != Result::NoVerify;
492
}
493
494
void DylibVerifier::assignSlice(const Target &T) {
495
assert(T == Ctx.Target && "Active targets should match.");
496
if (Dylib.empty())
497
return;
498
499
// Note: there are no reexport slices with binaries, as opposed to TBD files,
500
// so it can be assumed that the target match is the active top-level library.
501
auto It = find_if(
502
Dylib, [&T](const auto &Slice) { return T == Slice->getTarget(); });
503
504
assert(It != Dylib.end() && "Target slice should always exist.");
505
Ctx.DylibSlice = It->get();
506
}
507
508
void DylibVerifier::setTarget(const Target &T) {
509
Ctx.Target = T;
510
Ctx.DiscoveredFirstError = false;
511
if (Dylib.empty()) {
512
updateState(Result::NoVerify);
513
return;
514
}
515
updateState(Result::Ignore);
516
assignSlice(T);
517
}
518
519
void DylibVerifier::setSourceManager(
520
IntrusiveRefCntPtr<SourceManager> SourceMgr) {
521
if (!Ctx.Diag)
522
return;
523
SourceManagers.push_back(std::move(SourceMgr));
524
Ctx.Diag->setSourceManager(SourceManagers.back().get());
525
}
526
527
DylibVerifier::Result DylibVerifier::verify(ObjCIVarRecord *R,
528
const FrontendAttrs *FA,
529
const StringRef SuperClass) {
530
if (R->isVerified())
531
return getState();
532
533
std::string FullName =
534
ObjCIVarRecord::createScopedName(SuperClass, R->getName());
535
SymbolContext SymCtx{FullName, EncodeKind::ObjectiveCInstanceVariable, FA};
536
return verifyImpl(R, SymCtx);
537
}
538
539
static ObjCIFSymbolKind assignObjCIFSymbolKind(const ObjCInterfaceRecord *R) {
540
ObjCIFSymbolKind Result = ObjCIFSymbolKind::None;
541
if (R->getLinkageForSymbol(ObjCIFSymbolKind::Class) != RecordLinkage::Unknown)
542
Result |= ObjCIFSymbolKind::Class;
543
if (R->getLinkageForSymbol(ObjCIFSymbolKind::MetaClass) !=
544
RecordLinkage::Unknown)
545
Result |= ObjCIFSymbolKind::MetaClass;
546
if (R->getLinkageForSymbol(ObjCIFSymbolKind::EHType) !=
547
RecordLinkage::Unknown)
548
Result |= ObjCIFSymbolKind::EHType;
549
return Result;
550
}
551
552
DylibVerifier::Result DylibVerifier::verify(ObjCInterfaceRecord *R,
553
const FrontendAttrs *FA) {
554
if (R->isVerified())
555
return getState();
556
SymbolContext SymCtx;
557
SymCtx.SymbolName = R->getName();
558
SymCtx.ObjCIFKind = assignObjCIFSymbolKind(R);
559
560
SymCtx.Kind = R->hasExceptionAttribute() ? EncodeKind::ObjectiveCClassEHType
561
: EncodeKind::ObjectiveCClass;
562
SymCtx.FA = FA;
563
564
return verifyImpl(R, SymCtx);
565
}
566
567
DylibVerifier::Result DylibVerifier::verify(GlobalRecord *R,
568
const FrontendAttrs *FA) {
569
if (R->isVerified())
570
return getState();
571
572
// Global classifications could be obfusciated with `asm`.
573
SimpleSymbol Sym = parseSymbol(R->getName());
574
SymbolContext SymCtx;
575
SymCtx.SymbolName = Sym.Name;
576
SymCtx.Kind = Sym.Kind;
577
SymCtx.FA = FA;
578
SymCtx.Inlined = R->isInlined();
579
return verifyImpl(R, SymCtx);
580
}
581
582
void DylibVerifier::VerifierContext::emitDiag(llvm::function_ref<void()> Report,
583
RecordLoc *Loc) {
584
if (!DiscoveredFirstError) {
585
Diag->Report(diag::warn_target)
586
<< (PrintArch ? getArchitectureName(Target.Arch)
587
: getTargetTripleName(Target));
588
DiscoveredFirstError = true;
589
}
590
if (Loc && Loc->isValid())
591
llvm::errs() << Loc->File << ":" << Loc->Line << ":" << 0 << ": ";
592
593
Report();
594
}
595
596
// The existence of weak-defined RTTI can not always be inferred from the
597
// header files because they can be generated as part of an implementation
598
// file.
599
// InstallAPI doesn't warn about weak-defined RTTI, because this doesn't affect
600
// static linking and so can be ignored for text-api files.
601
static bool shouldIgnoreCpp(StringRef Name, bool IsWeakDef) {
602
return (IsWeakDef &&
603
(Name.starts_with("__ZTI") || Name.starts_with("__ZTS")));
604
}
605
void DylibVerifier::visitSymbolInDylib(const Record &R, SymbolContext &SymCtx) {
606
// Undefined symbols should not be in InstallAPI generated text-api files.
607
if (R.isUndefined()) {
608
updateState(Result::Valid);
609
return;
610
}
611
612
// Internal symbols should not be in InstallAPI generated text-api files.
613
if (R.isInternal()) {
614
updateState(Result::Valid);
615
return;
616
}
617
618
// Allow zippered symbols with potentially mismatching availability
619
// between macOS and macCatalyst in the final text-api file.
620
const StringRef SymbolName(SymCtx.SymbolName);
621
if (const Symbol *Sym = Exports->findSymbol(SymCtx.Kind, SymCtx.SymbolName,
622
SymCtx.ObjCIFKind)) {
623
if (Sym->hasArchitecture(Ctx.Target.Arch)) {
624
updateState(Result::Ignore);
625
return;
626
}
627
}
628
629
const bool IsLinkerSymbol = SymbolName.starts_with("$ld$");
630
631
if (R.isVerified()) {
632
// Check for unavailable symbols.
633
// This should only occur in the zippered case where we ignored
634
// availability until all headers have been parsed.
635
auto It = DeferredZipperedSymbols.find(SymCtx.SymbolName);
636
if (It == DeferredZipperedSymbols.end()) {
637
updateState(Result::Valid);
638
return;
639
}
640
641
ZipperedDeclSources Locs;
642
for (const ZipperedDeclSource &ZSource : It->second) {
643
if (ZSource.FA->Avail.isObsoleted()) {
644
updateState(Result::Ignore);
645
return;
646
}
647
if (ZSource.T.Arch != Ctx.Target.Arch)
648
continue;
649
Locs.emplace_back(ZSource);
650
}
651
assert(Locs.size() == 2 && "Expected two decls for zippered symbol");
652
653
// Print violating declarations per platform.
654
for (const ZipperedDeclSource &ZSource : Locs) {
655
unsigned DiagID = 0;
656
if (Mode == VerificationMode::Pedantic || IsLinkerSymbol) {
657
updateState(Result::Invalid);
658
DiagID = diag::err_header_availability_mismatch;
659
} else if (Mode == VerificationMode::ErrorsAndWarnings) {
660
updateState(Result::Ignore);
661
DiagID = diag::warn_header_availability_mismatch;
662
} else {
663
updateState(Result::Ignore);
664
return;
665
}
666
// Bypass emitDiag banner and print the target everytime.
667
Ctx.Diag->setSourceManager(ZSource.SrcMgr);
668
Ctx.Diag->Report(diag::warn_target) << getTargetTripleName(ZSource.T);
669
Ctx.Diag->Report(ZSource.FA->Loc, DiagID)
670
<< getAnnotatedName(&R, SymCtx) << ZSource.FA->Avail.isUnavailable()
671
<< ZSource.FA->Avail.isUnavailable();
672
}
673
return;
674
}
675
676
if (shouldIgnoreCpp(SymbolName, R.isWeakDefined())) {
677
updateState(Result::Valid);
678
return;
679
}
680
681
if (Aliases.count({SymbolName.str(), SymCtx.Kind})) {
682
updateState(Result::Valid);
683
return;
684
}
685
686
// All checks at this point classify as some kind of violation.
687
// The different verification modes dictate whether they are reported to the
688
// user.
689
if (IsLinkerSymbol || (Mode > VerificationMode::ErrorsOnly))
690
accumulateSrcLocForDylibSymbols();
691
RecordLoc Loc = DWARFCtx->SourceLocs.lookup(SymCtx.SymbolName);
692
693
// Regardless of verification mode, error out on mismatched special linker
694
// symbols.
695
if (IsLinkerSymbol) {
696
Ctx.emitDiag(
697
[&]() {
698
Ctx.Diag->Report(diag::err_header_symbol_missing)
699
<< getAnnotatedName(&R, SymCtx, Loc.isValid());
700
},
701
&Loc);
702
updateState(Result::Invalid);
703
return;
704
}
705
706
// Missing declarations for exported symbols are hard errors on Pedantic mode.
707
if (Mode == VerificationMode::Pedantic) {
708
Ctx.emitDiag(
709
[&]() {
710
Ctx.Diag->Report(diag::err_header_symbol_missing)
711
<< getAnnotatedName(&R, SymCtx, Loc.isValid());
712
},
713
&Loc);
714
updateState(Result::Invalid);
715
return;
716
}
717
718
// Missing declarations for exported symbols are warnings on ErrorsAndWarnings
719
// mode.
720
if (Mode == VerificationMode::ErrorsAndWarnings) {
721
Ctx.emitDiag(
722
[&]() {
723
Ctx.Diag->Report(diag::warn_header_symbol_missing)
724
<< getAnnotatedName(&R, SymCtx, Loc.isValid());
725
},
726
&Loc);
727
updateState(Result::Ignore);
728
return;
729
}
730
731
// Missing declarations are dropped for ErrorsOnly mode. It is the last
732
// remaining mode.
733
updateState(Result::Ignore);
734
return;
735
}
736
737
void DylibVerifier::visitGlobal(const GlobalRecord &R) {
738
SymbolContext SymCtx;
739
SimpleSymbol Sym = parseSymbol(R.getName());
740
SymCtx.SymbolName = Sym.Name;
741
SymCtx.Kind = Sym.Kind;
742
visitSymbolInDylib(R, SymCtx);
743
}
744
745
void DylibVerifier::visitObjCIVar(const ObjCIVarRecord &R,
746
const StringRef Super) {
747
SymbolContext SymCtx;
748
SymCtx.SymbolName = ObjCIVarRecord::createScopedName(Super, R.getName());
749
SymCtx.Kind = EncodeKind::ObjectiveCInstanceVariable;
750
visitSymbolInDylib(R, SymCtx);
751
}
752
753
void DylibVerifier::accumulateSrcLocForDylibSymbols() {
754
if (DSYMPath.empty())
755
return;
756
757
assert(DWARFCtx != nullptr && "Expected an initialized DWARFContext");
758
if (DWARFCtx->ParsedDSYM)
759
return;
760
DWARFCtx->ParsedDSYM = true;
761
DWARFCtx->SourceLocs =
762
DylibReader::accumulateSourceLocFromDSYM(DSYMPath, Ctx.Target);
763
}
764
765
void DylibVerifier::visitObjCInterface(const ObjCInterfaceRecord &R) {
766
SymbolContext SymCtx;
767
SymCtx.SymbolName = R.getName();
768
SymCtx.ObjCIFKind = assignObjCIFSymbolKind(&R);
769
if (SymCtx.ObjCIFKind > ObjCIFSymbolKind::EHType) {
770
if (R.hasExceptionAttribute()) {
771
SymCtx.Kind = EncodeKind::ObjectiveCClassEHType;
772
visitSymbolInDylib(R, SymCtx);
773
}
774
SymCtx.Kind = EncodeKind::ObjectiveCClass;
775
visitSymbolInDylib(R, SymCtx);
776
} else {
777
SymCtx.Kind = R.hasExceptionAttribute() ? EncodeKind::ObjectiveCClassEHType
778
: EncodeKind::ObjectiveCClass;
779
visitSymbolInDylib(R, SymCtx);
780
}
781
782
for (const ObjCIVarRecord *IV : R.getObjCIVars())
783
visitObjCIVar(*IV, R.getName());
784
}
785
786
void DylibVerifier::visitObjCCategory(const ObjCCategoryRecord &R) {
787
for (const ObjCIVarRecord *IV : R.getObjCIVars())
788
visitObjCIVar(*IV, R.getSuperClassName());
789
}
790
791
DylibVerifier::Result DylibVerifier::verifyRemainingSymbols() {
792
if (getState() == Result::NoVerify)
793
return Result::NoVerify;
794
assert(!Dylib.empty() && "No binary to verify against");
795
796
DWARFContext DWARFInfo;
797
DWARFCtx = &DWARFInfo;
798
Ctx.Target = Target(Architecture::AK_unknown, PlatformType::PLATFORM_UNKNOWN);
799
for (std::shared_ptr<RecordsSlice> Slice : Dylib) {
800
if (Ctx.Target.Arch == Slice->getTarget().Arch)
801
continue;
802
Ctx.DiscoveredFirstError = false;
803
Ctx.PrintArch = true;
804
Ctx.Target = Slice->getTarget();
805
Ctx.DylibSlice = Slice.get();
806
Slice->visit(*this);
807
}
808
return getState();
809
}
810
811
bool DylibVerifier::verifyBinaryAttrs(const ArrayRef<Target> ProvidedTargets,
812
const BinaryAttrs &ProvidedBA,
813
const LibAttrs &ProvidedReexports,
814
const LibAttrs &ProvidedClients,
815
const LibAttrs &ProvidedRPaths,
816
const FileType &FT) {
817
assert(!Dylib.empty() && "Need dylib to verify.");
818
819
// Pickup any load commands that can differ per slice to compare.
820
TargetList DylibTargets;
821
LibAttrs DylibReexports;
822
LibAttrs DylibClients;
823
LibAttrs DylibRPaths;
824
for (const std::shared_ptr<RecordsSlice> &RS : Dylib) {
825
DylibTargets.push_back(RS->getTarget());
826
const BinaryAttrs &BinInfo = RS->getBinaryAttrs();
827
for (const StringRef LibName : BinInfo.RexportedLibraries)
828
DylibReexports[LibName].set(DylibTargets.back().Arch);
829
for (const StringRef LibName : BinInfo.AllowableClients)
830
DylibClients[LibName].set(DylibTargets.back().Arch);
831
// Compare attributes that are only representable in >= TBD_V5.
832
if (FT >= FileType::TBD_V5)
833
for (const StringRef Name : BinInfo.RPaths)
834
DylibRPaths[Name].set(DylibTargets.back().Arch);
835
}
836
837
// Check targets first.
838
ArchitectureSet ProvidedArchs = mapToArchitectureSet(ProvidedTargets);
839
ArchitectureSet DylibArchs = mapToArchitectureSet(DylibTargets);
840
if (ProvidedArchs != DylibArchs) {
841
Ctx.Diag->Report(diag::err_architecture_mismatch)
842
<< ProvidedArchs << DylibArchs;
843
return false;
844
}
845
auto ProvidedPlatforms = mapToPlatformVersionSet(ProvidedTargets);
846
auto DylibPlatforms = mapToPlatformVersionSet(DylibTargets);
847
if (ProvidedPlatforms != DylibPlatforms) {
848
const bool DiffMinOS =
849
mapToPlatformSet(ProvidedTargets) == mapToPlatformSet(DylibTargets);
850
if (DiffMinOS)
851
Ctx.Diag->Report(diag::warn_platform_mismatch)
852
<< ProvidedPlatforms << DylibPlatforms;
853
else {
854
Ctx.Diag->Report(diag::err_platform_mismatch)
855
<< ProvidedPlatforms << DylibPlatforms;
856
return false;
857
}
858
}
859
860
// Because InstallAPI requires certain attributes to match across architecture
861
// slices, take the first one to compare those with.
862
const BinaryAttrs &DylibBA = (*Dylib.begin())->getBinaryAttrs();
863
864
if (ProvidedBA.InstallName != DylibBA.InstallName) {
865
Ctx.Diag->Report(diag::err_install_name_mismatch)
866
<< ProvidedBA.InstallName << DylibBA.InstallName;
867
return false;
868
}
869
870
if (ProvidedBA.CurrentVersion != DylibBA.CurrentVersion) {
871
Ctx.Diag->Report(diag::err_current_version_mismatch)
872
<< ProvidedBA.CurrentVersion << DylibBA.CurrentVersion;
873
return false;
874
}
875
876
if (ProvidedBA.CompatVersion != DylibBA.CompatVersion) {
877
Ctx.Diag->Report(diag::err_compatibility_version_mismatch)
878
<< ProvidedBA.CompatVersion << DylibBA.CompatVersion;
879
return false;
880
}
881
882
if (ProvidedBA.AppExtensionSafe != DylibBA.AppExtensionSafe) {
883
Ctx.Diag->Report(diag::err_appextension_safe_mismatch)
884
<< (ProvidedBA.AppExtensionSafe ? "true" : "false")
885
<< (DylibBA.AppExtensionSafe ? "true" : "false");
886
return false;
887
}
888
889
if (!DylibBA.TwoLevelNamespace) {
890
Ctx.Diag->Report(diag::err_no_twolevel_namespace);
891
return false;
892
}
893
894
if (ProvidedBA.OSLibNotForSharedCache != DylibBA.OSLibNotForSharedCache) {
895
Ctx.Diag->Report(diag::err_shared_cache_eligiblity_mismatch)
896
<< (ProvidedBA.OSLibNotForSharedCache ? "true" : "false")
897
<< (DylibBA.OSLibNotForSharedCache ? "true" : "false");
898
return false;
899
}
900
901
if (ProvidedBA.ParentUmbrella.empty() && !DylibBA.ParentUmbrella.empty()) {
902
Ctx.Diag->Report(diag::err_parent_umbrella_missing)
903
<< "installAPI option" << DylibBA.ParentUmbrella;
904
return false;
905
}
906
907
if (!ProvidedBA.ParentUmbrella.empty() && DylibBA.ParentUmbrella.empty()) {
908
Ctx.Diag->Report(diag::err_parent_umbrella_missing)
909
<< "binary file" << ProvidedBA.ParentUmbrella;
910
return false;
911
}
912
913
if ((!ProvidedBA.ParentUmbrella.empty()) &&
914
(ProvidedBA.ParentUmbrella != DylibBA.ParentUmbrella)) {
915
Ctx.Diag->Report(diag::err_parent_umbrella_mismatch)
916
<< ProvidedBA.ParentUmbrella << DylibBA.ParentUmbrella;
917
return false;
918
}
919
920
auto CompareLibraries = [&](const LibAttrs &Provided, const LibAttrs &Dylib,
921
unsigned DiagID_missing, unsigned DiagID_mismatch,
922
bool Fatal = true) {
923
if (Provided == Dylib)
924
return true;
925
926
for (const llvm::StringMapEntry<ArchitectureSet> &PAttr : Provided) {
927
const auto DAttrIt = Dylib.find(PAttr.getKey());
928
if (DAttrIt == Dylib.end()) {
929
Ctx.Diag->Report(DiagID_missing) << "binary file" << PAttr;
930
if (Fatal)
931
return false;
932
}
933
934
if (PAttr.getValue() != DAttrIt->getValue()) {
935
Ctx.Diag->Report(DiagID_mismatch) << PAttr << *DAttrIt;
936
if (Fatal)
937
return false;
938
}
939
}
940
941
for (const llvm::StringMapEntry<ArchitectureSet> &DAttr : Dylib) {
942
const auto PAttrIt = Provided.find(DAttr.getKey());
943
if (PAttrIt == Provided.end()) {
944
Ctx.Diag->Report(DiagID_missing) << "installAPI option" << DAttr;
945
if (!Fatal)
946
continue;
947
return false;
948
}
949
950
if (PAttrIt->getValue() != DAttr.getValue()) {
951
if (Fatal)
952
llvm_unreachable("this case was already covered above.");
953
}
954
}
955
return true;
956
};
957
958
if (!CompareLibraries(ProvidedReexports, DylibReexports,
959
diag::err_reexported_libraries_missing,
960
diag::err_reexported_libraries_mismatch))
961
return false;
962
963
if (!CompareLibraries(ProvidedClients, DylibClients,
964
diag::err_allowable_clients_missing,
965
diag::err_allowable_clients_mismatch))
966
return false;
967
968
if (FT >= FileType::TBD_V5) {
969
// Ignore rpath differences if building an asan variant, since the
970
// compiler injects additional paths.
971
// FIXME: Building with sanitizers does not always change the install
972
// name, so this is not a foolproof solution.
973
if (!ProvidedBA.InstallName.ends_with("_asan")) {
974
if (!CompareLibraries(ProvidedRPaths, DylibRPaths,
975
diag::warn_rpaths_missing,
976
diag::warn_rpaths_mismatch,
977
/*Fatal=*/false))
978
return true;
979
}
980
}
981
982
return true;
983
}
984
985
std::unique_ptr<SymbolSet> DylibVerifier::takeExports() {
986
for (const auto &[Alias, Base] : Aliases) {
987
TargetList Targets;
988
SymbolFlags Flags = SymbolFlags::None;
989
if (const Symbol *Sym = Exports->findSymbol(Base.second, Base.first)) {
990
Flags = Sym->getFlags();
991
Targets = {Sym->targets().begin(), Sym->targets().end()};
992
}
993
994
Record R(Alias.first, RecordLinkage::Exported, Flags);
995
SymbolContext SymCtx;
996
SymCtx.SymbolName = Alias.first;
997
SymCtx.Kind = Alias.second;
998
addSymbol(&R, SymCtx, std::move(Targets));
999
}
1000
1001
return std::move(Exports);
1002
}
1003
1004
} // namespace installapi
1005
} // namespace clang
1006
1007