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