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