Path: blob/main/contrib/llvm-project/clang/lib/InstallAPI/DylibVerifier.cpp
35233 views
//===- DylibVerifier.cpp ----------------------------------------*- C++--*-===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "clang/InstallAPI/DylibVerifier.h"9#include "DiagnosticBuilderWrappers.h"10#include "clang/InstallAPI/FrontendRecords.h"11#include "clang/InstallAPI/InstallAPIDiagnostic.h"12#include "llvm/Demangle/Demangle.h"13#include "llvm/TextAPI/DylibReader.h"1415using namespace llvm::MachO;1617namespace clang {18namespace installapi {1920/// Metadata stored about a mapping of a declaration to a symbol.21struct DylibVerifier::SymbolContext {22// Name to use for all querying and verification23// purposes.24std::string SymbolName{""};2526// Kind to map symbol type against record.27EncodeKind Kind = EncodeKind::GlobalSymbol;2829// Frontend Attributes tied to the AST.30const FrontendAttrs *FA = nullptr;3132// The ObjCInterface symbol type, if applicable.33ObjCIFSymbolKind ObjCIFKind = ObjCIFSymbolKind::None;3435// Whether Decl is inlined.36bool Inlined = false;37};3839struct DylibVerifier::DWARFContext {40// Track whether DSYM parsing has already been attempted to avoid re-parsing.41bool ParsedDSYM{false};4243// Lookup table for source locations by symbol name.44DylibReader::SymbolToSourceLocMap SourceLocs{};45};4647static bool isCppMangled(StringRef Name) {48// InstallAPI currently only supports itanium manglings.49return (Name.starts_with("_Z") || Name.starts_with("__Z") ||50Name.starts_with("___Z"));51}5253static std::string demangle(StringRef Name) {54// InstallAPI currently only supports itanium manglings.55if (!isCppMangled(Name))56return Name.str();57char *Result = llvm::itaniumDemangle(Name);58if (!Result)59return Name.str();6061std::string Demangled(Result);62free(Result);63return Demangled;64}6566std::string DylibVerifier::getAnnotatedName(const Record *R,67SymbolContext &SymCtx,68bool ValidSourceLoc) {69assert(!SymCtx.SymbolName.empty() && "Expected symbol name");7071const StringRef SymbolName = SymCtx.SymbolName;72std::string PrettyName =73(Demangle && (SymCtx.Kind == EncodeKind::GlobalSymbol))74? demangle(SymbolName)75: SymbolName.str();7677std::string Annotation;78if (R->isWeakDefined())79Annotation += "(weak-def) ";80if (R->isWeakReferenced())81Annotation += "(weak-ref) ";82if (R->isThreadLocalValue())83Annotation += "(tlv) ";8485// Check if symbol represents only part of a @interface declaration.86switch (SymCtx.ObjCIFKind) {87default:88break;89case ObjCIFSymbolKind::EHType:90return Annotation + "Exception Type of " + PrettyName;91case ObjCIFSymbolKind::MetaClass:92return Annotation + "Metaclass of " + PrettyName;93case ObjCIFSymbolKind::Class:94return Annotation + "Class of " + PrettyName;95}9697// Only print symbol type prefix or leading "_" if there is no source location98// tied to it. This can only ever happen when the location has to come from99// debug info.100if (ValidSourceLoc) {101StringRef PrettyNameRef(PrettyName);102if ((SymCtx.Kind == EncodeKind::GlobalSymbol) &&103!isCppMangled(SymbolName) && PrettyNameRef.starts_with("_"))104return Annotation + PrettyNameRef.drop_front(1).str();105return Annotation + PrettyName;106}107108switch (SymCtx.Kind) {109case EncodeKind::GlobalSymbol:110return Annotation + PrettyName;111case EncodeKind::ObjectiveCInstanceVariable:112return Annotation + "(ObjC IVar) " + PrettyName;113case EncodeKind::ObjectiveCClass:114return Annotation + "(ObjC Class) " + PrettyName;115case EncodeKind::ObjectiveCClassEHType:116return Annotation + "(ObjC Class EH) " + PrettyName;117}118119llvm_unreachable("unexpected case for EncodeKind");120}121122static DylibVerifier::Result updateResult(const DylibVerifier::Result Prev,123const DylibVerifier::Result Curr) {124if (Prev == Curr)125return Prev;126127// Never update from invalid or noverify state.128if ((Prev == DylibVerifier::Result::Invalid) ||129(Prev == DylibVerifier::Result::NoVerify))130return Prev;131132// Don't let an ignored verification remove a valid one.133if (Prev == DylibVerifier::Result::Valid &&134Curr == DylibVerifier::Result::Ignore)135return Prev;136137return Curr;138}139// __private_extern__ is a deprecated specifier that clang does not140// respect in all contexts, it should just be considered hidden for InstallAPI.141static bool shouldIgnorePrivateExternAttr(const Decl *D) {142if (const FunctionDecl *FD = cast<FunctionDecl>(D))143return FD->getStorageClass() == StorageClass::SC_PrivateExtern;144if (const VarDecl *VD = cast<VarDecl>(D))145return VD->getStorageClass() == StorageClass::SC_PrivateExtern;146147return false;148}149150Record *findRecordFromSlice(const RecordsSlice *Slice, StringRef Name,151EncodeKind Kind) {152switch (Kind) {153case EncodeKind::GlobalSymbol:154return Slice->findGlobal(Name);155case EncodeKind::ObjectiveCInstanceVariable:156return Slice->findObjCIVar(Name.contains('.'), Name);157case EncodeKind::ObjectiveCClass:158case EncodeKind::ObjectiveCClassEHType:159return Slice->findObjCInterface(Name);160}161llvm_unreachable("unexpected end when finding record");162}163164void DylibVerifier::updateState(Result State) {165Ctx.FrontendState = updateResult(Ctx.FrontendState, State);166}167168void DylibVerifier::addSymbol(const Record *R, SymbolContext &SymCtx,169TargetList &&Targets) {170if (Targets.empty())171Targets = {Ctx.Target};172173Exports->addGlobal(SymCtx.Kind, SymCtx.SymbolName, R->getFlags(), Targets);174}175176bool DylibVerifier::shouldIgnoreObsolete(const Record *R, SymbolContext &SymCtx,177const Record *DR) {178if (!SymCtx.FA->Avail.isObsoleted())179return false;180181if (Zippered)182DeferredZipperedSymbols[SymCtx.SymbolName].emplace_back(ZipperedDeclSource{183SymCtx.FA, &Ctx.Diag->getSourceManager(), Ctx.Target});184return true;185}186187bool DylibVerifier::shouldIgnoreReexport(const Record *R,188SymbolContext &SymCtx) const {189StringRef SymName = SymCtx.SymbolName;190// Linker directive symbols can never be ignored.191if (SymName.starts_with("$ld$"))192return false;193194if (Reexports.empty())195return false;196197for (const InterfaceFile &Lib : Reexports) {198if (!Lib.hasTarget(Ctx.Target))199continue;200if (auto Sym = Lib.getSymbol(SymCtx.Kind, SymName, SymCtx.ObjCIFKind))201if ((*Sym)->hasTarget(Ctx.Target))202return true;203}204return false;205}206207bool DylibVerifier::shouldIgnoreInternalZipperedSymbol(208const Record *R, const SymbolContext &SymCtx) const {209if (!Zippered)210return false;211212return Exports->findSymbol(SymCtx.Kind, SymCtx.SymbolName,213SymCtx.ObjCIFKind) != nullptr;214}215216bool DylibVerifier::shouldIgnoreZipperedAvailability(const Record *R,217SymbolContext &SymCtx) {218if (!(Zippered && SymCtx.FA->Avail.isUnavailable()))219return false;220221// Collect source location incase there is an exported symbol to diagnose222// during `verifyRemainingSymbols`.223DeferredZipperedSymbols[SymCtx.SymbolName].emplace_back(224ZipperedDeclSource{SymCtx.FA, SourceManagers.back().get(), Ctx.Target});225226return true;227}228229bool DylibVerifier::compareObjCInterfaceSymbols(const Record *R,230SymbolContext &SymCtx,231const ObjCInterfaceRecord *DR) {232const bool IsDeclVersionComplete =233((SymCtx.ObjCIFKind & ObjCIFSymbolKind::Class) ==234ObjCIFSymbolKind::Class) &&235((SymCtx.ObjCIFKind & ObjCIFSymbolKind::MetaClass) ==236ObjCIFSymbolKind::MetaClass);237238const bool IsDylibVersionComplete = DR->isCompleteInterface();239240// The common case, a complete ObjCInterface.241if (IsDeclVersionComplete && IsDylibVersionComplete)242return true;243244auto PrintDiagnostic = [&](auto SymLinkage, const Record *Record,245StringRef SymName, bool PrintAsWarning = false) {246if (SymLinkage == RecordLinkage::Unknown)247Ctx.emitDiag([&]() {248Ctx.Diag->Report(SymCtx.FA->Loc, PrintAsWarning249? diag::warn_library_missing_symbol250: diag::err_library_missing_symbol)251<< SymName;252});253else254Ctx.emitDiag([&]() {255Ctx.Diag->Report(SymCtx.FA->Loc, PrintAsWarning256? diag::warn_library_hidden_symbol257: diag::err_library_hidden_symbol)258<< SymName;259});260};261262if (IsDeclVersionComplete) {263// The decl represents a complete ObjCInterface, but the symbols in the264// dylib do not. Determine which symbol is missing. To keep older projects265// building, treat this as a warning.266if (!DR->isExportedSymbol(ObjCIFSymbolKind::Class)) {267SymCtx.ObjCIFKind = ObjCIFSymbolKind::Class;268PrintDiagnostic(DR->getLinkageForSymbol(ObjCIFSymbolKind::Class), R,269getAnnotatedName(R, SymCtx),270/*PrintAsWarning=*/true);271}272if (!DR->isExportedSymbol(ObjCIFSymbolKind::MetaClass)) {273SymCtx.ObjCIFKind = ObjCIFSymbolKind::MetaClass;274PrintDiagnostic(DR->getLinkageForSymbol(ObjCIFSymbolKind::MetaClass), R,275getAnnotatedName(R, SymCtx),276/*PrintAsWarning=*/true);277}278return true;279}280281if (DR->isExportedSymbol(SymCtx.ObjCIFKind)) {282if (!IsDylibVersionComplete) {283// Both the declaration and dylib have a non-complete interface.284SymCtx.Kind = EncodeKind::GlobalSymbol;285SymCtx.SymbolName = R->getName();286}287return true;288}289290// At this point that means there was not a matching class symbol291// to represent the one discovered as a declaration.292PrintDiagnostic(DR->getLinkageForSymbol(SymCtx.ObjCIFKind), R,293SymCtx.SymbolName);294return false;295}296297DylibVerifier::Result DylibVerifier::compareVisibility(const Record *R,298SymbolContext &SymCtx,299const Record *DR) {300301if (R->isExported()) {302if (!DR) {303Ctx.emitDiag([&]() {304Ctx.Diag->Report(SymCtx.FA->Loc, diag::err_library_missing_symbol)305<< getAnnotatedName(R, SymCtx);306});307return Result::Invalid;308}309if (DR->isInternal()) {310Ctx.emitDiag([&]() {311Ctx.Diag->Report(SymCtx.FA->Loc, diag::err_library_hidden_symbol)312<< getAnnotatedName(R, SymCtx);313});314return Result::Invalid;315}316}317318// Emit a diagnostic for hidden declarations with external symbols, except319// when theres an inlined attribute.320if ((R->isInternal() && !SymCtx.Inlined) && DR && DR->isExported()) {321322if (Mode == VerificationMode::ErrorsOnly)323return Result::Ignore;324325if (shouldIgnorePrivateExternAttr(SymCtx.FA->D))326return Result::Ignore;327328if (shouldIgnoreInternalZipperedSymbol(R, SymCtx))329return Result::Ignore;330331unsigned ID;332Result Outcome;333if (Mode == VerificationMode::ErrorsAndWarnings) {334ID = diag::warn_header_hidden_symbol;335Outcome = Result::Ignore;336} else {337ID = diag::err_header_hidden_symbol;338Outcome = Result::Invalid;339}340Ctx.emitDiag([&]() {341Ctx.Diag->Report(SymCtx.FA->Loc, ID) << getAnnotatedName(R, SymCtx);342});343return Outcome;344}345346if (R->isInternal())347return Result::Ignore;348349return Result::Valid;350}351352DylibVerifier::Result DylibVerifier::compareAvailability(const Record *R,353SymbolContext &SymCtx,354const Record *DR) {355if (!SymCtx.FA->Avail.isUnavailable())356return Result::Valid;357358if (shouldIgnoreZipperedAvailability(R, SymCtx))359return Result::Ignore;360361const bool IsDeclAvailable = SymCtx.FA->Avail.isUnavailable();362363switch (Mode) {364case VerificationMode::ErrorsAndWarnings:365Ctx.emitDiag([&]() {366Ctx.Diag->Report(SymCtx.FA->Loc, diag::warn_header_availability_mismatch)367<< getAnnotatedName(R, SymCtx) << IsDeclAvailable << IsDeclAvailable;368});369return Result::Ignore;370case VerificationMode::Pedantic:371Ctx.emitDiag([&]() {372Ctx.Diag->Report(SymCtx.FA->Loc, diag::err_header_availability_mismatch)373<< getAnnotatedName(R, SymCtx) << IsDeclAvailable << IsDeclAvailable;374});375return Result::Invalid;376case VerificationMode::ErrorsOnly:377return Result::Ignore;378case VerificationMode::Invalid:379llvm_unreachable("Unexpected verification mode symbol verification");380}381llvm_unreachable("Unexpected verification mode symbol verification");382}383384bool DylibVerifier::compareSymbolFlags(const Record *R, SymbolContext &SymCtx,385const Record *DR) {386if (DR->isThreadLocalValue() && !R->isThreadLocalValue()) {387Ctx.emitDiag([&]() {388Ctx.Diag->Report(SymCtx.FA->Loc, diag::err_dylib_symbol_flags_mismatch)389<< getAnnotatedName(DR, SymCtx) << DR->isThreadLocalValue();390});391return false;392}393if (!DR->isThreadLocalValue() && R->isThreadLocalValue()) {394Ctx.emitDiag([&]() {395Ctx.Diag->Report(SymCtx.FA->Loc, diag::err_header_symbol_flags_mismatch)396<< getAnnotatedName(R, SymCtx) << R->isThreadLocalValue();397});398return false;399}400401if (DR->isWeakDefined() && !R->isWeakDefined()) {402Ctx.emitDiag([&]() {403Ctx.Diag->Report(SymCtx.FA->Loc, diag::err_dylib_symbol_flags_mismatch)404<< getAnnotatedName(DR, SymCtx) << R->isWeakDefined();405});406return false;407}408if (!DR->isWeakDefined() && R->isWeakDefined()) {409Ctx.emitDiag([&]() {410Ctx.Diag->Report(SymCtx.FA->Loc, diag::err_header_symbol_flags_mismatch)411<< getAnnotatedName(R, SymCtx) << R->isWeakDefined();412});413return false;414}415416return true;417}418419DylibVerifier::Result DylibVerifier::verifyImpl(Record *R,420SymbolContext &SymCtx) {421R->setVerify();422if (!canVerify()) {423// Accumulate symbols when not in verifying against dylib.424if (R->isExported() && !SymCtx.FA->Avail.isUnavailable() &&425!SymCtx.FA->Avail.isObsoleted()) {426addSymbol(R, SymCtx);427}428return Ctx.FrontendState;429}430431if (shouldIgnoreReexport(R, SymCtx)) {432updateState(Result::Ignore);433return Ctx.FrontendState;434}435436Record *DR =437findRecordFromSlice(Ctx.DylibSlice, SymCtx.SymbolName, SymCtx.Kind);438if (DR)439DR->setVerify();440441if (shouldIgnoreObsolete(R, SymCtx, DR)) {442updateState(Result::Ignore);443return Ctx.FrontendState;444}445446// Unavailable declarations don't need matching symbols.447if (SymCtx.FA->Avail.isUnavailable() && (!DR || DR->isInternal())) {448updateState(Result::Valid);449return Ctx.FrontendState;450}451452Result VisibilityCheck = compareVisibility(R, SymCtx, DR);453if (VisibilityCheck != Result::Valid) {454updateState(VisibilityCheck);455return Ctx.FrontendState;456}457458// All missing symbol cases to diagnose have been handled now.459if (!DR) {460updateState(Result::Ignore);461return Ctx.FrontendState;462}463464// Check for mismatching ObjC interfaces.465if (SymCtx.ObjCIFKind != ObjCIFSymbolKind::None) {466if (!compareObjCInterfaceSymbols(467R, SymCtx, Ctx.DylibSlice->findObjCInterface(DR->getName()))) {468updateState(Result::Invalid);469return Ctx.FrontendState;470}471}472473Result AvailabilityCheck = compareAvailability(R, SymCtx, DR);474if (AvailabilityCheck != Result::Valid) {475updateState(AvailabilityCheck);476return Ctx.FrontendState;477}478479if (!compareSymbolFlags(R, SymCtx, DR)) {480updateState(Result::Invalid);481return Ctx.FrontendState;482}483484addSymbol(R, SymCtx);485updateState(Result::Valid);486return Ctx.FrontendState;487}488489bool DylibVerifier::canVerify() {490return Ctx.FrontendState != Result::NoVerify;491}492493void DylibVerifier::assignSlice(const Target &T) {494assert(T == Ctx.Target && "Active targets should match.");495if (Dylib.empty())496return;497498// Note: there are no reexport slices with binaries, as opposed to TBD files,499// so it can be assumed that the target match is the active top-level library.500auto It = find_if(501Dylib, [&T](const auto &Slice) { return T == Slice->getTarget(); });502503assert(It != Dylib.end() && "Target slice should always exist.");504Ctx.DylibSlice = It->get();505}506507void DylibVerifier::setTarget(const Target &T) {508Ctx.Target = T;509Ctx.DiscoveredFirstError = false;510if (Dylib.empty()) {511updateState(Result::NoVerify);512return;513}514updateState(Result::Ignore);515assignSlice(T);516}517518void DylibVerifier::setSourceManager(519IntrusiveRefCntPtr<SourceManager> SourceMgr) {520if (!Ctx.Diag)521return;522SourceManagers.push_back(std::move(SourceMgr));523Ctx.Diag->setSourceManager(SourceManagers.back().get());524}525526DylibVerifier::Result DylibVerifier::verify(ObjCIVarRecord *R,527const FrontendAttrs *FA,528const StringRef SuperClass) {529if (R->isVerified())530return getState();531532std::string FullName =533ObjCIVarRecord::createScopedName(SuperClass, R->getName());534SymbolContext SymCtx{FullName, EncodeKind::ObjectiveCInstanceVariable, FA};535return verifyImpl(R, SymCtx);536}537538static ObjCIFSymbolKind assignObjCIFSymbolKind(const ObjCInterfaceRecord *R) {539ObjCIFSymbolKind Result = ObjCIFSymbolKind::None;540if (R->getLinkageForSymbol(ObjCIFSymbolKind::Class) != RecordLinkage::Unknown)541Result |= ObjCIFSymbolKind::Class;542if (R->getLinkageForSymbol(ObjCIFSymbolKind::MetaClass) !=543RecordLinkage::Unknown)544Result |= ObjCIFSymbolKind::MetaClass;545if (R->getLinkageForSymbol(ObjCIFSymbolKind::EHType) !=546RecordLinkage::Unknown)547Result |= ObjCIFSymbolKind::EHType;548return Result;549}550551DylibVerifier::Result DylibVerifier::verify(ObjCInterfaceRecord *R,552const FrontendAttrs *FA) {553if (R->isVerified())554return getState();555SymbolContext SymCtx;556SymCtx.SymbolName = R->getName();557SymCtx.ObjCIFKind = assignObjCIFSymbolKind(R);558559SymCtx.Kind = R->hasExceptionAttribute() ? EncodeKind::ObjectiveCClassEHType560: EncodeKind::ObjectiveCClass;561SymCtx.FA = FA;562563return verifyImpl(R, SymCtx);564}565566DylibVerifier::Result DylibVerifier::verify(GlobalRecord *R,567const FrontendAttrs *FA) {568if (R->isVerified())569return getState();570571// Global classifications could be obfusciated with `asm`.572SimpleSymbol Sym = parseSymbol(R->getName());573SymbolContext SymCtx;574SymCtx.SymbolName = Sym.Name;575SymCtx.Kind = Sym.Kind;576SymCtx.FA = FA;577SymCtx.Inlined = R->isInlined();578return verifyImpl(R, SymCtx);579}580581void DylibVerifier::VerifierContext::emitDiag(llvm::function_ref<void()> Report,582RecordLoc *Loc) {583if (!DiscoveredFirstError) {584Diag->Report(diag::warn_target)585<< (PrintArch ? getArchitectureName(Target.Arch)586: getTargetTripleName(Target));587DiscoveredFirstError = true;588}589if (Loc && Loc->isValid())590llvm::errs() << Loc->File << ":" << Loc->Line << ":" << 0 << ": ";591592Report();593}594595// The existence of weak-defined RTTI can not always be inferred from the596// header files because they can be generated as part of an implementation597// file.598// InstallAPI doesn't warn about weak-defined RTTI, because this doesn't affect599// static linking and so can be ignored for text-api files.600static bool shouldIgnoreCpp(StringRef Name, bool IsWeakDef) {601return (IsWeakDef &&602(Name.starts_with("__ZTI") || Name.starts_with("__ZTS")));603}604void DylibVerifier::visitSymbolInDylib(const Record &R, SymbolContext &SymCtx) {605// Undefined symbols should not be in InstallAPI generated text-api files.606if (R.isUndefined()) {607updateState(Result::Valid);608return;609}610611// Internal symbols should not be in InstallAPI generated text-api files.612if (R.isInternal()) {613updateState(Result::Valid);614return;615}616617// Allow zippered symbols with potentially mismatching availability618// between macOS and macCatalyst in the final text-api file.619const StringRef SymbolName(SymCtx.SymbolName);620if (const Symbol *Sym = Exports->findSymbol(SymCtx.Kind, SymCtx.SymbolName,621SymCtx.ObjCIFKind)) {622if (Sym->hasArchitecture(Ctx.Target.Arch)) {623updateState(Result::Ignore);624return;625}626}627628const bool IsLinkerSymbol = SymbolName.starts_with("$ld$");629630if (R.isVerified()) {631// Check for unavailable symbols.632// This should only occur in the zippered case where we ignored633// availability until all headers have been parsed.634auto It = DeferredZipperedSymbols.find(SymCtx.SymbolName);635if (It == DeferredZipperedSymbols.end()) {636updateState(Result::Valid);637return;638}639640ZipperedDeclSources Locs;641for (const ZipperedDeclSource &ZSource : It->second) {642if (ZSource.FA->Avail.isObsoleted()) {643updateState(Result::Ignore);644return;645}646if (ZSource.T.Arch != Ctx.Target.Arch)647continue;648Locs.emplace_back(ZSource);649}650assert(Locs.size() == 2 && "Expected two decls for zippered symbol");651652// Print violating declarations per platform.653for (const ZipperedDeclSource &ZSource : Locs) {654unsigned DiagID = 0;655if (Mode == VerificationMode::Pedantic || IsLinkerSymbol) {656updateState(Result::Invalid);657DiagID = diag::err_header_availability_mismatch;658} else if (Mode == VerificationMode::ErrorsAndWarnings) {659updateState(Result::Ignore);660DiagID = diag::warn_header_availability_mismatch;661} else {662updateState(Result::Ignore);663return;664}665// Bypass emitDiag banner and print the target everytime.666Ctx.Diag->setSourceManager(ZSource.SrcMgr);667Ctx.Diag->Report(diag::warn_target) << getTargetTripleName(ZSource.T);668Ctx.Diag->Report(ZSource.FA->Loc, DiagID)669<< getAnnotatedName(&R, SymCtx) << ZSource.FA->Avail.isUnavailable()670<< ZSource.FA->Avail.isUnavailable();671}672return;673}674675if (shouldIgnoreCpp(SymbolName, R.isWeakDefined())) {676updateState(Result::Valid);677return;678}679680if (Aliases.count({SymbolName.str(), SymCtx.Kind})) {681updateState(Result::Valid);682return;683}684685// All checks at this point classify as some kind of violation.686// The different verification modes dictate whether they are reported to the687// user.688if (IsLinkerSymbol || (Mode > VerificationMode::ErrorsOnly))689accumulateSrcLocForDylibSymbols();690RecordLoc Loc = DWARFCtx->SourceLocs.lookup(SymCtx.SymbolName);691692// Regardless of verification mode, error out on mismatched special linker693// symbols.694if (IsLinkerSymbol) {695Ctx.emitDiag(696[&]() {697Ctx.Diag->Report(diag::err_header_symbol_missing)698<< getAnnotatedName(&R, SymCtx, Loc.isValid());699},700&Loc);701updateState(Result::Invalid);702return;703}704705// Missing declarations for exported symbols are hard errors on Pedantic mode.706if (Mode == VerificationMode::Pedantic) {707Ctx.emitDiag(708[&]() {709Ctx.Diag->Report(diag::err_header_symbol_missing)710<< getAnnotatedName(&R, SymCtx, Loc.isValid());711},712&Loc);713updateState(Result::Invalid);714return;715}716717// Missing declarations for exported symbols are warnings on ErrorsAndWarnings718// mode.719if (Mode == VerificationMode::ErrorsAndWarnings) {720Ctx.emitDiag(721[&]() {722Ctx.Diag->Report(diag::warn_header_symbol_missing)723<< getAnnotatedName(&R, SymCtx, Loc.isValid());724},725&Loc);726updateState(Result::Ignore);727return;728}729730// Missing declarations are dropped for ErrorsOnly mode. It is the last731// remaining mode.732updateState(Result::Ignore);733return;734}735736void DylibVerifier::visitGlobal(const GlobalRecord &R) {737SymbolContext SymCtx;738SimpleSymbol Sym = parseSymbol(R.getName());739SymCtx.SymbolName = Sym.Name;740SymCtx.Kind = Sym.Kind;741visitSymbolInDylib(R, SymCtx);742}743744void DylibVerifier::visitObjCIVar(const ObjCIVarRecord &R,745const StringRef Super) {746SymbolContext SymCtx;747SymCtx.SymbolName = ObjCIVarRecord::createScopedName(Super, R.getName());748SymCtx.Kind = EncodeKind::ObjectiveCInstanceVariable;749visitSymbolInDylib(R, SymCtx);750}751752void DylibVerifier::accumulateSrcLocForDylibSymbols() {753if (DSYMPath.empty())754return;755756assert(DWARFCtx != nullptr && "Expected an initialized DWARFContext");757if (DWARFCtx->ParsedDSYM)758return;759DWARFCtx->ParsedDSYM = true;760DWARFCtx->SourceLocs =761DylibReader::accumulateSourceLocFromDSYM(DSYMPath, Ctx.Target);762}763764void DylibVerifier::visitObjCInterface(const ObjCInterfaceRecord &R) {765SymbolContext SymCtx;766SymCtx.SymbolName = R.getName();767SymCtx.ObjCIFKind = assignObjCIFSymbolKind(&R);768if (SymCtx.ObjCIFKind > ObjCIFSymbolKind::EHType) {769if (R.hasExceptionAttribute()) {770SymCtx.Kind = EncodeKind::ObjectiveCClassEHType;771visitSymbolInDylib(R, SymCtx);772}773SymCtx.Kind = EncodeKind::ObjectiveCClass;774visitSymbolInDylib(R, SymCtx);775} else {776SymCtx.Kind = R.hasExceptionAttribute() ? EncodeKind::ObjectiveCClassEHType777: EncodeKind::ObjectiveCClass;778visitSymbolInDylib(R, SymCtx);779}780781for (const ObjCIVarRecord *IV : R.getObjCIVars())782visitObjCIVar(*IV, R.getName());783}784785void DylibVerifier::visitObjCCategory(const ObjCCategoryRecord &R) {786for (const ObjCIVarRecord *IV : R.getObjCIVars())787visitObjCIVar(*IV, R.getSuperClassName());788}789790DylibVerifier::Result DylibVerifier::verifyRemainingSymbols() {791if (getState() == Result::NoVerify)792return Result::NoVerify;793assert(!Dylib.empty() && "No binary to verify against");794795DWARFContext DWARFInfo;796DWARFCtx = &DWARFInfo;797Ctx.Target = Target(Architecture::AK_unknown, PlatformType::PLATFORM_UNKNOWN);798for (std::shared_ptr<RecordsSlice> Slice : Dylib) {799if (Ctx.Target.Arch == Slice->getTarget().Arch)800continue;801Ctx.DiscoveredFirstError = false;802Ctx.PrintArch = true;803Ctx.Target = Slice->getTarget();804Ctx.DylibSlice = Slice.get();805Slice->visit(*this);806}807return getState();808}809810bool DylibVerifier::verifyBinaryAttrs(const ArrayRef<Target> ProvidedTargets,811const BinaryAttrs &ProvidedBA,812const LibAttrs &ProvidedReexports,813const LibAttrs &ProvidedClients,814const LibAttrs &ProvidedRPaths,815const FileType &FT) {816assert(!Dylib.empty() && "Need dylib to verify.");817818// Pickup any load commands that can differ per slice to compare.819TargetList DylibTargets;820LibAttrs DylibReexports;821LibAttrs DylibClients;822LibAttrs DylibRPaths;823for (const std::shared_ptr<RecordsSlice> &RS : Dylib) {824DylibTargets.push_back(RS->getTarget());825const BinaryAttrs &BinInfo = RS->getBinaryAttrs();826for (const StringRef LibName : BinInfo.RexportedLibraries)827DylibReexports[LibName].set(DylibTargets.back().Arch);828for (const StringRef LibName : BinInfo.AllowableClients)829DylibClients[LibName].set(DylibTargets.back().Arch);830// Compare attributes that are only representable in >= TBD_V5.831if (FT >= FileType::TBD_V5)832for (const StringRef Name : BinInfo.RPaths)833DylibRPaths[Name].set(DylibTargets.back().Arch);834}835836// Check targets first.837ArchitectureSet ProvidedArchs = mapToArchitectureSet(ProvidedTargets);838ArchitectureSet DylibArchs = mapToArchitectureSet(DylibTargets);839if (ProvidedArchs != DylibArchs) {840Ctx.Diag->Report(diag::err_architecture_mismatch)841<< ProvidedArchs << DylibArchs;842return false;843}844auto ProvidedPlatforms = mapToPlatformVersionSet(ProvidedTargets);845auto DylibPlatforms = mapToPlatformVersionSet(DylibTargets);846if (ProvidedPlatforms != DylibPlatforms) {847const bool DiffMinOS =848mapToPlatformSet(ProvidedTargets) == mapToPlatformSet(DylibTargets);849if (DiffMinOS)850Ctx.Diag->Report(diag::warn_platform_mismatch)851<< ProvidedPlatforms << DylibPlatforms;852else {853Ctx.Diag->Report(diag::err_platform_mismatch)854<< ProvidedPlatforms << DylibPlatforms;855return false;856}857}858859// Because InstallAPI requires certain attributes to match across architecture860// slices, take the first one to compare those with.861const BinaryAttrs &DylibBA = (*Dylib.begin())->getBinaryAttrs();862863if (ProvidedBA.InstallName != DylibBA.InstallName) {864Ctx.Diag->Report(diag::err_install_name_mismatch)865<< ProvidedBA.InstallName << DylibBA.InstallName;866return false;867}868869if (ProvidedBA.CurrentVersion != DylibBA.CurrentVersion) {870Ctx.Diag->Report(diag::err_current_version_mismatch)871<< ProvidedBA.CurrentVersion << DylibBA.CurrentVersion;872return false;873}874875if (ProvidedBA.CompatVersion != DylibBA.CompatVersion) {876Ctx.Diag->Report(diag::err_compatibility_version_mismatch)877<< ProvidedBA.CompatVersion << DylibBA.CompatVersion;878return false;879}880881if (ProvidedBA.AppExtensionSafe != DylibBA.AppExtensionSafe) {882Ctx.Diag->Report(diag::err_appextension_safe_mismatch)883<< (ProvidedBA.AppExtensionSafe ? "true" : "false")884<< (DylibBA.AppExtensionSafe ? "true" : "false");885return false;886}887888if (!DylibBA.TwoLevelNamespace) {889Ctx.Diag->Report(diag::err_no_twolevel_namespace);890return false;891}892893if (ProvidedBA.OSLibNotForSharedCache != DylibBA.OSLibNotForSharedCache) {894Ctx.Diag->Report(diag::err_shared_cache_eligiblity_mismatch)895<< (ProvidedBA.OSLibNotForSharedCache ? "true" : "false")896<< (DylibBA.OSLibNotForSharedCache ? "true" : "false");897return false;898}899900if (ProvidedBA.ParentUmbrella.empty() && !DylibBA.ParentUmbrella.empty()) {901Ctx.Diag->Report(diag::err_parent_umbrella_missing)902<< "installAPI option" << DylibBA.ParentUmbrella;903return false;904}905906if (!ProvidedBA.ParentUmbrella.empty() && DylibBA.ParentUmbrella.empty()) {907Ctx.Diag->Report(diag::err_parent_umbrella_missing)908<< "binary file" << ProvidedBA.ParentUmbrella;909return false;910}911912if ((!ProvidedBA.ParentUmbrella.empty()) &&913(ProvidedBA.ParentUmbrella != DylibBA.ParentUmbrella)) {914Ctx.Diag->Report(diag::err_parent_umbrella_mismatch)915<< ProvidedBA.ParentUmbrella << DylibBA.ParentUmbrella;916return false;917}918919auto CompareLibraries = [&](const LibAttrs &Provided, const LibAttrs &Dylib,920unsigned DiagID_missing, unsigned DiagID_mismatch,921bool Fatal = true) {922if (Provided == Dylib)923return true;924925for (const llvm::StringMapEntry<ArchitectureSet> &PAttr : Provided) {926const auto DAttrIt = Dylib.find(PAttr.getKey());927if (DAttrIt == Dylib.end()) {928Ctx.Diag->Report(DiagID_missing) << "binary file" << PAttr;929if (Fatal)930return false;931}932933if (PAttr.getValue() != DAttrIt->getValue()) {934Ctx.Diag->Report(DiagID_mismatch) << PAttr << *DAttrIt;935if (Fatal)936return false;937}938}939940for (const llvm::StringMapEntry<ArchitectureSet> &DAttr : Dylib) {941const auto PAttrIt = Provided.find(DAttr.getKey());942if (PAttrIt == Provided.end()) {943Ctx.Diag->Report(DiagID_missing) << "installAPI option" << DAttr;944if (!Fatal)945continue;946return false;947}948949if (PAttrIt->getValue() != DAttr.getValue()) {950if (Fatal)951llvm_unreachable("this case was already covered above.");952}953}954return true;955};956957if (!CompareLibraries(ProvidedReexports, DylibReexports,958diag::err_reexported_libraries_missing,959diag::err_reexported_libraries_mismatch))960return false;961962if (!CompareLibraries(ProvidedClients, DylibClients,963diag::err_allowable_clients_missing,964diag::err_allowable_clients_mismatch))965return false;966967if (FT >= FileType::TBD_V5) {968// Ignore rpath differences if building an asan variant, since the969// compiler injects additional paths.970// FIXME: Building with sanitizers does not always change the install971// name, so this is not a foolproof solution.972if (!ProvidedBA.InstallName.ends_with("_asan")) {973if (!CompareLibraries(ProvidedRPaths, DylibRPaths,974diag::warn_rpaths_missing,975diag::warn_rpaths_mismatch,976/*Fatal=*/false))977return true;978}979}980981return true;982}983984std::unique_ptr<SymbolSet> DylibVerifier::takeExports() {985for (const auto &[Alias, Base] : Aliases) {986TargetList Targets;987SymbolFlags Flags = SymbolFlags::None;988if (const Symbol *Sym = Exports->findSymbol(Base.second, Base.first)) {989Flags = Sym->getFlags();990Targets = {Sym->targets().begin(), Sym->targets().end()};991}992993Record R(Alias.first, RecordLinkage::Exported, Flags);994SymbolContext SymCtx;995SymCtx.SymbolName = Alias.first;996SymCtx.Kind = Alias.second;997addSymbol(&R, SymCtx, std::move(Targets));998}9991000return std::move(Exports);1001}10021003} // namespace installapi1004} // namespace clang100510061007