clang 19.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.
142static 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
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 if (Reexports.empty())
191 return false;
192
193 for (const InterfaceFile &Lib : Reexports) {
194 if (!Lib.hasTarget(Ctx.Target))
195 continue;
196 if (auto Sym =
197 Lib.getSymbol(SymCtx.Kind, SymCtx.SymbolName, SymCtx.ObjCIFKind))
198 if ((*Sym)->hasTarget(Ctx.Target))
199 return true;
200 }
201 return false;
202}
203
204bool DylibVerifier::shouldIgnoreInternalZipperedSymbol(
205 const Record *R, const SymbolContext &SymCtx) const {
206 if (!Zippered)
207 return false;
208
209 return Exports->findSymbol(SymCtx.Kind, SymCtx.SymbolName,
210 SymCtx.ObjCIFKind) != nullptr;
211}
212
213bool DylibVerifier::shouldIgnoreZipperedAvailability(const Record *R,
214 SymbolContext &SymCtx) {
215 if (!(Zippered && SymCtx.FA->Avail.isUnavailable()))
216 return false;
217
218 // Collect source location incase there is an exported symbol to diagnose
219 // during `verifyRemainingSymbols`.
220 DeferredZipperedSymbols[SymCtx.SymbolName].emplace_back(
221 ZipperedDeclSource{SymCtx.FA, SourceManagers.back().get(), Ctx.Target});
222
223 return true;
224}
225
226bool DylibVerifier::compareObjCInterfaceSymbols(const Record *R,
227 SymbolContext &SymCtx,
228 const ObjCInterfaceRecord *DR) {
229 const bool IsDeclVersionComplete =
230 ((SymCtx.ObjCIFKind & ObjCIFSymbolKind::Class) ==
231 ObjCIFSymbolKind::Class) &&
232 ((SymCtx.ObjCIFKind & ObjCIFSymbolKind::MetaClass) ==
233 ObjCIFSymbolKind::MetaClass);
234
235 const bool IsDylibVersionComplete = DR->isCompleteInterface();
236
237 // The common case, a complete ObjCInterface.
238 if (IsDeclVersionComplete && IsDylibVersionComplete)
239 return true;
240
241 auto PrintDiagnostic = [&](auto SymLinkage, const Record *Record,
242 StringRef SymName, bool PrintAsWarning = false) {
243 if (SymLinkage == RecordLinkage::Unknown)
244 Ctx.emitDiag([&]() {
245 Ctx.Diag->Report(SymCtx.FA->Loc, PrintAsWarning
246 ? diag::warn_library_missing_symbol
247 : diag::err_library_missing_symbol)
248 << SymName;
249 });
250 else
251 Ctx.emitDiag([&]() {
252 Ctx.Diag->Report(SymCtx.FA->Loc, PrintAsWarning
253 ? diag::warn_library_hidden_symbol
254 : diag::err_library_hidden_symbol)
255 << SymName;
256 });
257 };
258
259 if (IsDeclVersionComplete) {
260 // The decl represents a complete ObjCInterface, but the symbols in the
261 // dylib do not. Determine which symbol is missing. To keep older projects
262 // building, treat this as a warning.
263 if (!DR->isExportedSymbol(ObjCIFSymbolKind::Class)) {
264 SymCtx.ObjCIFKind = ObjCIFSymbolKind::Class;
265 PrintDiagnostic(DR->getLinkageForSymbol(ObjCIFSymbolKind::Class), R,
266 getAnnotatedName(R, SymCtx),
267 /*PrintAsWarning=*/true);
268 }
269 if (!DR->isExportedSymbol(ObjCIFSymbolKind::MetaClass)) {
270 SymCtx.ObjCIFKind = ObjCIFSymbolKind::MetaClass;
271 PrintDiagnostic(DR->getLinkageForSymbol(ObjCIFSymbolKind::MetaClass), R,
272 getAnnotatedName(R, SymCtx),
273 /*PrintAsWarning=*/true);
274 }
275 return true;
276 }
277
278 if (DR->isExportedSymbol(SymCtx.ObjCIFKind)) {
279 if (!IsDylibVersionComplete) {
280 // Both the declaration and dylib have a non-complete interface.
281 SymCtx.Kind = EncodeKind::GlobalSymbol;
282 SymCtx.SymbolName = R->getName();
283 }
284 return true;
285 }
286
287 // At this point that means there was not a matching class symbol
288 // to represent the one discovered as a declaration.
289 PrintDiagnostic(DR->getLinkageForSymbol(SymCtx.ObjCIFKind), R,
290 SymCtx.SymbolName);
291 return false;
292}
293
294DylibVerifier::Result DylibVerifier::compareVisibility(const Record *R,
295 SymbolContext &SymCtx,
296 const Record *DR) {
297
298 if (R->isExported()) {
299 if (!DR) {
300 Ctx.emitDiag([&]() {
301 Ctx.Diag->Report(SymCtx.FA->Loc, diag::err_library_missing_symbol)
302 << getAnnotatedName(R, SymCtx);
303 });
304 return Result::Invalid;
305 }
306 if (DR->isInternal()) {
307 Ctx.emitDiag([&]() {
308 Ctx.Diag->Report(SymCtx.FA->Loc, diag::err_library_hidden_symbol)
309 << getAnnotatedName(R, SymCtx);
310 });
311 return Result::Invalid;
312 }
313 }
314
315 // Emit a diagnostic for hidden declarations with external symbols, except
316 // when theres an inlined attribute.
317 if ((R->isInternal() && !SymCtx.Inlined) && DR && DR->isExported()) {
318
320 return Result::Ignore;
321
322 if (shouldIgnorePrivateExternAttr(SymCtx.FA->D))
323 return Result::Ignore;
324
325 if (shouldIgnoreInternalZipperedSymbol(R, SymCtx))
326 return Result::Ignore;
327
328 unsigned ID;
329 Result Outcome;
331 ID = diag::warn_header_hidden_symbol;
332 Outcome = Result::Ignore;
333 } else {
334 ID = diag::err_header_hidden_symbol;
335 Outcome = Result::Invalid;
336 }
337 Ctx.emitDiag([&]() {
338 Ctx.Diag->Report(SymCtx.FA->Loc, ID) << getAnnotatedName(R, SymCtx);
339 });
340 return Outcome;
341 }
342
343 if (R->isInternal())
344 return Result::Ignore;
345
346 return Result::Valid;
347}
348
349DylibVerifier::Result DylibVerifier::compareAvailability(const Record *R,
350 SymbolContext &SymCtx,
351 const Record *DR) {
352 if (!SymCtx.FA->Avail.isUnavailable())
353 return Result::Valid;
354
355 if (shouldIgnoreZipperedAvailability(R, SymCtx))
356 return Result::Ignore;
357
358 const bool IsDeclAvailable = SymCtx.FA->Avail.isUnavailable();
359
360 switch (Mode) {
362 Ctx.emitDiag([&]() {
363 Ctx.Diag->Report(SymCtx.FA->Loc, diag::warn_header_availability_mismatch)
364 << getAnnotatedName(R, SymCtx) << IsDeclAvailable << IsDeclAvailable;
365 });
366 return Result::Ignore;
368 Ctx.emitDiag([&]() {
369 Ctx.Diag->Report(SymCtx.FA->Loc, diag::err_header_availability_mismatch)
370 << getAnnotatedName(R, SymCtx) << IsDeclAvailable << IsDeclAvailable;
371 });
372 return Result::Invalid;
374 return Result::Ignore;
376 llvm_unreachable("Unexpected verification mode symbol verification");
377 }
378 llvm_unreachable("Unexpected verification mode symbol verification");
379}
380
381bool DylibVerifier::compareSymbolFlags(const Record *R, SymbolContext &SymCtx,
382 const Record *DR) {
383 if (DR->isThreadLocalValue() && !R->isThreadLocalValue()) {
384 Ctx.emitDiag([&]() {
385 Ctx.Diag->Report(SymCtx.FA->Loc, diag::err_dylib_symbol_flags_mismatch)
386 << getAnnotatedName(DR, SymCtx) << DR->isThreadLocalValue();
387 });
388 return false;
389 }
390 if (!DR->isThreadLocalValue() && R->isThreadLocalValue()) {
391 Ctx.emitDiag([&]() {
392 Ctx.Diag->Report(SymCtx.FA->Loc, diag::err_header_symbol_flags_mismatch)
393 << getAnnotatedName(R, SymCtx) << R->isThreadLocalValue();
394 });
395 return false;
396 }
397
398 if (DR->isWeakDefined() && !R->isWeakDefined()) {
399 Ctx.emitDiag([&]() {
400 Ctx.Diag->Report(SymCtx.FA->Loc, diag::err_dylib_symbol_flags_mismatch)
401 << getAnnotatedName(DR, SymCtx) << R->isWeakDefined();
402 });
403 return false;
404 }
405 if (!DR->isWeakDefined() && R->isWeakDefined()) {
406 Ctx.emitDiag([&]() {
407 Ctx.Diag->Report(SymCtx.FA->Loc, diag::err_header_symbol_flags_mismatch)
408 << getAnnotatedName(R, SymCtx) << R->isWeakDefined();
409 });
410 return false;
411 }
412
413 return true;
414}
415
416DylibVerifier::Result DylibVerifier::verifyImpl(Record *R,
417 SymbolContext &SymCtx) {
418 R->setVerify();
419 if (!canVerify()) {
420 // Accumulate symbols when not in verifying against dylib.
421 if (R->isExported() && !SymCtx.FA->Avail.isUnavailable() &&
422 !SymCtx.FA->Avail.isObsoleted()) {
423 addSymbol(R, SymCtx);
424 }
425 return Ctx.FrontendState;
426 }
427
428 if (shouldIgnoreReexport(R, SymCtx)) {
429 updateState(Result::Ignore);
430 return Ctx.FrontendState;
431 }
432
433 Record *DR =
434 findRecordFromSlice(Ctx.DylibSlice, SymCtx.SymbolName, SymCtx.Kind);
435 if (DR)
436 DR->setVerify();
437
438 if (shouldIgnoreObsolete(R, SymCtx, DR)) {
439 updateState(Result::Ignore);
440 return Ctx.FrontendState;
441 }
442
443 // Unavailable declarations don't need matching symbols.
444 if (SymCtx.FA->Avail.isUnavailable() && (!DR || DR->isInternal())) {
445 updateState(Result::Valid);
446 return Ctx.FrontendState;
447 }
448
449 Result VisibilityCheck = compareVisibility(R, SymCtx, DR);
450 if (VisibilityCheck != Result::Valid) {
451 updateState(VisibilityCheck);
452 return Ctx.FrontendState;
453 }
454
455 // All missing symbol cases to diagnose have been handled now.
456 if (!DR) {
457 updateState(Result::Ignore);
458 return Ctx.FrontendState;
459 }
460
461 // Check for mismatching ObjC interfaces.
462 if (SymCtx.ObjCIFKind != ObjCIFSymbolKind::None) {
463 if (!compareObjCInterfaceSymbols(
464 R, SymCtx, Ctx.DylibSlice->findObjCInterface(DR->getName()))) {
465 updateState(Result::Invalid);
466 return Ctx.FrontendState;
467 }
468 }
469
470 Result AvailabilityCheck = compareAvailability(R, SymCtx, DR);
471 if (AvailabilityCheck != Result::Valid) {
472 updateState(AvailabilityCheck);
473 return Ctx.FrontendState;
474 }
475
476 if (!compareSymbolFlags(R, SymCtx, DR)) {
477 updateState(Result::Invalid);
478 return Ctx.FrontendState;
479 }
480
481 addSymbol(R, SymCtx);
482 updateState(Result::Valid);
483 return Ctx.FrontendState;
484}
485
486bool DylibVerifier::canVerify() {
487 return Ctx.FrontendState != Result::NoVerify;
488}
489
490void DylibVerifier::assignSlice(const Target &T) {
491 assert(T == Ctx.Target && "Active targets should match.");
492 if (Dylib.empty())
493 return;
494
495 // Note: there are no reexport slices with binaries, as opposed to TBD files,
496 // so it can be assumed that the target match is the active top-level library.
497 auto It = find_if(
498 Dylib, [&T](const auto &Slice) { return T == Slice->getTarget(); });
499
500 assert(It != Dylib.end() && "Target slice should always exist.");
501 Ctx.DylibSlice = It->get();
502}
503
505 Ctx.Target = T;
506 Ctx.DiscoveredFirstError = false;
507 if (Dylib.empty()) {
508 updateState(Result::NoVerify);
509 return;
510 }
511 updateState(Result::Ignore);
512 assignSlice(T);
513}
514
517 if (!Ctx.Diag)
518 return;
519 SourceManagers.push_back(std::move(SourceMgr));
520 Ctx.Diag->setSourceManager(SourceManagers.back().get());
521}
522
524 const FrontendAttrs *FA,
525 const StringRef SuperClass) {
526 if (R->isVerified())
527 return getState();
528
529 std::string FullName =
530 ObjCIVarRecord::createScopedName(SuperClass, R->getName());
531 SymbolContext SymCtx{FullName, EncodeKind::ObjectiveCInstanceVariable, FA};
532 return verifyImpl(R, SymCtx);
533}
534
536 ObjCIFSymbolKind Result = ObjCIFSymbolKind::None;
537 if (R->getLinkageForSymbol(ObjCIFSymbolKind::Class) != RecordLinkage::Unknown)
538 Result |= ObjCIFSymbolKind::Class;
539 if (R->getLinkageForSymbol(ObjCIFSymbolKind::MetaClass) !=
540 RecordLinkage::Unknown)
541 Result |= ObjCIFSymbolKind::MetaClass;
542 if (R->getLinkageForSymbol(ObjCIFSymbolKind::EHType) !=
543 RecordLinkage::Unknown)
544 Result |= ObjCIFSymbolKind::EHType;
545 return Result;
546}
547
549 const FrontendAttrs *FA) {
550 if (R->isVerified())
551 return getState();
552 SymbolContext SymCtx;
553 SymCtx.SymbolName = R->getName();
555
556 SymCtx.Kind = R->hasExceptionAttribute() ? EncodeKind::ObjectiveCClassEHType
557 : EncodeKind::ObjectiveCClass;
558 SymCtx.FA = FA;
559
560 return verifyImpl(R, SymCtx);
561}
562
564 const FrontendAttrs *FA) {
565 if (R->isVerified())
566 return getState();
567
568 // Global classifications could be obfusciated with `asm`.
569 SimpleSymbol Sym = parseSymbol(R->getName());
570 SymbolContext SymCtx;
571 SymCtx.SymbolName = Sym.Name;
572 SymCtx.Kind = Sym.Kind;
573 SymCtx.FA = FA;
574 SymCtx.Inlined = R->isInlined();
575 return verifyImpl(R, SymCtx);
576}
577
578void DylibVerifier::VerifierContext::emitDiag(llvm::function_ref<void()> Report,
579 RecordLoc *Loc) {
581 Diag->Report(diag::warn_target)
582 << (PrintArch ? getArchitectureName(Target.Arch)
583 : getTargetTripleName(Target));
585 }
586 if (Loc && Loc->isValid())
587 llvm::errs() << Loc->File << ":" << Loc->Line << ":" << 0 << ": ";
588
589 Report();
590}
591
592// The existence of weak-defined RTTI can not always be inferred from the
593// header files because they can be generated as part of an implementation
594// file.
595// InstallAPI doesn't warn about weak-defined RTTI, because this doesn't affect
596// static linking and so can be ignored for text-api files.
597static bool shouldIgnoreCpp(StringRef Name, bool IsWeakDef) {
598 return (IsWeakDef &&
599 (Name.starts_with("__ZTI") || Name.starts_with("__ZTS")));
600}
601void DylibVerifier::visitSymbolInDylib(const Record &R, SymbolContext &SymCtx) {
602 // Undefined symbols should not be in InstallAPI generated text-api files.
603 if (R.isUndefined()) {
604 updateState(Result::Valid);
605 return;
606 }
607
608 // Internal symbols should not be in InstallAPI generated text-api files.
609 if (R.isInternal()) {
610 updateState(Result::Valid);
611 return;
612 }
613
614 // Allow zippered symbols with potentially mismatching availability
615 // between macOS and macCatalyst in the final text-api file.
616 const StringRef SymbolName(SymCtx.SymbolName);
617 if (const Symbol *Sym = Exports->findSymbol(SymCtx.Kind, SymCtx.SymbolName,
618 SymCtx.ObjCIFKind)) {
619 if (Sym->hasArchitecture(Ctx.Target.Arch)) {
620 updateState(Result::Ignore);
621 return;
622 }
623 }
624
625 const bool IsLinkerSymbol = SymbolName.starts_with("$ld$");
626
627 if (R.isVerified()) {
628 // Check for unavailable symbols.
629 // This should only occur in the zippered case where we ignored
630 // availability until all headers have been parsed.
631 auto It = DeferredZipperedSymbols.find(SymCtx.SymbolName);
632 if (It == DeferredZipperedSymbols.end()) {
633 updateState(Result::Valid);
634 return;
635 }
636
638 for (const ZipperedDeclSource &ZSource : It->second) {
639 if (ZSource.FA->Avail.isObsoleted()) {
640 updateState(Result::Ignore);
641 return;
642 }
643 if (ZSource.T.Arch != Ctx.Target.Arch)
644 continue;
645 Locs.emplace_back(ZSource);
646 }
647 assert(Locs.size() == 2 && "Expected two decls for zippered symbol");
648
649 // Print violating declarations per platform.
650 for (const ZipperedDeclSource &ZSource : Locs) {
651 unsigned DiagID = 0;
652 if (Mode == VerificationMode::Pedantic || IsLinkerSymbol) {
653 updateState(Result::Invalid);
654 DiagID = diag::err_header_availability_mismatch;
655 } else if (Mode == VerificationMode::ErrorsAndWarnings) {
656 updateState(Result::Ignore);
657 DiagID = diag::warn_header_availability_mismatch;
658 } else {
659 updateState(Result::Ignore);
660 return;
661 }
662 // Bypass emitDiag banner and print the target everytime.
663 Ctx.Diag->setSourceManager(ZSource.SrcMgr);
664 Ctx.Diag->Report(diag::warn_target) << getTargetTripleName(ZSource.T);
665 Ctx.Diag->Report(ZSource.FA->Loc, DiagID)
666 << getAnnotatedName(&R, SymCtx) << ZSource.FA->Avail.isUnavailable()
667 << ZSource.FA->Avail.isUnavailable();
668 }
669 return;
670 }
671
672 if (shouldIgnoreCpp(SymbolName, R.isWeakDefined())) {
673 updateState(Result::Valid);
674 return;
675 }
676
677 if (Aliases.count({SymbolName.str(), SymCtx.Kind})) {
678 updateState(Result::Valid);
679 return;
680 }
681
682 // All checks at this point classify as some kind of violation.
683 // The different verification modes dictate whether they are reported to the
684 // user.
685 if (IsLinkerSymbol || (Mode > VerificationMode::ErrorsOnly))
686 accumulateSrcLocForDylibSymbols();
687 RecordLoc Loc = DWARFCtx->SourceLocs.lookup(SymCtx.SymbolName);
688
689 // Regardless of verification mode, error out on mismatched special linker
690 // symbols.
691 if (IsLinkerSymbol) {
692 Ctx.emitDiag(
693 [&]() {
694 Ctx.Diag->Report(diag::err_header_symbol_missing)
695 << getAnnotatedName(&R, SymCtx, Loc.isValid());
696 },
697 &Loc);
698 updateState(Result::Invalid);
699 return;
700 }
701
702 // Missing declarations for exported symbols are hard errors on Pedantic mode.
703 if (Mode == VerificationMode::Pedantic) {
704 Ctx.emitDiag(
705 [&]() {
706 Ctx.Diag->Report(diag::err_header_symbol_missing)
707 << getAnnotatedName(&R, SymCtx, Loc.isValid());
708 },
709 &Loc);
710 updateState(Result::Invalid);
711 return;
712 }
713
714 // Missing declarations for exported symbols are warnings on ErrorsAndWarnings
715 // mode.
717 Ctx.emitDiag(
718 [&]() {
719 Ctx.Diag->Report(diag::warn_header_symbol_missing)
720 << getAnnotatedName(&R, SymCtx, Loc.isValid());
721 },
722 &Loc);
723 updateState(Result::Ignore);
724 return;
725 }
726
727 // Missing declarations are dropped for ErrorsOnly mode. It is the last
728 // remaining mode.
729 updateState(Result::Ignore);
730 return;
731}
732
733void DylibVerifier::visitGlobal(const GlobalRecord &R) {
734 SymbolContext SymCtx;
735 SimpleSymbol Sym = parseSymbol(R.getName());
736 SymCtx.SymbolName = Sym.Name;
737 SymCtx.Kind = Sym.Kind;
738 visitSymbolInDylib(R, SymCtx);
739}
740
741void DylibVerifier::visitObjCIVar(const ObjCIVarRecord &R,
742 const StringRef Super) {
743 SymbolContext SymCtx;
744 SymCtx.SymbolName = ObjCIVarRecord::createScopedName(Super, R.getName());
745 SymCtx.Kind = EncodeKind::ObjectiveCInstanceVariable;
746 visitSymbolInDylib(R, SymCtx);
747}
748
749void DylibVerifier::accumulateSrcLocForDylibSymbols() {
750 if (DSYMPath.empty())
751 return;
752
753 assert(DWARFCtx != nullptr && "Expected an initialized DWARFContext");
754 if (DWARFCtx->ParsedDSYM)
755 return;
756 DWARFCtx->ParsedDSYM = true;
757 DWARFCtx->SourceLocs =
758 DylibReader::accumulateSourceLocFromDSYM(DSYMPath, Ctx.Target);
759}
760
761void DylibVerifier::visitObjCInterface(const ObjCInterfaceRecord &R) {
762 SymbolContext SymCtx;
763 SymCtx.SymbolName = R.getName();
764 SymCtx.ObjCIFKind = assignObjCIFSymbolKind(&R);
765 if (SymCtx.ObjCIFKind > ObjCIFSymbolKind::EHType) {
766 if (R.hasExceptionAttribute()) {
767 SymCtx.Kind = EncodeKind::ObjectiveCClassEHType;
768 visitSymbolInDylib(R, SymCtx);
769 }
770 SymCtx.Kind = EncodeKind::ObjectiveCClass;
771 visitSymbolInDylib(R, SymCtx);
772 } else {
773 SymCtx.Kind = R.hasExceptionAttribute() ? EncodeKind::ObjectiveCClassEHType
774 : EncodeKind::ObjectiveCClass;
775 visitSymbolInDylib(R, SymCtx);
776 }
777
778 for (const ObjCIVarRecord *IV : R.getObjCIVars())
779 visitObjCIVar(*IV, R.getName());
780}
781
782void DylibVerifier::visitObjCCategory(const ObjCCategoryRecord &R) {
783 for (const ObjCIVarRecord *IV : R.getObjCIVars())
784 visitObjCIVar(*IV, R.getSuperClassName());
785}
786
788 if (getState() == Result::NoVerify)
789 return Result::NoVerify;
790 assert(!Dylib.empty() && "No binary to verify against");
791
792 DWARFContext DWARFInfo;
793 DWARFCtx = &DWARFInfo;
794 Ctx.Target = Target(Architecture::AK_unknown, PlatformType::PLATFORM_UNKNOWN);
795 for (std::shared_ptr<RecordsSlice> Slice : Dylib) {
796 if (Ctx.Target.Arch == Slice->getTarget().Arch)
797 continue;
798 Ctx.DiscoveredFirstError = false;
799 Ctx.PrintArch = true;
800 Ctx.Target = Slice->getTarget();
801 Ctx.DylibSlice = Slice.get();
802 Slice->visit(*this);
803 }
804 return getState();
805}
806
808 const BinaryAttrs &ProvidedBA,
809 const LibAttrs &ProvidedReexports,
810 const LibAttrs &ProvidedClients,
811 const LibAttrs &ProvidedRPaths,
812 const FileType &FT) {
813 assert(!Dylib.empty() && "Need dylib to verify.");
814
815 // Pickup any load commands that can differ per slice to compare.
816 TargetList DylibTargets;
817 LibAttrs DylibReexports;
818 LibAttrs DylibClients;
819 LibAttrs DylibRPaths;
820 for (const std::shared_ptr<RecordsSlice> &RS : Dylib) {
821 DylibTargets.push_back(RS->getTarget());
822 const BinaryAttrs &BinInfo = RS->getBinaryAttrs();
823 for (const StringRef LibName : BinInfo.RexportedLibraries)
824 DylibReexports[LibName].set(DylibTargets.back().Arch);
825 for (const StringRef LibName : BinInfo.AllowableClients)
826 DylibClients[LibName].set(DylibTargets.back().Arch);
827 // Compare attributes that are only representable in >= TBD_V5.
828 if (FT >= FileType::TBD_V5)
829 for (const StringRef Name : BinInfo.RPaths)
830 DylibRPaths[Name].set(DylibTargets.back().Arch);
831 }
832
833 // Check targets first.
834 ArchitectureSet ProvidedArchs = mapToArchitectureSet(ProvidedTargets);
835 ArchitectureSet DylibArchs = mapToArchitectureSet(DylibTargets);
836 if (ProvidedArchs != DylibArchs) {
837 Ctx.Diag->Report(diag::err_architecture_mismatch)
838 << ProvidedArchs << DylibArchs;
839 return false;
840 }
841 auto ProvidedPlatforms = mapToPlatformVersionSet(ProvidedTargets);
842 auto DylibPlatforms = mapToPlatformVersionSet(DylibTargets);
843 if (ProvidedPlatforms != DylibPlatforms) {
844 const bool DiffMinOS =
845 mapToPlatformSet(ProvidedTargets) == mapToPlatformSet(DylibTargets);
846 if (DiffMinOS)
847 Ctx.Diag->Report(diag::warn_platform_mismatch)
848 << ProvidedPlatforms << DylibPlatforms;
849 else {
850 Ctx.Diag->Report(diag::err_platform_mismatch)
851 << ProvidedPlatforms << DylibPlatforms;
852 return false;
853 }
854 }
855
856 // Because InstallAPI requires certain attributes to match across architecture
857 // slices, take the first one to compare those with.
858 const BinaryAttrs &DylibBA = (*Dylib.begin())->getBinaryAttrs();
859
860 if (ProvidedBA.InstallName != DylibBA.InstallName) {
861 Ctx.Diag->Report(diag::err_install_name_mismatch)
862 << ProvidedBA.InstallName << DylibBA.InstallName;
863 return false;
864 }
865
866 if (ProvidedBA.CurrentVersion != DylibBA.CurrentVersion) {
867 Ctx.Diag->Report(diag::err_current_version_mismatch)
868 << ProvidedBA.CurrentVersion << DylibBA.CurrentVersion;
869 return false;
870 }
871
872 if (ProvidedBA.CompatVersion != DylibBA.CompatVersion) {
873 Ctx.Diag->Report(diag::err_compatibility_version_mismatch)
874 << ProvidedBA.CompatVersion << DylibBA.CompatVersion;
875 return false;
876 }
877
878 if (ProvidedBA.AppExtensionSafe != DylibBA.AppExtensionSafe) {
879 Ctx.Diag->Report(diag::err_appextension_safe_mismatch)
880 << (ProvidedBA.AppExtensionSafe ? "true" : "false")
881 << (DylibBA.AppExtensionSafe ? "true" : "false");
882 return false;
883 }
884
885 if (!DylibBA.TwoLevelNamespace) {
886 Ctx.Diag->Report(diag::err_no_twolevel_namespace);
887 return false;
888 }
889
890 if (ProvidedBA.OSLibNotForSharedCache != DylibBA.OSLibNotForSharedCache) {
891 Ctx.Diag->Report(diag::err_shared_cache_eligiblity_mismatch)
892 << (ProvidedBA.OSLibNotForSharedCache ? "true" : "false")
893 << (DylibBA.OSLibNotForSharedCache ? "true" : "false");
894 return false;
895 }
896
897 if (ProvidedBA.ParentUmbrella.empty() && !DylibBA.ParentUmbrella.empty()) {
898 Ctx.Diag->Report(diag::err_parent_umbrella_missing)
899 << "installAPI option" << DylibBA.ParentUmbrella;
900 return false;
901 }
902
903 if (!ProvidedBA.ParentUmbrella.empty() && DylibBA.ParentUmbrella.empty()) {
904 Ctx.Diag->Report(diag::err_parent_umbrella_missing)
905 << "binary file" << ProvidedBA.ParentUmbrella;
906 return false;
907 }
908
909 if ((!ProvidedBA.ParentUmbrella.empty()) &&
910 (ProvidedBA.ParentUmbrella != DylibBA.ParentUmbrella)) {
911 Ctx.Diag->Report(diag::err_parent_umbrella_mismatch)
912 << ProvidedBA.ParentUmbrella << DylibBA.ParentUmbrella;
913 return false;
914 }
915
916 auto CompareLibraries = [&](const LibAttrs &Provided, const LibAttrs &Dylib,
917 unsigned DiagID_missing, unsigned DiagID_mismatch,
918 bool Fatal = true) {
919 if (Provided == Dylib)
920 return true;
921
922 for (const llvm::StringMapEntry<ArchitectureSet> &PAttr : Provided) {
923 const auto DAttrIt = Dylib.find(PAttr.getKey());
924 if (DAttrIt == Dylib.end()) {
925 Ctx.Diag->Report(DiagID_missing) << "binary file" << PAttr;
926 if (Fatal)
927 return false;
928 }
929
930 if (PAttr.getValue() != DAttrIt->getValue()) {
931 Ctx.Diag->Report(DiagID_mismatch) << PAttr << *DAttrIt;
932 if (Fatal)
933 return false;
934 }
935 }
936
937 for (const llvm::StringMapEntry<ArchitectureSet> &DAttr : Dylib) {
938 const auto PAttrIt = Provided.find(DAttr.getKey());
939 if (PAttrIt == Provided.end()) {
940 Ctx.Diag->Report(DiagID_missing) << "installAPI option" << DAttr;
941 if (!Fatal)
942 continue;
943 return false;
944 }
945
946 if (PAttrIt->getValue() != DAttr.getValue()) {
947 if (Fatal)
948 llvm_unreachable("this case was already covered above.");
949 }
950 }
951 return true;
952 };
953
954 if (!CompareLibraries(ProvidedReexports, DylibReexports,
955 diag::err_reexported_libraries_missing,
956 diag::err_reexported_libraries_mismatch))
957 return false;
958
959 if (!CompareLibraries(ProvidedClients, DylibClients,
960 diag::err_allowable_clients_missing,
961 diag::err_allowable_clients_mismatch))
962 return false;
963
964 if (FT >= FileType::TBD_V5) {
965 // Ignore rpath differences if building an asan variant, since the
966 // compiler injects additional paths.
967 // FIXME: Building with sanitizers does not always change the install
968 // name, so this is not a foolproof solution.
969 if (!ProvidedBA.InstallName.ends_with("_asan")) {
970 if (!CompareLibraries(ProvidedRPaths, DylibRPaths,
971 diag::warn_rpaths_missing,
972 diag::warn_rpaths_mismatch,
973 /*Fatal=*/false))
974 return true;
975 }
976 }
977
978 return true;
979}
980
981std::unique_ptr<SymbolSet> DylibVerifier::takeExports() {
982 for (const auto &[Alias, Base] : Aliases) {
983 TargetList Targets;
984 SymbolFlags Flags = SymbolFlags::None;
985 if (const Symbol *Sym = Exports->findSymbol(Base.second, Base.first)) {
986 Flags = Sym->getFlags();
987 Targets = {Sym->targets().begin(), Sym->targets().end()};
988 }
989
990 Record R(Alias.first, RecordLinkage::Exported, Flags);
991 SymbolContext SymCtx;
992 SymCtx.SymbolName = Alias.first;
993 SymCtx.Kind = Alias.second;
994 addSymbol(&R, SymCtx, std::move(Targets));
995 }
996
997 return std::move(Exports);
998}
999
1000} // namespace installapi
1001} // namespace clang
llvm::MachO::ObjCIVarRecord ObjCIVarRecord
Definition: MachO.h:37
llvm::MachO::SymbolFlags SymbolFlags
Definition: MachO.h:29
llvm::MachO::RecordLoc RecordLoc
Definition: MachO.h:40
llvm::MachO::ObjCCategoryRecord ObjCCategoryRecord
Definition: MachO.h:36
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:35
llvm::MachO::ObjCIFSymbolKind ObjCIFSymbolKind
Definition: MachO.h:38
llvm::MachO::FileType FileType
Definition: MachO.h:45
llvm::MachO::Target Target
Definition: MachO.h:48
llvm::MachO::RecordsSlice::BinaryAttrs BinaryAttrs
Definition: MachO.h:42
llvm::MachO::Record Record
Definition: MachO.h:31
llvm::MachO::TargetList TargetList
Definition: MachO.h:49
llvm::MachO::SimpleSymbol SimpleSymbol
Definition: MachO.h:44
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:1971
Represents a variable declaration or definition.
Definition: Decl.h:918
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:250
@ 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.