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