xref: /freebsd/contrib/llvm-project/clang/lib/InstallAPI/DylibVerifier.cpp (revision 770cf0a5f02dc8983a89c6568d741fbc25baa999)
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 ArchitectureSet &LibAttrs::getArchSet(StringRef Attr) {
22   auto *It = llvm::find_if(LibraryAttributes, [&Attr](const auto &Input) {
23     return Attr == Input.first;
24   });
25   if (It != LibraryAttributes.end())
26     return It->second;
27   LibraryAttributes.push_back({Attr.str(), ArchitectureSet()});
28   return LibraryAttributes.back().second;
29 }
30 
31 std::optional<LibAttrs::Entry> LibAttrs::find(StringRef Attr) const {
32   auto *It = llvm::find_if(LibraryAttributes, [&Attr](const auto &Input) {
33     return Attr == Input.first;
34   });
35   if (It == LibraryAttributes.end())
36     return std::nullopt;
37   return *It;
38 }
39 
40 /// Metadata stored about a mapping of a declaration to a symbol.
41 struct DylibVerifier::SymbolContext {
42   // Name to use for all querying and verification
43   // purposes.
44   std::string SymbolName{""};
45 
46   // Kind to map symbol type against record.
47   EncodeKind Kind = EncodeKind::GlobalSymbol;
48 
49   // Frontend Attributes tied to the AST.
50   const FrontendAttrs *FA = nullptr;
51 
52   // The ObjCInterface symbol type, if applicable.
53   ObjCIFSymbolKind ObjCIFKind = ObjCIFSymbolKind::None;
54 
55   // Whether Decl is inlined.
56   bool Inlined = false;
57 };
58 
59 struct DylibVerifier::DWARFContext {
60   // Track whether DSYM parsing has already been attempted to avoid re-parsing.
61   bool ParsedDSYM{false};
62 
63   // Lookup table for source locations by symbol name.
64   DylibReader::SymbolToSourceLocMap SourceLocs{};
65 };
66 
67 static bool isCppMangled(StringRef Name) {
68   // InstallAPI currently only supports itanium manglings.
69   return (Name.starts_with("_Z") || Name.starts_with("__Z") ||
70           Name.starts_with("___Z"));
71 }
72 
73 static std::string demangle(StringRef Name) {
74   // InstallAPI currently only supports itanium manglings.
75   if (!isCppMangled(Name))
76     return Name.str();
77   char *Result = llvm::itaniumDemangle(Name);
78   if (!Result)
79     return Name.str();
80 
81   std::string Demangled(Result);
82   free(Result);
83   return Demangled;
84 }
85 
86 std::string DylibVerifier::getAnnotatedName(const Record *R,
87                                             SymbolContext &SymCtx,
88                                             bool ValidSourceLoc) {
89   assert(!SymCtx.SymbolName.empty() && "Expected symbol name");
90 
91   const StringRef SymbolName = SymCtx.SymbolName;
92   std::string PrettyName =
93       (Demangle && (SymCtx.Kind == EncodeKind::GlobalSymbol))
94           ? demangle(SymbolName)
95           : SymbolName.str();
96 
97   std::string Annotation;
98   if (R->isWeakDefined())
99     Annotation += "(weak-def) ";
100   if (R->isWeakReferenced())
101     Annotation += "(weak-ref) ";
102   if (R->isThreadLocalValue())
103     Annotation += "(tlv) ";
104 
105   // Check if symbol represents only part of a @interface declaration.
106   switch (SymCtx.ObjCIFKind) {
107   default:
108     break;
109   case ObjCIFSymbolKind::EHType:
110     return Annotation + "Exception Type of " + PrettyName;
111   case ObjCIFSymbolKind::MetaClass:
112     return Annotation + "Metaclass of " + PrettyName;
113   case ObjCIFSymbolKind::Class:
114     return Annotation + "Class of " + PrettyName;
115   }
116 
117   // Only print symbol type prefix or leading "_" if there is no source location
118   // tied to it. This can only ever happen when the location has to come from
119   // debug info.
120   if (ValidSourceLoc) {
121     StringRef PrettyNameRef(PrettyName);
122     if ((SymCtx.Kind == EncodeKind::GlobalSymbol) &&
123         !isCppMangled(SymbolName) && PrettyNameRef.starts_with("_"))
124       return Annotation + PrettyNameRef.drop_front(1).str();
125     return Annotation + PrettyName;
126   }
127 
128   switch (SymCtx.Kind) {
129   case EncodeKind::GlobalSymbol:
130     return Annotation + PrettyName;
131   case EncodeKind::ObjectiveCInstanceVariable:
132     return Annotation + "(ObjC IVar) " + PrettyName;
133   case EncodeKind::ObjectiveCClass:
134     return Annotation + "(ObjC Class) " + PrettyName;
135   case EncodeKind::ObjectiveCClassEHType:
136     return Annotation + "(ObjC Class EH) " + PrettyName;
137   }
138 
139   llvm_unreachable("unexpected case for EncodeKind");
140 }
141 
142 static DylibVerifier::Result updateResult(const DylibVerifier::Result Prev,
143                                           const DylibVerifier::Result Curr) {
144   if (Prev == Curr)
145     return Prev;
146 
147   // Never update from invalid or noverify state.
148   if ((Prev == DylibVerifier::Result::Invalid) ||
149       (Prev == DylibVerifier::Result::NoVerify))
150     return Prev;
151 
152   // Don't let an ignored verification remove a valid one.
153   if (Prev == DylibVerifier::Result::Valid &&
154       Curr == DylibVerifier::Result::Ignore)
155     return Prev;
156 
157   return Curr;
158 }
159 // __private_extern__ is a deprecated specifier that clang does not
160 // respect in all contexts, it should just be considered hidden for InstallAPI.
161 static bool shouldIgnorePrivateExternAttr(const Decl *D) {
162   if (const FunctionDecl *FD = cast<FunctionDecl>(D))
163     return FD->getStorageClass() == StorageClass::SC_PrivateExtern;
164   if (const VarDecl *VD = cast<VarDecl>(D))
165     return VD->getStorageClass() == StorageClass::SC_PrivateExtern;
166 
167   return false;
168 }
169 
170 Record *findRecordFromSlice(const RecordsSlice *Slice, StringRef Name,
171                             EncodeKind Kind) {
172   switch (Kind) {
173   case EncodeKind::GlobalSymbol:
174     return Slice->findGlobal(Name);
175   case EncodeKind::ObjectiveCInstanceVariable:
176     return Slice->findObjCIVar(Name.contains('.'), Name);
177   case EncodeKind::ObjectiveCClass:
178   case EncodeKind::ObjectiveCClassEHType:
179     return Slice->findObjCInterface(Name);
180   }
181   llvm_unreachable("unexpected end when finding record");
182 }
183 
184 void DylibVerifier::updateState(Result State) {
185   Ctx.FrontendState = updateResult(Ctx.FrontendState, State);
186 }
187 
188 void DylibVerifier::addSymbol(const Record *R, SymbolContext &SymCtx,
189                               TargetList &&Targets) {
190   if (Targets.empty())
191     Targets = {Ctx.Target};
192 
193   Exports->addGlobal(SymCtx.Kind, SymCtx.SymbolName, R->getFlags(), Targets);
194 }
195 
196 bool DylibVerifier::shouldIgnoreObsolete(const Record *R, SymbolContext &SymCtx,
197                                          const Record *DR) {
198   if (!SymCtx.FA->Avail.isObsoleted())
199     return false;
200 
201   if (Zippered)
202     DeferredZipperedSymbols[SymCtx.SymbolName].emplace_back(ZipperedDeclSource{
203         SymCtx.FA, &Ctx.Diag->getSourceManager(), Ctx.Target});
204   return true;
205 }
206 
207 bool DylibVerifier::shouldIgnoreReexport(const Record *R,
208                                          SymbolContext &SymCtx) const {
209   StringRef SymName = SymCtx.SymbolName;
210   // Linker directive symbols can never be ignored.
211   if (SymName.starts_with("$ld$"))
212     return false;
213 
214   if (Reexports.empty())
215     return false;
216 
217   for (const InterfaceFile &Lib : Reexports) {
218     if (!Lib.hasTarget(Ctx.Target))
219       continue;
220     if (auto Sym = Lib.getSymbol(SymCtx.Kind, SymName, SymCtx.ObjCIFKind))
221       if ((*Sym)->hasTarget(Ctx.Target))
222         return true;
223   }
224   return false;
225 }
226 
227 bool DylibVerifier::shouldIgnoreInternalZipperedSymbol(
228     const Record *R, const SymbolContext &SymCtx) const {
229   if (!Zippered)
230     return false;
231 
232   return Exports->findSymbol(SymCtx.Kind, SymCtx.SymbolName,
233                              SymCtx.ObjCIFKind) != nullptr;
234 }
235 
236 bool DylibVerifier::shouldIgnoreZipperedAvailability(const Record *R,
237                                                      SymbolContext &SymCtx) {
238   if (!(Zippered && SymCtx.FA->Avail.isUnavailable()))
239     return false;
240 
241   // Collect source location incase there is an exported symbol to diagnose
242   // during `verifyRemainingSymbols`.
243   DeferredZipperedSymbols[SymCtx.SymbolName].emplace_back(
244       ZipperedDeclSource{SymCtx.FA, SourceManagers.back().get(), Ctx.Target});
245 
246   return true;
247 }
248 
249 bool DylibVerifier::compareObjCInterfaceSymbols(const Record *R,
250                                                 SymbolContext &SymCtx,
251                                                 const ObjCInterfaceRecord *DR) {
252   const bool IsDeclVersionComplete =
253       ((SymCtx.ObjCIFKind & ObjCIFSymbolKind::Class) ==
254        ObjCIFSymbolKind::Class) &&
255       ((SymCtx.ObjCIFKind & ObjCIFSymbolKind::MetaClass) ==
256        ObjCIFSymbolKind::MetaClass);
257 
258   const bool IsDylibVersionComplete = DR->isCompleteInterface();
259 
260   // The common case, a complete ObjCInterface.
261   if (IsDeclVersionComplete && IsDylibVersionComplete)
262     return true;
263 
264   auto PrintDiagnostic = [&](auto SymLinkage, const Record *Record,
265                              StringRef SymName, bool PrintAsWarning = false) {
266     if (SymLinkage == RecordLinkage::Unknown)
267       Ctx.emitDiag([&]() {
268         Ctx.Diag->Report(SymCtx.FA->Loc, PrintAsWarning
269                                              ? diag::warn_library_missing_symbol
270                                              : diag::err_library_missing_symbol)
271             << SymName;
272       });
273     else
274       Ctx.emitDiag([&]() {
275         Ctx.Diag->Report(SymCtx.FA->Loc, PrintAsWarning
276                                              ? diag::warn_library_hidden_symbol
277                                              : diag::err_library_hidden_symbol)
278             << SymName;
279       });
280   };
281 
282   if (IsDeclVersionComplete) {
283     // The decl represents a complete ObjCInterface, but the symbols in the
284     // dylib do not. Determine which symbol is missing. To keep older projects
285     // building, treat this as a warning.
286     if (!DR->isExportedSymbol(ObjCIFSymbolKind::Class)) {
287       SymCtx.ObjCIFKind = ObjCIFSymbolKind::Class;
288       PrintDiagnostic(DR->getLinkageForSymbol(ObjCIFSymbolKind::Class), R,
289                       getAnnotatedName(R, SymCtx),
290                       /*PrintAsWarning=*/true);
291     }
292     if (!DR->isExportedSymbol(ObjCIFSymbolKind::MetaClass)) {
293       SymCtx.ObjCIFKind = ObjCIFSymbolKind::MetaClass;
294       PrintDiagnostic(DR->getLinkageForSymbol(ObjCIFSymbolKind::MetaClass), R,
295                       getAnnotatedName(R, SymCtx),
296                       /*PrintAsWarning=*/true);
297     }
298     return true;
299   }
300 
301   if (DR->isExportedSymbol(SymCtx.ObjCIFKind)) {
302     if (!IsDylibVersionComplete) {
303       // Both the declaration and dylib have a non-complete interface.
304       SymCtx.Kind = EncodeKind::GlobalSymbol;
305       SymCtx.SymbolName = R->getName();
306     }
307     return true;
308   }
309 
310   // At this point that means there was not a matching class symbol
311   // to represent the one discovered as a declaration.
312   PrintDiagnostic(DR->getLinkageForSymbol(SymCtx.ObjCIFKind), R,
313                   SymCtx.SymbolName);
314   return false;
315 }
316 
317 DylibVerifier::Result DylibVerifier::compareVisibility(const Record *R,
318                                                        SymbolContext &SymCtx,
319                                                        const Record *DR) {
320 
321   if (R->isExported()) {
322     if (!DR) {
323       Ctx.emitDiag([&]() {
324         Ctx.Diag->Report(SymCtx.FA->Loc, diag::err_library_missing_symbol)
325             << getAnnotatedName(R, SymCtx);
326       });
327       return Result::Invalid;
328     }
329     if (DR->isInternal()) {
330       Ctx.emitDiag([&]() {
331         Ctx.Diag->Report(SymCtx.FA->Loc, diag::err_library_hidden_symbol)
332             << getAnnotatedName(R, SymCtx);
333       });
334       return Result::Invalid;
335     }
336   }
337 
338   // Emit a diagnostic for hidden declarations with external symbols, except
339   // when theres an inlined attribute.
340   if ((R->isInternal() && !SymCtx.Inlined) && DR && DR->isExported()) {
341 
342     if (Mode == VerificationMode::ErrorsOnly)
343       return Result::Ignore;
344 
345     if (shouldIgnorePrivateExternAttr(SymCtx.FA->D))
346       return Result::Ignore;
347 
348     if (shouldIgnoreInternalZipperedSymbol(R, SymCtx))
349       return Result::Ignore;
350 
351     unsigned ID;
352     Result Outcome;
353     if (Mode == VerificationMode::ErrorsAndWarnings) {
354       ID = diag::warn_header_hidden_symbol;
355       Outcome = Result::Ignore;
356     } else {
357       ID = diag::err_header_hidden_symbol;
358       Outcome = Result::Invalid;
359     }
360     Ctx.emitDiag([&]() {
361       Ctx.Diag->Report(SymCtx.FA->Loc, ID) << getAnnotatedName(R, SymCtx);
362     });
363     return Outcome;
364   }
365 
366   if (R->isInternal())
367     return Result::Ignore;
368 
369   return Result::Valid;
370 }
371 
372 DylibVerifier::Result DylibVerifier::compareAvailability(const Record *R,
373                                                          SymbolContext &SymCtx,
374                                                          const Record *DR) {
375   if (!SymCtx.FA->Avail.isUnavailable())
376     return Result::Valid;
377 
378   if (shouldIgnoreZipperedAvailability(R, SymCtx))
379     return Result::Ignore;
380 
381   const bool IsDeclAvailable = SymCtx.FA->Avail.isUnavailable();
382 
383   switch (Mode) {
384   case VerificationMode::ErrorsAndWarnings:
385     Ctx.emitDiag([&]() {
386       Ctx.Diag->Report(SymCtx.FA->Loc, diag::warn_header_availability_mismatch)
387           << getAnnotatedName(R, SymCtx) << IsDeclAvailable << IsDeclAvailable;
388     });
389     return Result::Ignore;
390   case VerificationMode::Pedantic:
391     Ctx.emitDiag([&]() {
392       Ctx.Diag->Report(SymCtx.FA->Loc, diag::err_header_availability_mismatch)
393           << getAnnotatedName(R, SymCtx) << IsDeclAvailable << IsDeclAvailable;
394     });
395     return Result::Invalid;
396   case VerificationMode::ErrorsOnly:
397     return Result::Ignore;
398   case VerificationMode::Invalid:
399     llvm_unreachable("Unexpected verification mode symbol verification");
400   }
401   llvm_unreachable("Unexpected verification mode symbol verification");
402 }
403 
404 bool DylibVerifier::compareSymbolFlags(const Record *R, SymbolContext &SymCtx,
405                                        const Record *DR) {
406   if (DR->isThreadLocalValue() && !R->isThreadLocalValue()) {
407     Ctx.emitDiag([&]() {
408       Ctx.Diag->Report(SymCtx.FA->Loc, diag::err_dylib_symbol_flags_mismatch)
409           << getAnnotatedName(DR, SymCtx) << DR->isThreadLocalValue();
410     });
411     return false;
412   }
413   if (!DR->isThreadLocalValue() && R->isThreadLocalValue()) {
414     Ctx.emitDiag([&]() {
415       Ctx.Diag->Report(SymCtx.FA->Loc, diag::err_header_symbol_flags_mismatch)
416           << getAnnotatedName(R, SymCtx) << R->isThreadLocalValue();
417     });
418     return false;
419   }
420 
421   if (DR->isWeakDefined() && !R->isWeakDefined()) {
422     Ctx.emitDiag([&]() {
423       Ctx.Diag->Report(SymCtx.FA->Loc, diag::err_dylib_symbol_flags_mismatch)
424           << getAnnotatedName(DR, SymCtx) << R->isWeakDefined();
425     });
426     return false;
427   }
428   if (!DR->isWeakDefined() && R->isWeakDefined()) {
429     Ctx.emitDiag([&]() {
430       Ctx.Diag->Report(SymCtx.FA->Loc, diag::err_header_symbol_flags_mismatch)
431           << getAnnotatedName(R, SymCtx) << R->isWeakDefined();
432     });
433     return false;
434   }
435 
436   return true;
437 }
438 
439 DylibVerifier::Result DylibVerifier::verifyImpl(Record *R,
440                                                 SymbolContext &SymCtx) {
441   R->setVerify();
442   if (!canVerify()) {
443     // Accumulate symbols when not in verifying against dylib.
444     if (R->isExported() && !SymCtx.FA->Avail.isUnavailable() &&
445         !SymCtx.FA->Avail.isObsoleted()) {
446       addSymbol(R, SymCtx);
447     }
448     return Ctx.FrontendState;
449   }
450 
451   if (shouldIgnoreReexport(R, SymCtx)) {
452     updateState(Result::Ignore);
453     return Ctx.FrontendState;
454   }
455 
456   Record *DR =
457       findRecordFromSlice(Ctx.DylibSlice, SymCtx.SymbolName, SymCtx.Kind);
458   if (DR)
459     DR->setVerify();
460 
461   if (shouldIgnoreObsolete(R, SymCtx, DR)) {
462     updateState(Result::Ignore);
463     return Ctx.FrontendState;
464   }
465 
466   // Unavailable declarations don't need matching symbols.
467   if (SymCtx.FA->Avail.isUnavailable() && (!DR || DR->isInternal())) {
468     updateState(Result::Valid);
469     return Ctx.FrontendState;
470   }
471 
472   Result VisibilityCheck = compareVisibility(R, SymCtx, DR);
473   if (VisibilityCheck != Result::Valid) {
474     updateState(VisibilityCheck);
475     return Ctx.FrontendState;
476   }
477 
478   // All missing symbol cases to diagnose have been handled now.
479   if (!DR) {
480     updateState(Result::Ignore);
481     return Ctx.FrontendState;
482   }
483 
484   // Check for mismatching ObjC interfaces.
485   if (SymCtx.ObjCIFKind != ObjCIFSymbolKind::None) {
486     if (!compareObjCInterfaceSymbols(
487             R, SymCtx, Ctx.DylibSlice->findObjCInterface(DR->getName()))) {
488       updateState(Result::Invalid);
489       return Ctx.FrontendState;
490     }
491   }
492 
493   Result AvailabilityCheck = compareAvailability(R, SymCtx, DR);
494   if (AvailabilityCheck != Result::Valid) {
495     updateState(AvailabilityCheck);
496     return Ctx.FrontendState;
497   }
498 
499   if (!compareSymbolFlags(R, SymCtx, DR)) {
500     updateState(Result::Invalid);
501     return Ctx.FrontendState;
502   }
503 
504   addSymbol(R, SymCtx);
505   updateState(Result::Valid);
506   return Ctx.FrontendState;
507 }
508 
509 bool DylibVerifier::canVerify() {
510   return Ctx.FrontendState != Result::NoVerify;
511 }
512 
513 void DylibVerifier::assignSlice(const Target &T) {
514   assert(T == Ctx.Target && "Active targets should match.");
515   if (Dylib.empty())
516     return;
517 
518   // Note: there are no reexport slices with binaries, as opposed to TBD files,
519   // so it can be assumed that the target match is the active top-level library.
520   auto It = find_if(
521       Dylib, [&T](const auto &Slice) { return T == Slice->getTarget(); });
522 
523   assert(It != Dylib.end() && "Target slice should always exist.");
524   Ctx.DylibSlice = It->get();
525 }
526 
527 void DylibVerifier::setTarget(const Target &T) {
528   Ctx.Target = T;
529   Ctx.DiscoveredFirstError = false;
530   if (Dylib.empty()) {
531     updateState(Result::NoVerify);
532     return;
533   }
534   updateState(Result::Ignore);
535   assignSlice(T);
536 }
537 
538 void DylibVerifier::setSourceManager(
539     IntrusiveRefCntPtr<SourceManager> SourceMgr) {
540   if (!Ctx.Diag)
541     return;
542   SourceManagers.push_back(std::move(SourceMgr));
543   Ctx.Diag->setSourceManager(SourceManagers.back().get());
544 }
545 
546 DylibVerifier::Result DylibVerifier::verify(ObjCIVarRecord *R,
547                                             const FrontendAttrs *FA,
548                                             const StringRef SuperClass) {
549   if (R->isVerified())
550     return getState();
551 
552   std::string FullName =
553       ObjCIVarRecord::createScopedName(SuperClass, R->getName());
554   SymbolContext SymCtx{FullName, EncodeKind::ObjectiveCInstanceVariable, FA};
555   return verifyImpl(R, SymCtx);
556 }
557 
558 static ObjCIFSymbolKind assignObjCIFSymbolKind(const ObjCInterfaceRecord *R) {
559   ObjCIFSymbolKind Result = ObjCIFSymbolKind::None;
560   if (R->getLinkageForSymbol(ObjCIFSymbolKind::Class) != RecordLinkage::Unknown)
561     Result |= ObjCIFSymbolKind::Class;
562   if (R->getLinkageForSymbol(ObjCIFSymbolKind::MetaClass) !=
563       RecordLinkage::Unknown)
564     Result |= ObjCIFSymbolKind::MetaClass;
565   if (R->getLinkageForSymbol(ObjCIFSymbolKind::EHType) !=
566       RecordLinkage::Unknown)
567     Result |= ObjCIFSymbolKind::EHType;
568   return Result;
569 }
570 
571 DylibVerifier::Result DylibVerifier::verify(ObjCInterfaceRecord *R,
572                                             const FrontendAttrs *FA) {
573   if (R->isVerified())
574     return getState();
575   SymbolContext SymCtx;
576   SymCtx.SymbolName = R->getName();
577   SymCtx.ObjCIFKind = assignObjCIFSymbolKind(R);
578 
579   SymCtx.Kind = R->hasExceptionAttribute() ? EncodeKind::ObjectiveCClassEHType
580                                            : EncodeKind::ObjectiveCClass;
581   SymCtx.FA = FA;
582 
583   return verifyImpl(R, SymCtx);
584 }
585 
586 DylibVerifier::Result DylibVerifier::verify(GlobalRecord *R,
587                                             const FrontendAttrs *FA) {
588   if (R->isVerified())
589     return getState();
590 
591   // Global classifications could be obfusciated with `asm`.
592   SimpleSymbol Sym = parseSymbol(R->getName());
593   SymbolContext SymCtx;
594   SymCtx.SymbolName = Sym.Name;
595   SymCtx.Kind = Sym.Kind;
596   SymCtx.FA = FA;
597   SymCtx.Inlined = R->isInlined();
598   return verifyImpl(R, SymCtx);
599 }
600 
601 void DylibVerifier::VerifierContext::emitDiag(llvm::function_ref<void()> Report,
602                                               RecordLoc *Loc) {
603   if (!DiscoveredFirstError) {
604     Diag->Report(diag::warn_target)
605         << (PrintArch ? getArchitectureName(Target.Arch)
606                       : getTargetTripleName(Target));
607     DiscoveredFirstError = true;
608   }
609   if (Loc && Loc->isValid())
610     llvm::errs() << Loc->File << ":" << Loc->Line << ":" << 0 << ": ";
611 
612   Report();
613 }
614 
615 // The existence of weak-defined RTTI can not always be inferred from the
616 // header files because they can be generated as part of an implementation
617 // file.
618 // InstallAPI doesn't warn about weak-defined RTTI, because this doesn't affect
619 // static linking and so can be ignored for text-api files.
620 static bool shouldIgnoreCpp(StringRef Name, bool IsWeakDef) {
621   return (IsWeakDef &&
622           (Name.starts_with("__ZTI") || Name.starts_with("__ZTS")));
623 }
624 void DylibVerifier::visitSymbolInDylib(const Record &R, SymbolContext &SymCtx) {
625   // Undefined symbols should not be in InstallAPI generated text-api files.
626   if (R.isUndefined()) {
627     updateState(Result::Valid);
628     return;
629   }
630 
631   // Internal symbols should not be in InstallAPI generated text-api files.
632   if (R.isInternal()) {
633     updateState(Result::Valid);
634     return;
635   }
636 
637   // Allow zippered symbols with potentially mismatching availability
638   // between macOS and macCatalyst in the final text-api file.
639   const StringRef SymbolName(SymCtx.SymbolName);
640   if (const Symbol *Sym = Exports->findSymbol(SymCtx.Kind, SymCtx.SymbolName,
641                                               SymCtx.ObjCIFKind)) {
642     if (Sym->hasArchitecture(Ctx.Target.Arch)) {
643       updateState(Result::Ignore);
644       return;
645     }
646   }
647 
648   const bool IsLinkerSymbol = SymbolName.starts_with("$ld$");
649 
650   if (R.isVerified()) {
651     // Check for unavailable symbols.
652     // This should only occur in the zippered case where we ignored
653     // availability until all headers have been parsed.
654     auto It = DeferredZipperedSymbols.find(SymCtx.SymbolName);
655     if (It == DeferredZipperedSymbols.end()) {
656       updateState(Result::Valid);
657       return;
658     }
659 
660     ZipperedDeclSources Locs;
661     for (const ZipperedDeclSource &ZSource : It->second) {
662       if (ZSource.FA->Avail.isObsoleted()) {
663         updateState(Result::Ignore);
664         return;
665       }
666       if (ZSource.T.Arch != Ctx.Target.Arch)
667         continue;
668       Locs.emplace_back(ZSource);
669     }
670     assert(Locs.size() == 2 && "Expected two decls for zippered symbol");
671 
672     // Print violating declarations per platform.
673     for (const ZipperedDeclSource &ZSource : Locs) {
674       unsigned DiagID = 0;
675       if (Mode == VerificationMode::Pedantic || IsLinkerSymbol) {
676         updateState(Result::Invalid);
677         DiagID = diag::err_header_availability_mismatch;
678       } else if (Mode == VerificationMode::ErrorsAndWarnings) {
679         updateState(Result::Ignore);
680         DiagID = diag::warn_header_availability_mismatch;
681       } else {
682         updateState(Result::Ignore);
683         return;
684       }
685       // Bypass emitDiag banner and print the target everytime.
686       Ctx.Diag->setSourceManager(ZSource.SrcMgr);
687       Ctx.Diag->Report(diag::warn_target) << getTargetTripleName(ZSource.T);
688       Ctx.Diag->Report(ZSource.FA->Loc, DiagID)
689           << getAnnotatedName(&R, SymCtx) << ZSource.FA->Avail.isUnavailable()
690           << ZSource.FA->Avail.isUnavailable();
691     }
692     return;
693   }
694 
695   if (shouldIgnoreCpp(SymbolName, R.isWeakDefined())) {
696     updateState(Result::Valid);
697     return;
698   }
699 
700   if (Aliases.count({SymbolName.str(), SymCtx.Kind})) {
701     updateState(Result::Valid);
702     return;
703   }
704 
705   // All checks at this point classify as some kind of violation.
706   // The different verification modes dictate whether they are reported to the
707   // user.
708   if (IsLinkerSymbol || (Mode > VerificationMode::ErrorsOnly))
709     accumulateSrcLocForDylibSymbols();
710   RecordLoc Loc = DWARFCtx->SourceLocs.lookup(SymCtx.SymbolName);
711 
712   // Regardless of verification mode, error out on mismatched special linker
713   // symbols.
714   if (IsLinkerSymbol) {
715     Ctx.emitDiag(
716         [&]() {
717           Ctx.Diag->Report(diag::err_header_symbol_missing)
718               << getAnnotatedName(&R, SymCtx, Loc.isValid());
719         },
720         &Loc);
721     updateState(Result::Invalid);
722     return;
723   }
724 
725   // Missing declarations for exported symbols are hard errors on Pedantic mode.
726   if (Mode == VerificationMode::Pedantic) {
727     Ctx.emitDiag(
728         [&]() {
729           Ctx.Diag->Report(diag::err_header_symbol_missing)
730               << getAnnotatedName(&R, SymCtx, Loc.isValid());
731         },
732         &Loc);
733     updateState(Result::Invalid);
734     return;
735   }
736 
737   // Missing declarations for exported symbols are warnings on ErrorsAndWarnings
738   // mode.
739   if (Mode == VerificationMode::ErrorsAndWarnings) {
740     Ctx.emitDiag(
741         [&]() {
742           Ctx.Diag->Report(diag::warn_header_symbol_missing)
743               << getAnnotatedName(&R, SymCtx, Loc.isValid());
744         },
745         &Loc);
746     updateState(Result::Ignore);
747     return;
748   }
749 
750   // Missing declarations are dropped for ErrorsOnly mode. It is the last
751   // remaining mode.
752   updateState(Result::Ignore);
753 }
754 
755 void DylibVerifier::visitGlobal(const GlobalRecord &R) {
756   SymbolContext SymCtx;
757   SimpleSymbol Sym = parseSymbol(R.getName());
758   SymCtx.SymbolName = Sym.Name;
759   SymCtx.Kind = Sym.Kind;
760   visitSymbolInDylib(R, SymCtx);
761 }
762 
763 void DylibVerifier::visitObjCIVar(const ObjCIVarRecord &R,
764                                   const StringRef Super) {
765   SymbolContext SymCtx;
766   SymCtx.SymbolName = ObjCIVarRecord::createScopedName(Super, R.getName());
767   SymCtx.Kind = EncodeKind::ObjectiveCInstanceVariable;
768   visitSymbolInDylib(R, SymCtx);
769 }
770 
771 void DylibVerifier::accumulateSrcLocForDylibSymbols() {
772   if (DSYMPath.empty())
773     return;
774 
775   assert(DWARFCtx != nullptr && "Expected an initialized DWARFContext");
776   if (DWARFCtx->ParsedDSYM)
777     return;
778   DWARFCtx->ParsedDSYM = true;
779   DWARFCtx->SourceLocs =
780       DylibReader::accumulateSourceLocFromDSYM(DSYMPath, Ctx.Target);
781 }
782 
783 void DylibVerifier::visitObjCInterface(const ObjCInterfaceRecord &R) {
784   SymbolContext SymCtx;
785   SymCtx.SymbolName = R.getName();
786   SymCtx.ObjCIFKind = assignObjCIFSymbolKind(&R);
787   if (SymCtx.ObjCIFKind > ObjCIFSymbolKind::EHType) {
788     if (R.hasExceptionAttribute()) {
789       SymCtx.Kind = EncodeKind::ObjectiveCClassEHType;
790       visitSymbolInDylib(R, SymCtx);
791     }
792     SymCtx.Kind = EncodeKind::ObjectiveCClass;
793     visitSymbolInDylib(R, SymCtx);
794   } else {
795     SymCtx.Kind = R.hasExceptionAttribute() ? EncodeKind::ObjectiveCClassEHType
796                                             : EncodeKind::ObjectiveCClass;
797     visitSymbolInDylib(R, SymCtx);
798   }
799 
800   for (const ObjCIVarRecord *IV : R.getObjCIVars())
801     visitObjCIVar(*IV, R.getName());
802 }
803 
804 void DylibVerifier::visitObjCCategory(const ObjCCategoryRecord &R) {
805   for (const ObjCIVarRecord *IV : R.getObjCIVars())
806     visitObjCIVar(*IV, R.getSuperClassName());
807 }
808 
809 DylibVerifier::Result DylibVerifier::verifyRemainingSymbols() {
810   if (getState() == Result::NoVerify)
811     return Result::NoVerify;
812   assert(!Dylib.empty() && "No binary to verify against");
813 
814   DWARFContext DWARFInfo;
815   DWARFCtx = &DWARFInfo;
816   Ctx.Target = Target(Architecture::AK_unknown, PlatformType::PLATFORM_UNKNOWN);
817   for (std::shared_ptr<RecordsSlice> Slice : Dylib) {
818     if (Ctx.Target.Arch == Slice->getTarget().Arch)
819       continue;
820     Ctx.DiscoveredFirstError = false;
821     Ctx.PrintArch = true;
822     Ctx.Target = Slice->getTarget();
823     Ctx.DylibSlice = Slice.get();
824     Slice->visit(*this);
825   }
826   return getState();
827 }
828 
829 bool DylibVerifier::verifyBinaryAttrs(const ArrayRef<Target> ProvidedTargets,
830                                       const BinaryAttrs &ProvidedBA,
831                                       const LibAttrs &ProvidedReexports,
832                                       const LibAttrs &ProvidedClients,
833                                       const LibAttrs &ProvidedRPaths,
834                                       const FileType &FT) {
835   assert(!Dylib.empty() && "Need dylib to verify.");
836 
837   // Pickup any load commands that can differ per slice to compare.
838   TargetList DylibTargets;
839   LibAttrs DylibReexports;
840   LibAttrs DylibClients;
841   LibAttrs DylibRPaths;
842   for (const std::shared_ptr<RecordsSlice> &RS : Dylib) {
843     DylibTargets.push_back(RS->getTarget());
844     const BinaryAttrs &BinInfo = RS->getBinaryAttrs();
845     for (const StringRef LibName : BinInfo.RexportedLibraries)
846       DylibReexports.getArchSet(LibName).set(DylibTargets.back().Arch);
847     for (const StringRef LibName : BinInfo.AllowableClients)
848       DylibClients.getArchSet(LibName).set(DylibTargets.back().Arch);
849     // Compare attributes that are only representable in >= TBD_V5.
850     if (FT >= FileType::TBD_V5)
851       for (const StringRef Name : BinInfo.RPaths)
852         DylibRPaths.getArchSet(Name).set(DylibTargets.back().Arch);
853   }
854 
855   // Check targets first.
856   ArchitectureSet ProvidedArchs = mapToArchitectureSet(ProvidedTargets);
857   ArchitectureSet DylibArchs = mapToArchitectureSet(DylibTargets);
858   if (ProvidedArchs != DylibArchs) {
859     Ctx.Diag->Report(diag::err_architecture_mismatch)
860         << ProvidedArchs << DylibArchs;
861     return false;
862   }
863   auto ProvidedPlatforms = mapToPlatformVersionSet(ProvidedTargets);
864   auto DylibPlatforms = mapToPlatformVersionSet(DylibTargets);
865   if (ProvidedPlatforms != DylibPlatforms) {
866     const bool DiffMinOS =
867         mapToPlatformSet(ProvidedTargets) == mapToPlatformSet(DylibTargets);
868     if (DiffMinOS)
869       Ctx.Diag->Report(diag::warn_platform_mismatch)
870           << ProvidedPlatforms << DylibPlatforms;
871     else {
872       Ctx.Diag->Report(diag::err_platform_mismatch)
873           << ProvidedPlatforms << DylibPlatforms;
874       return false;
875     }
876   }
877 
878   // Because InstallAPI requires certain attributes to match across architecture
879   // slices, take the first one to compare those with.
880   const BinaryAttrs &DylibBA = (*Dylib.begin())->getBinaryAttrs();
881 
882   if (ProvidedBA.InstallName != DylibBA.InstallName) {
883     Ctx.Diag->Report(diag::err_install_name_mismatch)
884         << ProvidedBA.InstallName << DylibBA.InstallName;
885     return false;
886   }
887 
888   if (ProvidedBA.CurrentVersion != DylibBA.CurrentVersion) {
889     Ctx.Diag->Report(diag::err_current_version_mismatch)
890         << ProvidedBA.CurrentVersion << DylibBA.CurrentVersion;
891     return false;
892   }
893 
894   if (ProvidedBA.CompatVersion != DylibBA.CompatVersion) {
895     Ctx.Diag->Report(diag::err_compatibility_version_mismatch)
896         << ProvidedBA.CompatVersion << DylibBA.CompatVersion;
897     return false;
898   }
899 
900   if (ProvidedBA.AppExtensionSafe != DylibBA.AppExtensionSafe) {
901     Ctx.Diag->Report(diag::err_appextension_safe_mismatch)
902         << (ProvidedBA.AppExtensionSafe ? "true" : "false")
903         << (DylibBA.AppExtensionSafe ? "true" : "false");
904     return false;
905   }
906 
907   if (!DylibBA.TwoLevelNamespace) {
908     Ctx.Diag->Report(diag::err_no_twolevel_namespace);
909     return false;
910   }
911 
912   if (ProvidedBA.OSLibNotForSharedCache != DylibBA.OSLibNotForSharedCache) {
913     Ctx.Diag->Report(diag::err_shared_cache_eligiblity_mismatch)
914         << (ProvidedBA.OSLibNotForSharedCache ? "true" : "false")
915         << (DylibBA.OSLibNotForSharedCache ? "true" : "false");
916     return false;
917   }
918 
919   if (ProvidedBA.ParentUmbrella.empty() && !DylibBA.ParentUmbrella.empty()) {
920     Ctx.Diag->Report(diag::err_parent_umbrella_missing)
921         << "installAPI option" << DylibBA.ParentUmbrella;
922     return false;
923   }
924 
925   if (!ProvidedBA.ParentUmbrella.empty() && DylibBA.ParentUmbrella.empty()) {
926     Ctx.Diag->Report(diag::err_parent_umbrella_missing)
927         << "binary file" << ProvidedBA.ParentUmbrella;
928     return false;
929   }
930 
931   if ((!ProvidedBA.ParentUmbrella.empty()) &&
932       (ProvidedBA.ParentUmbrella != DylibBA.ParentUmbrella)) {
933     Ctx.Diag->Report(diag::err_parent_umbrella_mismatch)
934         << ProvidedBA.ParentUmbrella << DylibBA.ParentUmbrella;
935     return false;
936   }
937 
938   auto CompareLibraries = [&](const LibAttrs &Provided, const LibAttrs &Dylib,
939                               unsigned DiagID_missing, unsigned DiagID_mismatch,
940                               bool Fatal = true) {
941     if (Provided == Dylib)
942       return true;
943 
944     for (const LibAttrs::Entry &PEntry : Provided.get()) {
945       const auto &[PAttr, PArchSet] = PEntry;
946       auto DAttrEntry = Dylib.find(PAttr);
947       if (!DAttrEntry) {
948         Ctx.Diag->Report(DiagID_missing) << "binary file" << PEntry;
949         if (Fatal)
950           return false;
951       }
952 
953       if (PArchSet != DAttrEntry->second) {
954         Ctx.Diag->Report(DiagID_mismatch) << PEntry << *DAttrEntry;
955         if (Fatal)
956           return false;
957       }
958     }
959 
960     for (const LibAttrs::Entry &DEntry : Dylib.get()) {
961       const auto &[DAttr, DArchSet] = DEntry;
962       const auto &PAttrEntry = Provided.find(DAttr);
963       if (!PAttrEntry) {
964         Ctx.Diag->Report(DiagID_missing) << "installAPI option" << DEntry;
965         if (!Fatal)
966           continue;
967         return false;
968       }
969 
970       if (PAttrEntry->second != DArchSet) {
971         if (Fatal)
972           llvm_unreachable("this case was already covered above.");
973       }
974     }
975     return true;
976   };
977 
978   if (!CompareLibraries(ProvidedReexports, DylibReexports,
979                         diag::err_reexported_libraries_missing,
980                         diag::err_reexported_libraries_mismatch))
981     return false;
982 
983   if (!CompareLibraries(ProvidedClients, DylibClients,
984                         diag::err_allowable_clients_missing,
985                         diag::err_allowable_clients_mismatch))
986     return false;
987 
988   if (FT >= FileType::TBD_V5) {
989     // Ignore rpath differences if building an asan variant, since the
990     //   compiler injects additional paths.
991     // FIXME: Building with sanitizers does not always change the install
992     //   name, so this is not a foolproof solution.
993     if (!ProvidedBA.InstallName.ends_with("_asan")) {
994       if (!CompareLibraries(ProvidedRPaths, DylibRPaths,
995                             diag::warn_rpaths_missing,
996                             diag::warn_rpaths_mismatch,
997                             /*Fatal=*/false))
998         return true;
999     }
1000   }
1001 
1002   return true;
1003 }
1004 
1005 std::unique_ptr<SymbolSet> DylibVerifier::takeExports() {
1006   for (const auto &[Alias, Base] : Aliases) {
1007     TargetList Targets;
1008     SymbolFlags Flags = SymbolFlags::None;
1009     if (const Symbol *Sym = Exports->findSymbol(Base.second, Base.first)) {
1010       Flags = Sym->getFlags();
1011       Targets = {Sym->targets().begin(), Sym->targets().end()};
1012     }
1013 
1014     Record R(Alias.first, RecordLinkage::Exported, Flags);
1015     SymbolContext SymCtx;
1016     SymCtx.SymbolName = Alias.first;
1017     SymCtx.Kind = Alias.second;
1018     addSymbol(&R, SymCtx, std::move(Targets));
1019   }
1020 
1021   return std::move(Exports);
1022 }
1023 
1024 } // namespace installapi
1025 } // namespace clang
1026