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