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