xref: /llvm-project/clang/lib/InstallAPI/DylibVerifier.cpp (revision 6d579cd1d91cdde5adfa455bda7903e737e9d5cf)
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 "clang/InstallAPI/FrontendRecords.h"
11 #include "clang/InstallAPI/InstallAPIDiagnostic.h"
12 #include "llvm/Demangle/Demangle.h"
13 
14 using namespace llvm::MachO;
15 
16 namespace clang {
17 namespace installapi {
18 
19 /// Metadata stored about a mapping of a declaration to a symbol.
20 struct DylibVerifier::SymbolContext {
21   // Name to use for all querying and verification
22   // purposes.
23   std::string SymbolName{""};
24 
25   // Kind to map symbol type against record.
26   EncodeKind Kind = EncodeKind::GlobalSymbol;
27 
28   // Frontend Attributes tied to the AST.
29   const FrontendAttrs *FA = nullptr;
30 
31   // The ObjCInterface symbol type, if applicable.
32   ObjCIFSymbolKind ObjCIFKind = ObjCIFSymbolKind::None;
33 
34   // Whether Decl is inlined.
35   bool Inlined = false;
36 };
37 
38 static bool isCppMangled(StringRef Name) {
39   // InstallAPI currently only supports itanium manglings.
40   return (Name.starts_with("_Z") || Name.starts_with("__Z") ||
41           Name.starts_with("___Z"));
42 }
43 
44 static std::string demangle(StringRef Name) {
45   // InstallAPI currently only supports itanium manglings.
46   if (!isCppMangled(Name))
47     return Name.str();
48   char *Result = llvm::itaniumDemangle(Name);
49   if (!Result)
50     return Name.str();
51 
52   std::string Demangled(Result);
53   free(Result);
54   return Demangled;
55 }
56 
57 std::string DylibVerifier::getAnnotatedName(const Record *R,
58                                             SymbolContext &SymCtx,
59                                             bool ValidSourceLoc) {
60   assert(!SymCtx.SymbolName.empty() && "Expected symbol name");
61 
62   const StringRef SymbolName = SymCtx.SymbolName;
63   std::string PrettyName =
64       (Demangle && (SymCtx.Kind == EncodeKind::GlobalSymbol))
65           ? demangle(SymbolName)
66           : SymbolName.str();
67 
68   std::string Annotation;
69   if (R->isWeakDefined())
70     Annotation += "(weak-def) ";
71   if (R->isWeakReferenced())
72     Annotation += "(weak-ref) ";
73   if (R->isThreadLocalValue())
74     Annotation += "(tlv) ";
75 
76   // Check if symbol represents only part of a @interface declaration.
77   switch (SymCtx.ObjCIFKind) {
78   default:
79     break;
80   case ObjCIFSymbolKind::EHType:
81     return Annotation + "Exception Type of " + PrettyName;
82   case ObjCIFSymbolKind::MetaClass:
83     return Annotation + "Metaclass of " + PrettyName;
84   case ObjCIFSymbolKind::Class:
85     return Annotation + "Class of " + PrettyName;
86   }
87 
88   // Only print symbol type prefix or leading "_" if there is no source location
89   // tied to it. This can only ever happen when the location has to come from
90   // debug info.
91   if (ValidSourceLoc) {
92     StringRef PrettyNameRef(PrettyName);
93     if ((SymCtx.Kind == EncodeKind::GlobalSymbol) &&
94         !isCppMangled(SymbolName) && PrettyNameRef.starts_with("_"))
95       return Annotation + PrettyNameRef.drop_front(1).str();
96     return Annotation + PrettyName;
97   }
98 
99   switch (SymCtx.Kind) {
100   case EncodeKind::GlobalSymbol:
101     return Annotation + PrettyName;
102   case EncodeKind::ObjectiveCInstanceVariable:
103     return Annotation + "(ObjC IVar) " + PrettyName;
104   case EncodeKind::ObjectiveCClass:
105     return Annotation + "(ObjC Class) " + PrettyName;
106   case EncodeKind::ObjectiveCClassEHType:
107     return Annotation + "(ObjC Class EH) " + PrettyName;
108   }
109 
110   llvm_unreachable("unexpected case for EncodeKind");
111 }
112 
113 static DylibVerifier::Result updateResult(const DylibVerifier::Result Prev,
114                                           const DylibVerifier::Result Curr) {
115   if (Prev == Curr)
116     return Prev;
117 
118   // Never update from invalid or noverify state.
119   if ((Prev == DylibVerifier::Result::Invalid) ||
120       (Prev == DylibVerifier::Result::NoVerify))
121     return Prev;
122 
123   // Don't let an ignored verification remove a valid one.
124   if (Prev == DylibVerifier::Result::Valid &&
125       Curr == DylibVerifier::Result::Ignore)
126     return Prev;
127 
128   return Curr;
129 }
130 // __private_extern__ is a deprecated specifier that clang does not
131 // respect in all contexts, it should just be considered hidden for InstallAPI.
132 static bool shouldIgnorePrivateExternAttr(const Decl *D) {
133   if (const FunctionDecl *FD = cast<FunctionDecl>(D))
134     return FD->getStorageClass() == StorageClass::SC_PrivateExtern;
135   if (const VarDecl *VD = cast<VarDecl>(D))
136     return VD->getStorageClass() == StorageClass::SC_PrivateExtern;
137 
138   return false;
139 }
140 
141 Record *findRecordFromSlice(const RecordsSlice *Slice, StringRef Name,
142                             EncodeKind Kind) {
143   switch (Kind) {
144   case EncodeKind::GlobalSymbol:
145     return Slice->findGlobal(Name);
146   case EncodeKind::ObjectiveCInstanceVariable:
147     return Slice->findObjCIVar(Name.contains('.'), Name);
148   case EncodeKind::ObjectiveCClass:
149   case EncodeKind::ObjectiveCClassEHType:
150     return Slice->findObjCInterface(Name);
151   }
152   llvm_unreachable("unexpected end when finding record");
153 }
154 
155 void DylibVerifier::updateState(Result State) {
156   Ctx.FrontendState = updateResult(Ctx.FrontendState, State);
157 }
158 
159 void DylibVerifier::addSymbol(const Record *R, SymbolContext &SymCtx,
160                               TargetList &&Targets) {
161   if (Targets.empty())
162     Targets = {Ctx.Target};
163 
164   Exports->addGlobal(SymCtx.Kind, SymCtx.SymbolName, R->getFlags(), Targets);
165 }
166 
167 bool DylibVerifier::shouldIgnoreObsolete(const Record *R, SymbolContext &SymCtx,
168                                          const Record *DR) {
169   return SymCtx.FA->Avail.isObsoleted();
170 }
171 
172 bool DylibVerifier::compareObjCInterfaceSymbols(const Record *R,
173                                                 SymbolContext &SymCtx,
174                                                 const ObjCInterfaceRecord *DR) {
175   const bool IsDeclVersionComplete =
176       ((SymCtx.ObjCIFKind & ObjCIFSymbolKind::Class) ==
177        ObjCIFSymbolKind::Class) &&
178       ((SymCtx.ObjCIFKind & ObjCIFSymbolKind::MetaClass) ==
179        ObjCIFSymbolKind::MetaClass);
180 
181   const bool IsDylibVersionComplete = DR->isCompleteInterface();
182 
183   // The common case, a complete ObjCInterface.
184   if (IsDeclVersionComplete && IsDylibVersionComplete)
185     return true;
186 
187   auto PrintDiagnostic = [&](auto SymLinkage, const Record *Record,
188                              StringRef SymName, bool PrintAsWarning = false) {
189     if (SymLinkage == RecordLinkage::Unknown)
190       Ctx.emitDiag([&]() {
191         Ctx.Diag->Report(SymCtx.FA->D->getLocation(),
192                          PrintAsWarning ? diag::warn_library_missing_symbol
193                                         : diag::err_library_missing_symbol)
194             << SymName;
195       });
196     else
197       Ctx.emitDiag([&]() {
198         Ctx.Diag->Report(SymCtx.FA->D->getLocation(),
199                          PrintAsWarning ? diag::warn_library_hidden_symbol
200                                         : diag::err_library_hidden_symbol)
201             << SymName;
202       });
203   };
204 
205   if (IsDeclVersionComplete) {
206     // The decl represents a complete ObjCInterface, but the symbols in the
207     // dylib do not. Determine which symbol is missing. To keep older projects
208     // building, treat this as a warning.
209     if (!DR->isExportedSymbol(ObjCIFSymbolKind::Class)) {
210       SymCtx.ObjCIFKind = ObjCIFSymbolKind::Class;
211       PrintDiagnostic(DR->getLinkageForSymbol(ObjCIFSymbolKind::Class), R,
212                       getAnnotatedName(R, SymCtx),
213                       /*PrintAsWarning=*/true);
214     }
215     if (!DR->isExportedSymbol(ObjCIFSymbolKind::MetaClass)) {
216       SymCtx.ObjCIFKind = ObjCIFSymbolKind::MetaClass;
217       PrintDiagnostic(DR->getLinkageForSymbol(ObjCIFSymbolKind::MetaClass), R,
218                       getAnnotatedName(R, SymCtx),
219                       /*PrintAsWarning=*/true);
220     }
221     return true;
222   }
223 
224   if (DR->isExportedSymbol(SymCtx.ObjCIFKind)) {
225     if (!IsDylibVersionComplete) {
226       // Both the declaration and dylib have a non-complete interface.
227       SymCtx.Kind = EncodeKind::GlobalSymbol;
228       SymCtx.SymbolName = R->getName();
229     }
230     return true;
231   }
232 
233   // At this point that means there was not a matching class symbol
234   // to represent the one discovered as a declaration.
235   PrintDiagnostic(DR->getLinkageForSymbol(SymCtx.ObjCIFKind), R,
236                   SymCtx.SymbolName);
237   return false;
238 }
239 
240 DylibVerifier::Result DylibVerifier::compareVisibility(const Record *R,
241                                                        SymbolContext &SymCtx,
242                                                        const Record *DR) {
243 
244   if (R->isExported()) {
245     if (!DR) {
246       Ctx.emitDiag([&]() {
247         Ctx.Diag->Report(SymCtx.FA->D->getLocation(),
248                          diag::err_library_missing_symbol)
249             << getAnnotatedName(R, SymCtx);
250       });
251       return Result::Invalid;
252     }
253     if (DR->isInternal()) {
254       Ctx.emitDiag([&]() {
255         Ctx.Diag->Report(SymCtx.FA->D->getLocation(),
256                          diag::err_library_hidden_symbol)
257             << getAnnotatedName(R, SymCtx);
258       });
259       return Result::Invalid;
260     }
261   }
262 
263   // Emit a diagnostic for hidden declarations with external symbols, except
264   // when theres an inlined attribute.
265   if ((R->isInternal() && !SymCtx.Inlined) && DR && DR->isExported()) {
266 
267     if (Mode == VerificationMode::ErrorsOnly)
268       return Result::Ignore;
269 
270     if (shouldIgnorePrivateExternAttr(SymCtx.FA->D))
271       return Result::Ignore;
272 
273     unsigned ID;
274     Result Outcome;
275     if (Mode == VerificationMode::ErrorsAndWarnings) {
276       ID = diag::warn_header_hidden_symbol;
277       Outcome = Result::Ignore;
278     } else {
279       ID = diag::err_header_hidden_symbol;
280       Outcome = Result::Invalid;
281     }
282     Ctx.emitDiag([&]() {
283       Ctx.Diag->Report(SymCtx.FA->D->getLocation(), ID)
284           << getAnnotatedName(R, SymCtx);
285     });
286     return Outcome;
287   }
288 
289   if (R->isInternal())
290     return Result::Ignore;
291 
292   return Result::Valid;
293 }
294 
295 DylibVerifier::Result DylibVerifier::compareAvailability(const Record *R,
296                                                          SymbolContext &SymCtx,
297                                                          const Record *DR) {
298   if (!SymCtx.FA->Avail.isUnavailable())
299     return Result::Valid;
300 
301   const bool IsDeclAvailable = SymCtx.FA->Avail.isUnavailable();
302 
303   switch (Mode) {
304   case VerificationMode::ErrorsAndWarnings:
305     Ctx.emitDiag([&]() {
306       Ctx.Diag->Report(SymCtx.FA->D->getLocation(),
307                        diag::warn_header_availability_mismatch)
308           << getAnnotatedName(R, SymCtx) << IsDeclAvailable << IsDeclAvailable;
309     });
310     return Result::Ignore;
311   case VerificationMode::Pedantic:
312     Ctx.emitDiag([&]() {
313       Ctx.Diag->Report(SymCtx.FA->D->getLocation(),
314                        diag::err_header_availability_mismatch)
315           << getAnnotatedName(R, SymCtx) << IsDeclAvailable << IsDeclAvailable;
316     });
317     return Result::Invalid;
318   case VerificationMode::ErrorsOnly:
319     return Result::Ignore;
320   case VerificationMode::Invalid:
321     llvm_unreachable("Unexpected verification mode symbol verification");
322   }
323   llvm_unreachable("Unexpected verification mode symbol verification");
324 }
325 
326 bool DylibVerifier::compareSymbolFlags(const Record *R, SymbolContext &SymCtx,
327                                        const Record *DR) {
328   if (DR->isThreadLocalValue() && !R->isThreadLocalValue()) {
329     Ctx.emitDiag([&]() {
330       Ctx.Diag->Report(SymCtx.FA->D->getLocation(),
331                        diag::err_dylib_symbol_flags_mismatch)
332           << getAnnotatedName(DR, SymCtx) << DR->isThreadLocalValue();
333     });
334     return false;
335   }
336   if (!DR->isThreadLocalValue() && R->isThreadLocalValue()) {
337     Ctx.emitDiag([&]() {
338       Ctx.Diag->Report(SymCtx.FA->D->getLocation(),
339                        diag::err_header_symbol_flags_mismatch)
340           << getAnnotatedName(R, SymCtx) << R->isThreadLocalValue();
341     });
342     return false;
343   }
344 
345   if (DR->isWeakDefined() && !R->isWeakDefined()) {
346     Ctx.emitDiag([&]() {
347       Ctx.Diag->Report(SymCtx.FA->D->getLocation(),
348                        diag::err_dylib_symbol_flags_mismatch)
349           << getAnnotatedName(DR, SymCtx) << R->isWeakDefined();
350     });
351     return false;
352   }
353   if (!DR->isWeakDefined() && R->isWeakDefined()) {
354     Ctx.emitDiag([&]() {
355       Ctx.Diag->Report(SymCtx.FA->D->getLocation(),
356                        diag::err_header_symbol_flags_mismatch)
357           << getAnnotatedName(R, SymCtx) << R->isWeakDefined();
358     });
359     return false;
360   }
361 
362   return true;
363 }
364 
365 DylibVerifier::Result DylibVerifier::verifyImpl(Record *R,
366                                                 SymbolContext &SymCtx) {
367   R->setVerify();
368   if (!canVerify()) {
369     // Accumulate symbols when not in verifying against dylib.
370     if (R->isExported() && !SymCtx.FA->Avail.isUnavailable() &&
371         !SymCtx.FA->Avail.isObsoleted()) {
372       addSymbol(R, SymCtx);
373     }
374     return Ctx.FrontendState;
375   }
376 
377   Record *DR =
378       findRecordFromSlice(Ctx.DylibSlice, SymCtx.SymbolName, SymCtx.Kind);
379   if (DR)
380     DR->setVerify();
381 
382   if (shouldIgnoreObsolete(R, SymCtx, DR)) {
383     updateState(Result::Ignore);
384     return Ctx.FrontendState;
385   }
386 
387   // Unavailable declarations don't need matching symbols.
388   if (SymCtx.FA->Avail.isUnavailable() && (!DR || DR->isInternal())) {
389     updateState(Result::Valid);
390     return Ctx.FrontendState;
391   }
392 
393   Result VisibilityCheck = compareVisibility(R, SymCtx, DR);
394   if (VisibilityCheck != Result::Valid) {
395     updateState(VisibilityCheck);
396     return Ctx.FrontendState;
397   }
398 
399   // All missing symbol cases to diagnose have been handled now.
400   if (!DR) {
401     updateState(Result::Ignore);
402     return Ctx.FrontendState;
403   }
404 
405   // Check for mismatching ObjC interfaces.
406   if (SymCtx.ObjCIFKind != ObjCIFSymbolKind::None) {
407     if (!compareObjCInterfaceSymbols(
408             R, SymCtx, Ctx.DylibSlice->findObjCInterface(DR->getName()))) {
409       updateState(Result::Invalid);
410       return Ctx.FrontendState;
411     }
412   }
413 
414   Result AvailabilityCheck = compareAvailability(R, SymCtx, DR);
415   if (AvailabilityCheck != Result::Valid) {
416     updateState(AvailabilityCheck);
417     return Ctx.FrontendState;
418   }
419 
420   if (!compareSymbolFlags(R, SymCtx, DR)) {
421     updateState(Result::Invalid);
422     return Ctx.FrontendState;
423   }
424 
425   addSymbol(R, SymCtx);
426   updateState(Result::Valid);
427   return Ctx.FrontendState;
428 }
429 
430 bool DylibVerifier::canVerify() {
431   return Ctx.FrontendState != Result::NoVerify;
432 }
433 
434 void DylibVerifier::assignSlice(const Target &T) {
435   assert(T == Ctx.Target && "Active targets should match.");
436   if (Dylib.empty())
437     return;
438 
439   // Note: there are no reexport slices with binaries, as opposed to TBD files,
440   // so it can be assumed that the target match is the active top-level library.
441   auto It = find_if(
442       Dylib, [&T](const auto &Slice) { return T == Slice->getTarget(); });
443 
444   assert(It != Dylib.end() && "Target slice should always exist.");
445   Ctx.DylibSlice = It->get();
446 }
447 
448 void DylibVerifier::setTarget(const Target &T) {
449   Ctx.Target = T;
450   Ctx.DiscoveredFirstError = false;
451   if (Dylib.empty()) {
452     updateState(Result::NoVerify);
453     return;
454   }
455   updateState(Result::Ignore);
456   assignSlice(T);
457 }
458 
459 DylibVerifier::Result DylibVerifier::verify(ObjCIVarRecord *R,
460                                             const FrontendAttrs *FA,
461                                             const StringRef SuperClass) {
462   if (R->isVerified())
463     return getState();
464 
465   std::string FullName =
466       ObjCIVarRecord::createScopedName(SuperClass, R->getName());
467   SymbolContext SymCtx{FullName, EncodeKind::ObjectiveCInstanceVariable, FA};
468   return verifyImpl(R, SymCtx);
469 }
470 
471 static ObjCIFSymbolKind assignObjCIFSymbolKind(const ObjCInterfaceRecord *R) {
472   ObjCIFSymbolKind Result = ObjCIFSymbolKind::None;
473   if (R->getLinkageForSymbol(ObjCIFSymbolKind::Class) != RecordLinkage::Unknown)
474     Result |= ObjCIFSymbolKind::Class;
475   if (R->getLinkageForSymbol(ObjCIFSymbolKind::MetaClass) !=
476       RecordLinkage::Unknown)
477     Result |= ObjCIFSymbolKind::MetaClass;
478   if (R->getLinkageForSymbol(ObjCIFSymbolKind::EHType) !=
479       RecordLinkage::Unknown)
480     Result |= ObjCIFSymbolKind::EHType;
481   return Result;
482 }
483 
484 DylibVerifier::Result DylibVerifier::verify(ObjCInterfaceRecord *R,
485                                             const FrontendAttrs *FA) {
486   if (R->isVerified())
487     return getState();
488   SymbolContext SymCtx;
489   SymCtx.SymbolName = R->getName();
490   SymCtx.ObjCIFKind = assignObjCIFSymbolKind(R);
491 
492   SymCtx.Kind = R->hasExceptionAttribute() ? EncodeKind::ObjectiveCClassEHType
493                                            : EncodeKind::ObjectiveCClass;
494   SymCtx.FA = FA;
495 
496   return verifyImpl(R, SymCtx);
497 }
498 
499 DylibVerifier::Result DylibVerifier::verify(GlobalRecord *R,
500                                             const FrontendAttrs *FA) {
501   if (R->isVerified())
502     return getState();
503 
504   // Global classifications could be obfusciated with `asm`.
505   SimpleSymbol Sym = parseSymbol(R->getName());
506   SymbolContext SymCtx;
507   SymCtx.SymbolName = Sym.Name;
508   SymCtx.Kind = Sym.Kind;
509   SymCtx.FA = FA;
510   SymCtx.Inlined = R->isInlined();
511   return verifyImpl(R, SymCtx);
512 }
513 
514 void DylibVerifier::VerifierContext::emitDiag(
515     llvm::function_ref<void()> Report) {
516   if (!DiscoveredFirstError) {
517     Diag->Report(diag::warn_target)
518         << (PrintArch ? getArchitectureName(Target.Arch)
519                       : getTargetTripleName(Target));
520     DiscoveredFirstError = true;
521   }
522 
523   Report();
524 }
525 
526 // The existence of weak-defined RTTI can not always be inferred from the
527 // header files because they can be generated as part of an implementation
528 // file.
529 // InstallAPI doesn't warn about weak-defined RTTI, because this doesn't affect
530 // static linking and so can be ignored for text-api files.
531 static bool shouldIgnoreCpp(StringRef Name, bool IsWeakDef) {
532   return (IsWeakDef &&
533           (Name.starts_with("__ZTI") || Name.starts_with("__ZTS")));
534 }
535 void DylibVerifier::visitSymbolInDylib(const Record &R, SymbolContext &SymCtx) {
536   // Undefined symbols should not be in InstallAPI generated text-api files.
537   if (R.isUndefined()) {
538     updateState(Result::Valid);
539     return;
540   }
541 
542   // Internal symbols should not be in InstallAPI generated text-api files.
543   if (R.isInternal()) {
544     updateState(Result::Valid);
545     return;
546   }
547 
548   // Allow zippered symbols with potentially mismatching availability
549   // between macOS and macCatalyst in the final text-api file.
550   const StringRef SymbolName(SymCtx.SymbolName);
551   if (const Symbol *Sym = Exports->findSymbol(SymCtx.Kind, SymCtx.SymbolName,
552                                               SymCtx.ObjCIFKind)) {
553     if (Sym->hasArchitecture(Ctx.Target.Arch)) {
554       updateState(Result::Ignore);
555       return;
556     }
557   }
558 
559   if (shouldIgnoreCpp(SymbolName, R.isWeakDefined())) {
560     updateState(Result::Valid);
561     return;
562   }
563 
564   // All checks at this point classify as some kind of violation that should be
565   // reported.
566 
567   // Regardless of verification mode, error out on mismatched special linker
568   // symbols.
569   if (SymbolName.starts_with("$ld$")) {
570     Ctx.emitDiag([&]() {
571       Ctx.Diag->Report(diag::err_header_symbol_missing)
572           << getAnnotatedName(&R, SymCtx, /*ValidSourceLoc=*/false);
573     });
574     updateState(Result::Invalid);
575     return;
576   }
577 
578   // Missing declarations for exported symbols are hard errors on Pedantic mode.
579   if (Mode == VerificationMode::Pedantic) {
580     Ctx.emitDiag([&]() {
581       Ctx.Diag->Report(diag::err_header_symbol_missing)
582           << getAnnotatedName(&R, SymCtx, /*ValidSourceLoc=*/false);
583     });
584     updateState(Result::Invalid);
585     return;
586   }
587 
588   // Missing declarations for exported symbols are warnings on ErrorsAndWarnings
589   // mode.
590   if (Mode == VerificationMode::ErrorsAndWarnings) {
591     Ctx.emitDiag([&]() {
592       Ctx.Diag->Report(diag::warn_header_symbol_missing)
593           << getAnnotatedName(&R, SymCtx, /*ValidSourceLoc=*/false);
594     });
595     updateState(Result::Ignore);
596     return;
597   }
598 
599   // Missing declarations are dropped for ErrorsOnly mode. It is the last
600   // remaining mode.
601   updateState(Result::Ignore);
602   return;
603 }
604 
605 void DylibVerifier::visitGlobal(const GlobalRecord &R) {
606   if (R.isVerified())
607     return;
608   SymbolContext SymCtx;
609   SimpleSymbol Sym = parseSymbol(R.getName());
610   SymCtx.SymbolName = Sym.Name;
611   SymCtx.Kind = Sym.Kind;
612   visitSymbolInDylib(R, SymCtx);
613 }
614 
615 void DylibVerifier::visitObjCIVar(const ObjCIVarRecord &R,
616                                   const StringRef Super) {
617   if (R.isVerified())
618     return;
619   SymbolContext SymCtx;
620   SymCtx.SymbolName = ObjCIVarRecord::createScopedName(Super, R.getName());
621   SymCtx.Kind = EncodeKind::ObjectiveCInstanceVariable;
622   visitSymbolInDylib(R, SymCtx);
623 }
624 
625 void DylibVerifier::visitObjCInterface(const ObjCInterfaceRecord &R) {
626   if (R.isVerified())
627     return;
628   SymbolContext SymCtx;
629   SymCtx.SymbolName = R.getName();
630   SymCtx.ObjCIFKind = assignObjCIFSymbolKind(&R);
631   if (SymCtx.ObjCIFKind > ObjCIFSymbolKind::EHType) {
632     if (R.hasExceptionAttribute()) {
633       SymCtx.Kind = EncodeKind::ObjectiveCClassEHType;
634       visitSymbolInDylib(R, SymCtx);
635     }
636     SymCtx.Kind = EncodeKind::ObjectiveCClass;
637     visitSymbolInDylib(R, SymCtx);
638   } else {
639     SymCtx.Kind = R.hasExceptionAttribute() ? EncodeKind::ObjectiveCClassEHType
640                                             : EncodeKind::ObjectiveCClass;
641     visitSymbolInDylib(R, SymCtx);
642   }
643 
644   for (const ObjCIVarRecord *IV : R.getObjCIVars())
645     visitObjCIVar(*IV, R.getName());
646 }
647 
648 void DylibVerifier::visitObjCCategory(const ObjCCategoryRecord &R) {
649   for (const ObjCIVarRecord *IV : R.getObjCIVars())
650     visitObjCIVar(*IV, R.getSuperClassName());
651 }
652 
653 DylibVerifier::Result DylibVerifier::verifyRemainingSymbols() {
654   if (getState() == Result::NoVerify)
655     return Result::NoVerify;
656   assert(!Dylib.empty() && "No binary to verify against");
657 
658   Ctx.DiscoveredFirstError = false;
659   Ctx.PrintArch = true;
660   for (std::shared_ptr<RecordsSlice> Slice : Dylib) {
661     Ctx.Target = Slice->getTarget();
662     Ctx.DylibSlice = Slice.get();
663     Slice->visit(*this);
664   }
665   return getState();
666 }
667 
668 } // namespace installapi
669 } // namespace clang
670