xref: /llvm-project/clang/lib/InstallAPI/DylibVerifier.cpp (revision c24efffabbf96c7a138439bb8e219850c4d78887)
1 //===- DylibVerifier.cpp ----------------------------------------*- C++--*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "clang/InstallAPI/DylibVerifier.h"
10 #include "DiagnosticBuilderWrappers.h"
11 #include "clang/InstallAPI/FrontendRecords.h"
12 #include "clang/InstallAPI/InstallAPIDiagnostic.h"
13 #include "llvm/Demangle/Demangle.h"
14 #include "llvm/TextAPI/DylibReader.h"
15 
16 using namespace llvm::MachO;
17 
18 namespace clang {
19 namespace installapi {
20 
21 /// Metadata stored about a mapping of a declaration to a symbol.
22 struct DylibVerifier::SymbolContext {
23   // Name to use for all querying and verification
24   // purposes.
25   std::string SymbolName{""};
26 
27   // Kind to map symbol type against record.
28   EncodeKind Kind = EncodeKind::GlobalSymbol;
29 
30   // Frontend Attributes tied to the AST.
31   const FrontendAttrs *FA = nullptr;
32 
33   // The ObjCInterface symbol type, if applicable.
34   ObjCIFSymbolKind ObjCIFKind = ObjCIFSymbolKind::None;
35 
36   // Whether Decl is inlined.
37   bool Inlined = false;
38 };
39 
40 struct DylibVerifier::DWARFContext {
41   // Track whether DSYM parsing has already been attempted to avoid re-parsing.
42   bool ParsedDSYM{false};
43 
44   // Lookup table for source locations by symbol name.
45   DylibReader::SymbolToSourceLocMap SourceLocs{};
46 };
47 
48 static bool isCppMangled(StringRef Name) {
49   // InstallAPI currently only supports itanium manglings.
50   return (Name.starts_with("_Z") || Name.starts_with("__Z") ||
51           Name.starts_with("___Z"));
52 }
53 
54 static std::string demangle(StringRef Name) {
55   // InstallAPI currently only supports itanium manglings.
56   if (!isCppMangled(Name))
57     return Name.str();
58   char *Result = llvm::itaniumDemangle(Name);
59   if (!Result)
60     return Name.str();
61 
62   std::string Demangled(Result);
63   free(Result);
64   return Demangled;
65 }
66 
67 std::string DylibVerifier::getAnnotatedName(const Record *R,
68                                             SymbolContext &SymCtx,
69                                             bool ValidSourceLoc) {
70   assert(!SymCtx.SymbolName.empty() && "Expected symbol name");
71 
72   const StringRef SymbolName = SymCtx.SymbolName;
73   std::string PrettyName =
74       (Demangle && (SymCtx.Kind == EncodeKind::GlobalSymbol))
75           ? demangle(SymbolName)
76           : SymbolName.str();
77 
78   std::string Annotation;
79   if (R->isWeakDefined())
80     Annotation += "(weak-def) ";
81   if (R->isWeakReferenced())
82     Annotation += "(weak-ref) ";
83   if (R->isThreadLocalValue())
84     Annotation += "(tlv) ";
85 
86   // Check if symbol represents only part of a @interface declaration.
87   switch (SymCtx.ObjCIFKind) {
88   default:
89     break;
90   case ObjCIFSymbolKind::EHType:
91     return Annotation + "Exception Type of " + PrettyName;
92   case ObjCIFSymbolKind::MetaClass:
93     return Annotation + "Metaclass of " + PrettyName;
94   case ObjCIFSymbolKind::Class:
95     return Annotation + "Class of " + PrettyName;
96   }
97 
98   // Only print symbol type prefix or leading "_" if there is no source location
99   // tied to it. This can only ever happen when the location has to come from
100   // debug info.
101   if (ValidSourceLoc) {
102     StringRef PrettyNameRef(PrettyName);
103     if ((SymCtx.Kind == EncodeKind::GlobalSymbol) &&
104         !isCppMangled(SymbolName) && PrettyNameRef.starts_with("_"))
105       return Annotation + PrettyNameRef.drop_front(1).str();
106     return Annotation + PrettyName;
107   }
108 
109   switch (SymCtx.Kind) {
110   case EncodeKind::GlobalSymbol:
111     return Annotation + PrettyName;
112   case EncodeKind::ObjectiveCInstanceVariable:
113     return Annotation + "(ObjC IVar) " + PrettyName;
114   case EncodeKind::ObjectiveCClass:
115     return Annotation + "(ObjC Class) " + PrettyName;
116   case EncodeKind::ObjectiveCClassEHType:
117     return Annotation + "(ObjC Class EH) " + PrettyName;
118   }
119 
120   llvm_unreachable("unexpected case for EncodeKind");
121 }
122 
123 static DylibVerifier::Result updateResult(const DylibVerifier::Result Prev,
124                                           const DylibVerifier::Result Curr) {
125   if (Prev == Curr)
126     return Prev;
127 
128   // Never update from invalid or noverify state.
129   if ((Prev == DylibVerifier::Result::Invalid) ||
130       (Prev == DylibVerifier::Result::NoVerify))
131     return Prev;
132 
133   // Don't let an ignored verification remove a valid one.
134   if (Prev == DylibVerifier::Result::Valid &&
135       Curr == DylibVerifier::Result::Ignore)
136     return Prev;
137 
138   return Curr;
139 }
140 // __private_extern__ is a deprecated specifier that clang does not
141 // respect in all contexts, it should just be considered hidden for InstallAPI.
142 static bool shouldIgnorePrivateExternAttr(const Decl *D) {
143   if (const FunctionDecl *FD = cast<FunctionDecl>(D))
144     return FD->getStorageClass() == StorageClass::SC_PrivateExtern;
145   if (const VarDecl *VD = cast<VarDecl>(D))
146     return VD->getStorageClass() == StorageClass::SC_PrivateExtern;
147 
148   return false;
149 }
150 
151 Record *findRecordFromSlice(const RecordsSlice *Slice, StringRef Name,
152                             EncodeKind Kind) {
153   switch (Kind) {
154   case EncodeKind::GlobalSymbol:
155     return Slice->findGlobal(Name);
156   case EncodeKind::ObjectiveCInstanceVariable:
157     return Slice->findObjCIVar(Name.contains('.'), Name);
158   case EncodeKind::ObjectiveCClass:
159   case EncodeKind::ObjectiveCClassEHType:
160     return Slice->findObjCInterface(Name);
161   }
162   llvm_unreachable("unexpected end when finding record");
163 }
164 
165 void DylibVerifier::updateState(Result State) {
166   Ctx.FrontendState = updateResult(Ctx.FrontendState, State);
167 }
168 
169 void DylibVerifier::addSymbol(const Record *R, SymbolContext &SymCtx,
170                               TargetList &&Targets) {
171   if (Targets.empty())
172     Targets = {Ctx.Target};
173 
174   Exports->addGlobal(SymCtx.Kind, SymCtx.SymbolName, R->getFlags(), Targets);
175 }
176 
177 bool DylibVerifier::shouldIgnoreObsolete(const Record *R, SymbolContext &SymCtx,
178                                          const Record *DR) {
179   if (!SymCtx.FA->Avail.isObsoleted())
180     return false;
181 
182   if (Zippered)
183     DeferredZipperedSymbols[SymCtx.SymbolName].emplace_back(ZipperedDeclSource{
184         SymCtx.FA, &Ctx.Diag->getSourceManager(), Ctx.Target});
185   return true;
186 }
187 
188 bool DylibVerifier::shouldIgnoreReexport(const Record *R,
189                                          SymbolContext &SymCtx) const {
190   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 
204 bool 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 
213 bool 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 
226 bool 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 
294 DylibVerifier::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 
319     if (Mode == VerificationMode::ErrorsOnly)
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;
330     if (Mode == VerificationMode::ErrorsAndWarnings) {
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 
349 DylibVerifier::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) {
361   case VerificationMode::ErrorsAndWarnings:
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;
367   case VerificationMode::Pedantic:
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;
373   case VerificationMode::ErrorsOnly:
374     return Result::Ignore;
375   case VerificationMode::Invalid:
376     llvm_unreachable("Unexpected verification mode symbol verification");
377   }
378   llvm_unreachable("Unexpected verification mode symbol verification");
379 }
380 
381 bool 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 
416 DylibVerifier::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 
486 bool DylibVerifier::canVerify() {
487   return Ctx.FrontendState != Result::NoVerify;
488 }
489 
490 void 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 
504 void DylibVerifier::setTarget(const Target &T) {
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 
515 void DylibVerifier::setSourceManager(
516     IntrusiveRefCntPtr<SourceManager> SourceMgr) {
517   if (!Ctx.Diag)
518     return;
519   SourceManagers.push_back(std::move(SourceMgr));
520   Ctx.Diag->setSourceManager(SourceManagers.back().get());
521 }
522 
523 DylibVerifier::Result DylibVerifier::verify(ObjCIVarRecord *R,
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 
535 static ObjCIFSymbolKind assignObjCIFSymbolKind(const ObjCInterfaceRecord *R) {
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 
548 DylibVerifier::Result DylibVerifier::verify(ObjCInterfaceRecord *R,
549                                             const FrontendAttrs *FA) {
550   if (R->isVerified())
551     return getState();
552   SymbolContext SymCtx;
553   SymCtx.SymbolName = R->getName();
554   SymCtx.ObjCIFKind = assignObjCIFSymbolKind(R);
555 
556   SymCtx.Kind = R->hasExceptionAttribute() ? EncodeKind::ObjectiveCClassEHType
557                                            : EncodeKind::ObjectiveCClass;
558   SymCtx.FA = FA;
559 
560   return verifyImpl(R, SymCtx);
561 }
562 
563 DylibVerifier::Result DylibVerifier::verify(GlobalRecord *R,
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 
578 void DylibVerifier::VerifierContext::emitDiag(llvm::function_ref<void()> Report,
579                                               RecordLoc *Loc) {
580   if (!DiscoveredFirstError) {
581     Diag->Report(diag::warn_target)
582         << (PrintArch ? getArchitectureName(Target.Arch)
583                       : getTargetTripleName(Target));
584     DiscoveredFirstError = true;
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.
597 static bool shouldIgnoreCpp(StringRef Name, bool IsWeakDef) {
598   return (IsWeakDef &&
599           (Name.starts_with("__ZTI") || Name.starts_with("__ZTS")));
600 }
601 void 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 
637     ZipperedDeclSources Locs;
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   // All checks at this point classify as some kind of violation.
678   // The different verification modes dictate whether they are reported to the
679   // user.
680   if (IsLinkerSymbol || (Mode > VerificationMode::ErrorsOnly))
681     accumulateSrcLocForDylibSymbols();
682   RecordLoc Loc = DWARFCtx->SourceLocs.lookup(SymCtx.SymbolName);
683 
684   // Regardless of verification mode, error out on mismatched special linker
685   // symbols.
686   if (IsLinkerSymbol) {
687     Ctx.emitDiag(
688         [&]() {
689           Ctx.Diag->Report(diag::err_header_symbol_missing)
690               << getAnnotatedName(&R, SymCtx, Loc.isValid());
691         },
692         &Loc);
693     updateState(Result::Invalid);
694     return;
695   }
696 
697   // Missing declarations for exported symbols are hard errors on Pedantic mode.
698   if (Mode == VerificationMode::Pedantic) {
699     Ctx.emitDiag(
700         [&]() {
701           Ctx.Diag->Report(diag::err_header_symbol_missing)
702               << getAnnotatedName(&R, SymCtx, Loc.isValid());
703         },
704         &Loc);
705     updateState(Result::Invalid);
706     return;
707   }
708 
709   // Missing declarations for exported symbols are warnings on ErrorsAndWarnings
710   // mode.
711   if (Mode == VerificationMode::ErrorsAndWarnings) {
712     Ctx.emitDiag(
713         [&]() {
714           Ctx.Diag->Report(diag::warn_header_symbol_missing)
715               << getAnnotatedName(&R, SymCtx, Loc.isValid());
716         },
717         &Loc);
718     updateState(Result::Ignore);
719     return;
720   }
721 
722   // Missing declarations are dropped for ErrorsOnly mode. It is the last
723   // remaining mode.
724   updateState(Result::Ignore);
725   return;
726 }
727 
728 void DylibVerifier::visitGlobal(const GlobalRecord &R) {
729   SymbolContext SymCtx;
730   SimpleSymbol Sym = parseSymbol(R.getName());
731   SymCtx.SymbolName = Sym.Name;
732   SymCtx.Kind = Sym.Kind;
733   visitSymbolInDylib(R, SymCtx);
734 }
735 
736 void DylibVerifier::visitObjCIVar(const ObjCIVarRecord &R,
737                                   const StringRef Super) {
738   SymbolContext SymCtx;
739   SymCtx.SymbolName = ObjCIVarRecord::createScopedName(Super, R.getName());
740   SymCtx.Kind = EncodeKind::ObjectiveCInstanceVariable;
741   visitSymbolInDylib(R, SymCtx);
742 }
743 
744 void DylibVerifier::accumulateSrcLocForDylibSymbols() {
745   if (DSYMPath.empty())
746     return;
747 
748   assert(DWARFCtx != nullptr && "Expected an initialized DWARFContext");
749   if (DWARFCtx->ParsedDSYM)
750     return;
751   DWARFCtx->ParsedDSYM = true;
752   DWARFCtx->SourceLocs =
753       DylibReader::accumulateSourceLocFromDSYM(DSYMPath, Ctx.Target);
754 }
755 
756 void DylibVerifier::visitObjCInterface(const ObjCInterfaceRecord &R) {
757   SymbolContext SymCtx;
758   SymCtx.SymbolName = R.getName();
759   SymCtx.ObjCIFKind = assignObjCIFSymbolKind(&R);
760   if (SymCtx.ObjCIFKind > ObjCIFSymbolKind::EHType) {
761     if (R.hasExceptionAttribute()) {
762       SymCtx.Kind = EncodeKind::ObjectiveCClassEHType;
763       visitSymbolInDylib(R, SymCtx);
764     }
765     SymCtx.Kind = EncodeKind::ObjectiveCClass;
766     visitSymbolInDylib(R, SymCtx);
767   } else {
768     SymCtx.Kind = R.hasExceptionAttribute() ? EncodeKind::ObjectiveCClassEHType
769                                             : EncodeKind::ObjectiveCClass;
770     visitSymbolInDylib(R, SymCtx);
771   }
772 
773   for (const ObjCIVarRecord *IV : R.getObjCIVars())
774     visitObjCIVar(*IV, R.getName());
775 }
776 
777 void DylibVerifier::visitObjCCategory(const ObjCCategoryRecord &R) {
778   for (const ObjCIVarRecord *IV : R.getObjCIVars())
779     visitObjCIVar(*IV, R.getSuperClassName());
780 }
781 
782 DylibVerifier::Result DylibVerifier::verifyRemainingSymbols() {
783   if (getState() == Result::NoVerify)
784     return Result::NoVerify;
785   assert(!Dylib.empty() && "No binary to verify against");
786 
787   DWARFContext DWARFInfo;
788   DWARFCtx = &DWARFInfo;
789   Ctx.Target = Target(Architecture::AK_unknown, PlatformType::PLATFORM_UNKNOWN);
790   for (std::shared_ptr<RecordsSlice> Slice : Dylib) {
791     if (Ctx.Target.Arch == Slice->getTarget().Arch)
792       continue;
793     Ctx.DiscoveredFirstError = false;
794     Ctx.PrintArch = true;
795     Ctx.Target = Slice->getTarget();
796     Ctx.DylibSlice = Slice.get();
797     Slice->visit(*this);
798   }
799   return getState();
800 }
801 
802 bool DylibVerifier::verifyBinaryAttrs(const ArrayRef<Target> ProvidedTargets,
803                                       const BinaryAttrs &ProvidedBA,
804                                       const LibAttrs &ProvidedReexports,
805                                       const LibAttrs &ProvidedClients,
806                                       const LibAttrs &ProvidedRPaths,
807                                       const FileType &FT) {
808   assert(!Dylib.empty() && "Need dylib to verify.");
809 
810   // Pickup any load commands that can differ per slice to compare.
811   TargetList DylibTargets;
812   LibAttrs DylibReexports;
813   LibAttrs DylibClients;
814   LibAttrs DylibRPaths;
815   for (const std::shared_ptr<RecordsSlice> &RS : Dylib) {
816     DylibTargets.push_back(RS->getTarget());
817     const BinaryAttrs &BinInfo = RS->getBinaryAttrs();
818     for (const StringRef LibName : BinInfo.RexportedLibraries)
819       DylibReexports[LibName].set(DylibTargets.back().Arch);
820     for (const StringRef LibName : BinInfo.AllowableClients)
821       DylibClients[LibName].set(DylibTargets.back().Arch);
822     // Compare attributes that are only representable in >= TBD_V5.
823     if (FT >= FileType::TBD_V5)
824       for (const StringRef Name : BinInfo.RPaths)
825         DylibRPaths[Name].set(DylibTargets.back().Arch);
826   }
827 
828   // Check targets first.
829   ArchitectureSet ProvidedArchs = mapToArchitectureSet(ProvidedTargets);
830   ArchitectureSet DylibArchs = mapToArchitectureSet(DylibTargets);
831   if (ProvidedArchs != DylibArchs) {
832     Ctx.Diag->Report(diag::err_architecture_mismatch)
833         << ProvidedArchs << DylibArchs;
834     return false;
835   }
836   auto ProvidedPlatforms = mapToPlatformVersionSet(ProvidedTargets);
837   auto DylibPlatforms = mapToPlatformVersionSet(DylibTargets);
838   if (ProvidedPlatforms != DylibPlatforms) {
839     const bool DiffMinOS =
840         mapToPlatformSet(ProvidedTargets) == mapToPlatformSet(DylibTargets);
841     if (DiffMinOS)
842       Ctx.Diag->Report(diag::warn_platform_mismatch)
843           << ProvidedPlatforms << DylibPlatforms;
844     else {
845       Ctx.Diag->Report(diag::err_platform_mismatch)
846           << ProvidedPlatforms << DylibPlatforms;
847       return false;
848     }
849   }
850 
851   // Because InstallAPI requires certain attributes to match across architecture
852   // slices, take the first one to compare those with.
853   const BinaryAttrs &DylibBA = (*Dylib.begin())->getBinaryAttrs();
854 
855   if (ProvidedBA.InstallName != DylibBA.InstallName) {
856     Ctx.Diag->Report(diag::err_install_name_mismatch)
857         << ProvidedBA.InstallName << DylibBA.InstallName;
858     return false;
859   }
860 
861   if (ProvidedBA.CurrentVersion != DylibBA.CurrentVersion) {
862     Ctx.Diag->Report(diag::err_current_version_mismatch)
863         << ProvidedBA.CurrentVersion << DylibBA.CurrentVersion;
864     return false;
865   }
866 
867   if (ProvidedBA.CompatVersion != DylibBA.CompatVersion) {
868     Ctx.Diag->Report(diag::err_compatibility_version_mismatch)
869         << ProvidedBA.CompatVersion << DylibBA.CompatVersion;
870     return false;
871   }
872 
873   if (ProvidedBA.AppExtensionSafe != DylibBA.AppExtensionSafe) {
874     Ctx.Diag->Report(diag::err_appextension_safe_mismatch)
875         << (ProvidedBA.AppExtensionSafe ? "true" : "false")
876         << (DylibBA.AppExtensionSafe ? "true" : "false");
877     return false;
878   }
879 
880   if (!DylibBA.TwoLevelNamespace) {
881     Ctx.Diag->Report(diag::err_no_twolevel_namespace);
882     return false;
883   }
884 
885   if (ProvidedBA.OSLibNotForSharedCache != DylibBA.OSLibNotForSharedCache) {
886     Ctx.Diag->Report(diag::err_shared_cache_eligiblity_mismatch)
887         << (ProvidedBA.OSLibNotForSharedCache ? "true" : "false")
888         << (DylibBA.OSLibNotForSharedCache ? "true" : "false");
889     return false;
890   }
891 
892   if (ProvidedBA.ParentUmbrella.empty() && !DylibBA.ParentUmbrella.empty()) {
893     Ctx.Diag->Report(diag::err_parent_umbrella_missing)
894         << "installAPI option" << DylibBA.ParentUmbrella;
895     return false;
896   }
897 
898   if (!ProvidedBA.ParentUmbrella.empty() && DylibBA.ParentUmbrella.empty()) {
899     Ctx.Diag->Report(diag::err_parent_umbrella_missing)
900         << "binary file" << ProvidedBA.ParentUmbrella;
901     return false;
902   }
903 
904   if ((!ProvidedBA.ParentUmbrella.empty()) &&
905       (ProvidedBA.ParentUmbrella != DylibBA.ParentUmbrella)) {
906     Ctx.Diag->Report(diag::err_parent_umbrella_mismatch)
907         << ProvidedBA.ParentUmbrella << DylibBA.ParentUmbrella;
908     return false;
909   }
910 
911   auto CompareLibraries = [&](const LibAttrs &Provided, const LibAttrs &Dylib,
912                               unsigned DiagID_missing, unsigned DiagID_mismatch,
913                               bool Fatal = true) {
914     if (Provided == Dylib)
915       return true;
916 
917     for (const llvm::StringMapEntry<ArchitectureSet> &PAttr : Provided) {
918       const auto DAttrIt = Dylib.find(PAttr.getKey());
919       if (DAttrIt == Dylib.end()) {
920         Ctx.Diag->Report(DiagID_missing) << "binary file" << PAttr;
921         if (Fatal)
922           return false;
923       }
924 
925       if (PAttr.getValue() != DAttrIt->getValue()) {
926         Ctx.Diag->Report(DiagID_mismatch) << PAttr << *DAttrIt;
927         if (Fatal)
928           return false;
929       }
930     }
931 
932     for (const llvm::StringMapEntry<ArchitectureSet> &DAttr : Dylib) {
933       const auto PAttrIt = Provided.find(DAttr.getKey());
934       if (PAttrIt == Provided.end()) {
935         Ctx.Diag->Report(DiagID_missing) << "installAPI option" << DAttr;
936         if (!Fatal)
937           continue;
938         return false;
939       }
940 
941       if (PAttrIt->getValue() != DAttr.getValue()) {
942         if (Fatal)
943           llvm_unreachable("this case was already covered above.");
944       }
945     }
946     return true;
947   };
948 
949   if (!CompareLibraries(ProvidedReexports, DylibReexports,
950                         diag::err_reexported_libraries_missing,
951                         diag::err_reexported_libraries_mismatch))
952     return false;
953 
954   if (!CompareLibraries(ProvidedClients, DylibClients,
955                         diag::err_allowable_clients_missing,
956                         diag::err_allowable_clients_mismatch))
957     return false;
958 
959   if (FT >= FileType::TBD_V5) {
960     // Ignore rpath differences if building an asan variant, since the
961     //   compiler injects additional paths.
962     // FIXME: Building with sanitizers does not always change the install
963     //   name, so this is not a foolproof solution.
964     if (!ProvidedBA.InstallName.ends_with("_asan")) {
965       if (!CompareLibraries(ProvidedRPaths, DylibRPaths,
966                             diag::warn_rpaths_missing,
967                             diag::warn_rpaths_mismatch,
968                             /*Fatal=*/false))
969         return true;
970     }
971   }
972 
973   return true;
974 }
975 
976 } // namespace installapi
977 } // namespace clang
978