clang 20.0.0git
DylibVerifier.cpp
Go to the documentation of this file.
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
13#include "llvm/Demangle/Demangle.h"
14#include "llvm/TextAPI/DylibReader.h"
15
16using namespace llvm::MachO;
17
18namespace clang {
19namespace installapi {
20
21/// Metadata stored about a mapping of a declaration to a symbol.
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
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
48static 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
54static 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
67std::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
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) ||
131 return Prev;
132
133 // Don't let an ignored verification remove a valid one.
134 if (Prev == DylibVerifier::Result::Valid &&
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.
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
151Record *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
165void DylibVerifier::updateState(Result State) {
166 Ctx.FrontendState = updateResult(Ctx.FrontendState, State);
167}
168
169void 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
177bool 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
188bool 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
208bool 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
217bool 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
230bool 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
298DylibVerifier::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
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;
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
353DylibVerifier::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) {
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;
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;
378 return Result::Ignore;
380 llvm_unreachable("Unexpected verification mode symbol verification");
381 }
382 llvm_unreachable("Unexpected verification mode symbol verification");
383}
384
385bool 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
420DylibVerifier::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
490bool DylibVerifier::canVerify() {
491 return Ctx.FrontendState != Result::NoVerify;
492}
493
494void 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
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
521 if (!Ctx.Diag)
522 return;
523 SourceManagers.push_back(std::move(SourceMgr));
524 Ctx.Diag->setSourceManager(SourceManagers.back().get());
525}
526
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
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
553 const FrontendAttrs *FA) {
554 if (R->isVerified())
555 return getState();
556 SymbolContext SymCtx;
557 SymCtx.SymbolName = R->getName();
559
560 SymCtx.Kind = R->hasExceptionAttribute() ? EncodeKind::ObjectiveCClassEHType
561 : EncodeKind::ObjectiveCClass;
562 SymCtx.FA = FA;
563
564 return verifyImpl(R, SymCtx);
565}
566
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
582void DylibVerifier::VerifierContext::emitDiag(llvm::function_ref<void()> Report,
583 RecordLoc *Loc) {
585 Diag->Report(diag::warn_target)
586 << (PrintArch ? getArchitectureName(Target.Arch)
587 : getTargetTripleName(Target));
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.
601static bool shouldIgnoreCpp(StringRef Name, bool IsWeakDef) {
602 return (IsWeakDef &&
603 (Name.starts_with("__ZTI") || Name.starts_with("__ZTS")));
604}
605void 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
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.
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
737void 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
745void 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
753void 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
765void 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
786void DylibVerifier::visitObjCCategory(const ObjCCategoryRecord &R) {
787 for (const ObjCIVarRecord *IV : R.getObjCIVars())
788 visitObjCIVar(*IV, R.getSuperClassName());
789}
790
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
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
985std::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
const Decl * D
llvm::MachO::ObjCIVarRecord ObjCIVarRecord
Definition: MachO.h:38
llvm::MachO::SymbolFlags SymbolFlags
Definition: MachO.h:29
llvm::MachO::RecordLoc RecordLoc
Definition: MachO.h:41
llvm::MachO::InterfaceFile InterfaceFile
Definition: MachO.h:34
llvm::MachO::ObjCCategoryRecord ObjCCategoryRecord
Definition: MachO.h:37
llvm::MachO::GlobalRecord GlobalRecord
Definition: MachO.h:33
llvm::MachO::ArchitectureSet ArchitectureSet
Definition: MachO.h:28
llvm::MachO::EncodeKind EncodeKind
Definition: MachO.h:32
llvm::MachO::ObjCInterfaceRecord ObjCInterfaceRecord
Definition: MachO.h:36
llvm::MachO::ObjCIFSymbolKind ObjCIFSymbolKind
Definition: MachO.h:39
llvm::MachO::FileType FileType
Definition: MachO.h:46
llvm::MachO::Target Target
Definition: MachO.h:51
llvm::MachO::RecordsSlice::BinaryAttrs BinaryAttrs
Definition: MachO.h:43
llvm::MachO::Record Record
Definition: MachO.h:31
llvm::MachO::TargetList TargetList
Definition: MachO.h:52
llvm::MachO::SimpleSymbol SimpleSymbol
Definition: MachO.h:45
int32_t FullName
Definition: SemaARM.cpp:1144
SourceLocation Loc
Definition: SemaObjC.cpp:758
Decl - This represents one declaration (or definition), e.g.
Definition: DeclBase.h:86
DiagnosticBuilder Report(SourceLocation Loc, unsigned DiagID)
Issue the message to the client.
Definition: Diagnostic.h:1547
void setSourceManager(SourceManager *SrcMgr)
Definition: Diagnostic.h:589
SourceManager & getSourceManager() const
Definition: Diagnostic.h:584
Represents a function declaration or definition.
Definition: Decl.h:1932
Represents a variable declaration or definition.
Definition: Decl.h:879
Result verify(GlobalRecord *R, const FrontendAttrs *FA)
Result getState() const
Get result of verification.
void setSourceManager(IntrusiveRefCntPtr< SourceManager > SourceMgr)
Set different source managers to the same diagnostics engine.
std::unique_ptr< SymbolSet > takeExports()
Release ownership over exports.
bool verifyBinaryAttrs(const ArrayRef< Target > ProvidedTargets, const BinaryAttrs &ProvidedBA, const LibAttrs &ProvidedReexports, const LibAttrs &ProvidedClients, const LibAttrs &ProvidedRPaths, const FileType &FT)
Compare and report the attributes represented as load commands in the dylib to the attributes provide...
void setTarget(const Target &T)
Initialize target for verification.
Record * findRecordFromSlice(const RecordsSlice *Slice, StringRef Name, EncodeKind Kind)
llvm::StringMap< ArchitectureSet > LibAttrs
Definition: DylibVerifier.h:28
static bool isCppMangled(StringRef Name)
static std::string demangle(StringRef Name)
static DylibVerifier::Result updateResult(const DylibVerifier::Result Prev, const DylibVerifier::Result Curr)
static ObjCIFSymbolKind assignObjCIFSymbolKind(const ObjCInterfaceRecord *R)
static bool shouldIgnorePrivateExternAttr(const Decl *D)
std::vector< ZipperedDeclSource > ZipperedDeclSources
Definition: DylibVerifier.h:39
static bool shouldIgnoreCpp(StringRef Name, bool IsWeakDef)
The JSON file list parser is used to communicate input to InstallAPI.
@ SC_PrivateExtern
Definition: Specifiers.h:253
@ Result
The result type of a method or function.
const FunctionProtoType * T
DylibReader::SymbolToSourceLocMap SourceLocs
Metadata stored about a mapping of a declaration to a symbol.
void emitDiag(llvm::function_ref< void()> Report, RecordLoc *Loc=nullptr)
Frontend information captured about records.