[InstallAPI] Capture & compare load commands that may differ per arch slice (#87674)

* Capture reexported libraries, allowable clients, rpaths, shared cache
eligibility.
* Add support for select Xarch options.
* Add diagnostics related to capturing these options.
* Add support for verifying these attributes against what is encoded in
the dylib.
This commit is contained in:
Cyndy Ishida
2024-04-05 13:58:24 -07:00
committed by GitHub
parent 8ee6ab7f69
commit 27b2d7d4bb
19 changed files with 2234 additions and 16 deletions

View File

@@ -666,6 +666,7 @@ def warn_drv_darwin_sdk_invalid_settings : Warning<
"SDK settings were ignored as 'SDKSettings.json' could not be parsed">,
InGroup<DiagGroup<"darwin-sdk-settings">>;
def err_missing_sysroot : Error<"no such sysroot directory: '%0'">;
def err_drv_darwin_sdk_missing_arclite : Error<
"SDK does not contain 'libarclite' at the path '%0'; try increasing the minimum deployment target">;

View File

@@ -19,9 +19,11 @@ def err_no_such_header_file : Error<"no such %select{public|private|project}1 he
def warn_no_such_excluded_header_file : Warning<"no such excluded %select{public|private}0 header file: '%1'">, InGroup<InstallAPIViolation>;
def warn_glob_did_not_match: Warning<"glob '%0' did not match any header file">, InGroup<InstallAPIViolation>;
def err_no_such_umbrella_header_file : Error<"%select{public|private|project}1 umbrella header file not found in input: '%0'">;
def err_cannot_find_reexport : Error<"cannot find re-exported %select{framework|library}0: '%1'">;
} // end of command line category.
let CategoryName = "Verification" in {
// Diagnostics about symbols.
def warn_target: Warning<"violations found for %0">, InGroup<InstallAPIViolation>;
def err_library_missing_symbol : Error<"declaration has external linkage, but dynamic library doesn't have symbol '%0'">;
def warn_library_missing_symbol : Warning<"declaration has external linkage, but dynamic library doesn't have symbol '%0'">, InGroup<InstallAPIViolation>;
@@ -43,6 +45,25 @@ def err_dylib_symbol_flags_mismatch : Error<"dynamic library symbol '%0' is "
"%select{weak defined|thread local}1, but its declaration is not">;
def err_header_symbol_flags_mismatch : Error<"declaration '%0' is "
"%select{weak defined|thread local}1, but symbol is not in dynamic library">;
// Diagnostics about load commands.
def err_architecture_mismatch : Error<"architectures do not match: '%0' (provided) vs '%1' (found)">;
def warn_platform_mismatch : Warning<"platform does not match: '%0' (provided) vs '%1' (found)">, InGroup<InstallAPIViolation>;
def err_platform_mismatch : Error<"platform does not match: '%0' (provided) vs '%1' (found)">;
def err_install_name_mismatch : Error<"install_name does not match: '%0' (provided) vs '%1' (found)">;
def err_current_version_mismatch : Error<"current_version does not match: '%0' (provided) vs '%1' (found)">;
def err_compatibility_version_mismatch : Error<"compatibility_version does not match: '%0' (provided) vs '%1' (found)">;
def err_appextension_safe_mismatch : Error<"ApplicationExtensionSafe flag does not match: '%0' (provided) vs '%1' (found)">;
def err_shared_cache_eligiblity_mismatch : Error<"NotForDyldSharedCache flag does not match: '%0' (provided) vs '%1' (found)">;
def err_no_twolevel_namespace : Error<"flat namespace libraries are not supported">;
def err_parent_umbrella_missing: Error<"parent umbrella missing from %0: '%1'">;
def err_parent_umbrella_mismatch : Error<"parent umbrella does not match: '%0' (provided) vs '%1' (found)">;
def err_reexported_libraries_missing : Error<"re-exported library missing from %0: '%1'">;
def err_reexported_libraries_mismatch : Error<"re-exported libraries do not match: '%0' (provided) vs '%1' (found)">;
def err_allowable_clients_missing : Error<"allowable client missing from %0: '%1'">;
def err_allowable_clients_mismatch : Error<"allowable clients do not match: '%0' (provided) vs '%1' (found)">;
def warn_rpaths_missing : Warning<"runpath search paths missing from %0: '%1'">, InGroup<InstallAPIViolation>;
def warn_rpaths_mismatch : Warning<"runpath search paths do not match: '%0' (provided) vs '%1' (found)">, InGroup<InstallAPIViolation>;
} // end of Verification category.
} // end of InstallAPI component

View File

@@ -28,6 +28,9 @@ struct InstallAPIContext {
/// Library attributes that are typically passed as linker inputs.
BinaryAttrs BA;
/// Install names of reexported libraries of a library.
LibAttrs Reexports;
/// All headers that represent a library.
HeaderSeq InputHeaders;
@@ -80,6 +83,20 @@ private:
llvm::DenseMap<StringRef, HeaderType> KnownIncludes;
};
/// Lookup the dylib or TextAPI file location for a system library or framework.
/// The search paths provided are searched in order.
/// @rpath based libraries are not supported.
///
/// \param InstallName The install name for the library.
/// \param FrameworkSearchPaths Search paths to look up frameworks with.
/// \param LibrarySearchPaths Search paths to look up dylibs with.
/// \param SearchPaths Fallback search paths if library was not found in earlier
/// paths.
/// \return The full path of the library.
std::string findLibrary(StringRef InstallName, FileManager &FM,
ArrayRef<std::string> FrameworkSearchPaths,
ArrayRef<std::string> LibrarySearchPaths,
ArrayRef<std::string> SearchPaths);
} // namespace installapi
} // namespace clang

View File

@@ -24,6 +24,9 @@ enum class VerificationMode {
Pedantic,
};
using LibAttrs = llvm::StringMap<ArchitectureSet>;
using ReexportedInterfaces = llvm::SmallVector<llvm::MachO::InterfaceFile, 8>;
/// Service responsible to tracking state of verification across the
/// lifetime of InstallAPI.
/// As declarations are collected during AST traversal, they are
@@ -63,11 +66,12 @@ public:
DylibVerifier() = default;
DylibVerifier(llvm::MachO::Records &&Dylib, DiagnosticsEngine *Diag,
VerificationMode Mode, bool Demangle, StringRef DSYMPath)
: Dylib(std::move(Dylib)), Mode(Mode), Demangle(Demangle),
DSYMPath(DSYMPath), Exports(std::make_unique<SymbolSet>()),
Ctx(VerifierContext{Diag}) {}
DylibVerifier(llvm::MachO::Records &&Dylib, ReexportedInterfaces &&Reexports,
DiagnosticsEngine *Diag, VerificationMode Mode, bool Demangle,
StringRef DSYMPath)
: Dylib(std::move(Dylib)), Reexports(std::move(Reexports)), Mode(Mode),
Demangle(Demangle), DSYMPath(DSYMPath),
Exports(std::make_unique<SymbolSet>()), Ctx(VerifierContext{Diag}) {}
Result verify(GlobalRecord *R, const FrontendAttrs *FA);
Result verify(ObjCInterfaceRecord *R, const FrontendAttrs *FA);
@@ -77,6 +81,14 @@ public:
// Scan through dylib slices and report any remaining missing exports.
Result verifyRemainingSymbols();
/// Compare and report the attributes represented as
/// load commands in the dylib to the attributes provided via options.
bool verifyBinaryAttrs(const ArrayRef<Target> ProvidedTargets,
const BinaryAttrs &ProvidedBA,
const LibAttrs &ProvidedReexports,
const LibAttrs &ProvidedClients,
const LibAttrs &ProvidedRPaths, const FileType &FT);
/// Initialize target for verification.
void setTarget(const Target &T);
@@ -105,6 +117,10 @@ private:
bool shouldIgnoreObsolete(const Record *R, SymbolContext &SymCtx,
const Record *DR);
/// Check if declaration is exported from a reexported library. These
/// symbols should be omitted from the text-api file.
bool shouldIgnoreReexport(const Record *R, SymbolContext &SymCtx) const;
/// Compare the visibility declarations to the linkage of symbol found in
/// dylib.
Result compareVisibility(const Record *R, SymbolContext &SymCtx,
@@ -154,6 +170,9 @@ private:
// Symbols in dylib.
llvm::MachO::Records Dylib;
// Reexported interfaces apart of the library.
ReexportedInterfaces Reexports;
// Controls what class of violations to report.
VerificationMode Mode = VerificationMode::Invalid;

View File

@@ -23,6 +23,8 @@
#include "llvm/TextAPI/TextAPIWriter.h"
#include "llvm/TextAPI/Utils.h"
using Architecture = llvm::MachO::Architecture;
using ArchitectureSet = llvm::MachO::ArchitectureSet;
using SymbolFlags = llvm::MachO::SymbolFlags;
using RecordLinkage = llvm::MachO::RecordLinkage;
using Record = llvm::MachO::Record;

View File

@@ -7,6 +7,7 @@ set(LLVM_LINK_COMPONENTS
)
add_clang_library(clangInstallAPI
DiagnosticBuilderWrappers.cpp
DylibVerifier.cpp
FileList.cpp
Frontend.cpp

View File

@@ -0,0 +1,109 @@
//===- DiagnosticBuilderWrappers.cpp ----------------------------*- C++-*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "DiagnosticBuilderWrappers.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/TextAPI/Platform.h"
using clang::DiagnosticBuilder;
namespace llvm {
namespace MachO {
const DiagnosticBuilder &operator<<(const DiagnosticBuilder &DB,
const Architecture &Arch) {
DB.AddString(getArchitectureName(Arch));
return DB;
}
const DiagnosticBuilder &operator<<(const DiagnosticBuilder &DB,
const ArchitectureSet &ArchSet) {
DB.AddString(std::string(ArchSet));
return DB;
}
const DiagnosticBuilder &operator<<(const DiagnosticBuilder &DB,
const PlatformType &Platform) {
DB.AddString(getPlatformName(Platform));
return DB;
}
const DiagnosticBuilder &operator<<(const DiagnosticBuilder &DB,
const PlatformVersionSet &Platforms) {
std::string PlatformAsString;
raw_string_ostream Stream(PlatformAsString);
Stream << "[ ";
llvm::interleaveComma(
Platforms, Stream,
[&Stream](const std::pair<PlatformType, VersionTuple> &PV) {
Stream << getPlatformName(PV.first);
if (!PV.second.empty())
Stream << PV.second.getAsString();
});
Stream << " ]";
DB.AddString(Stream.str());
return DB;
}
const DiagnosticBuilder &operator<<(const DiagnosticBuilder &DB,
const FileType &Type) {
switch (Type) {
case FileType::MachO_Bundle:
DB.AddString("mach-o bundle");
return DB;
case FileType::MachO_DynamicLibrary:
DB.AddString("mach-o dynamic library");
return DB;
case FileType::MachO_DynamicLibrary_Stub:
DB.AddString("mach-o dynamic library stub");
return DB;
case FileType::TBD_V1:
DB.AddString("tbd-v1");
return DB;
case FileType::TBD_V2:
DB.AddString("tbd-v2");
return DB;
case FileType::TBD_V3:
DB.AddString("tbd-v3");
return DB;
case FileType::TBD_V4:
DB.AddString("tbd-v4");
return DB;
case FileType::TBD_V5:
DB.AddString("tbd-v5");
return DB;
case FileType::Invalid:
case FileType::All:
llvm_unreachable("Unexpected file type for diagnostics.");
}
}
const DiagnosticBuilder &operator<<(const DiagnosticBuilder &DB,
const PackedVersion &Version) {
std::string VersionString;
raw_string_ostream OS(VersionString);
OS << Version;
DB.AddString(OS.str());
return DB;
}
const clang::DiagnosticBuilder &
operator<<(const clang::DiagnosticBuilder &DB,
const StringMapEntry<ArchitectureSet> &LibAttr) {
std::string IFAsString;
raw_string_ostream OS(IFAsString);
OS << LibAttr.getKey() << " [ " << LibAttr.getValue() << " ]";
DB.AddString(OS.str());
return DB;
}
} // namespace MachO
} // namespace llvm

View File

@@ -0,0 +1,49 @@
//===- DiagnosticBuilderWrappers.h -----------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
/// Diagnostic wrappers for TextAPI types for error reporting.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_INSTALLAPI_DIAGNOSTICBUILDER_WRAPPER_H
#define LLVM_CLANG_INSTALLAPI_DIAGNOSTICBUILDER_WRAPPER_H
#include "clang/Basic/Diagnostic.h"
#include "llvm/TextAPI/Architecture.h"
#include "llvm/TextAPI/ArchitectureSet.h"
#include "llvm/TextAPI/InterfaceFile.h"
#include "llvm/TextAPI/Platform.h"
namespace llvm {
namespace MachO {
const clang::DiagnosticBuilder &operator<<(const clang::DiagnosticBuilder &DB,
const PlatformType &Platform);
const clang::DiagnosticBuilder &operator<<(const clang::DiagnosticBuilder &DB,
const PlatformVersionSet &Platforms);
const clang::DiagnosticBuilder &operator<<(const clang::DiagnosticBuilder &DB,
const Architecture &Arch);
const clang::DiagnosticBuilder &operator<<(const clang::DiagnosticBuilder &DB,
const ArchitectureSet &ArchSet);
const clang::DiagnosticBuilder &operator<<(const clang::DiagnosticBuilder &DB,
const FileType &Type);
const clang::DiagnosticBuilder &operator<<(const clang::DiagnosticBuilder &DB,
const PackedVersion &Version);
const clang::DiagnosticBuilder &
operator<<(const clang::DiagnosticBuilder &DB,
const StringMapEntry<ArchitectureSet> &LibAttr);
} // namespace MachO
} // namespace llvm
#endif // LLVM_CLANG_INSTALLAPI_DIAGNOSTICBUILDER_WRAPPER_H

View File

@@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
#include "clang/InstallAPI/DylibVerifier.h"
#include "DiagnosticBuilderWrappers.h"
#include "clang/InstallAPI/FrontendRecords.h"
#include "clang/InstallAPI/InstallAPIDiagnostic.h"
#include "llvm/Demangle/Demangle.h"
@@ -178,6 +179,22 @@ bool DylibVerifier::shouldIgnoreObsolete(const Record *R, SymbolContext &SymCtx,
return SymCtx.FA->Avail.isObsoleted();
}
bool DylibVerifier::shouldIgnoreReexport(const Record *R,
SymbolContext &SymCtx) const {
if (Reexports.empty())
return false;
for (const InterfaceFile &Lib : Reexports) {
if (!Lib.hasTarget(Ctx.Target))
continue;
if (auto Sym =
Lib.getSymbol(SymCtx.Kind, SymCtx.SymbolName, SymCtx.ObjCIFKind))
if ((*Sym)->hasTarget(Ctx.Target))
return true;
}
return false;
}
bool DylibVerifier::compareObjCInterfaceSymbols(const Record *R,
SymbolContext &SymCtx,
const ObjCInterfaceRecord *DR) {
@@ -383,6 +400,11 @@ DylibVerifier::Result DylibVerifier::verifyImpl(Record *R,
return Ctx.FrontendState;
}
if (shouldIgnoreReexport(R, SymCtx)) {
updateState(Result::Ignore);
return Ctx.FrontendState;
}
Record *DR =
findRecordFromSlice(Ctx.DylibSlice, SymCtx.SymbolName, SymCtx.Kind);
if (DR)
@@ -702,5 +724,179 @@ DylibVerifier::Result DylibVerifier::verifyRemainingSymbols() {
return getState();
}
bool DylibVerifier::verifyBinaryAttrs(const ArrayRef<Target> ProvidedTargets,
const BinaryAttrs &ProvidedBA,
const LibAttrs &ProvidedReexports,
const LibAttrs &ProvidedClients,
const LibAttrs &ProvidedRPaths,
const FileType &FT) {
assert(!Dylib.empty() && "Need dylib to verify.");
// Pickup any load commands that can differ per slice to compare.
TargetList DylibTargets;
LibAttrs DylibReexports;
LibAttrs DylibClients;
LibAttrs DylibRPaths;
for (const std::shared_ptr<RecordsSlice> &RS : Dylib) {
DylibTargets.push_back(RS->getTarget());
const BinaryAttrs &BinInfo = RS->getBinaryAttrs();
for (const StringRef LibName : BinInfo.RexportedLibraries)
DylibReexports[LibName].set(DylibTargets.back().Arch);
for (const StringRef LibName : BinInfo.AllowableClients)
DylibClients[LibName].set(DylibTargets.back().Arch);
// Compare attributes that are only representable in >= TBD_V5.
if (FT >= FileType::TBD_V5)
for (const StringRef Name : BinInfo.RPaths)
DylibRPaths[Name].set(DylibTargets.back().Arch);
}
// Check targets first.
ArchitectureSet ProvidedArchs = mapToArchitectureSet(ProvidedTargets);
ArchitectureSet DylibArchs = mapToArchitectureSet(DylibTargets);
if (ProvidedArchs != DylibArchs) {
Ctx.Diag->Report(diag::err_architecture_mismatch)
<< ProvidedArchs << DylibArchs;
return false;
}
auto ProvidedPlatforms = mapToPlatformVersionSet(ProvidedTargets);
auto DylibPlatforms = mapToPlatformVersionSet(DylibTargets);
if (ProvidedPlatforms != DylibPlatforms) {
const bool DiffMinOS =
mapToPlatformSet(ProvidedTargets) == mapToPlatformSet(DylibTargets);
if (DiffMinOS)
Ctx.Diag->Report(diag::warn_platform_mismatch)
<< ProvidedPlatforms << DylibPlatforms;
else {
Ctx.Diag->Report(diag::err_platform_mismatch)
<< ProvidedPlatforms << DylibPlatforms;
return false;
}
}
// Because InstallAPI requires certain attributes to match across architecture
// slices, take the first one to compare those with.
const BinaryAttrs &DylibBA = (*Dylib.begin())->getBinaryAttrs();
if (ProvidedBA.InstallName != DylibBA.InstallName) {
Ctx.Diag->Report(diag::err_install_name_mismatch)
<< ProvidedBA.InstallName << DylibBA.InstallName;
return false;
}
if (ProvidedBA.CurrentVersion != DylibBA.CurrentVersion) {
Ctx.Diag->Report(diag::err_current_version_mismatch)
<< ProvidedBA.CurrentVersion << DylibBA.CurrentVersion;
return false;
}
if (ProvidedBA.CompatVersion != DylibBA.CompatVersion) {
Ctx.Diag->Report(diag::err_compatibility_version_mismatch)
<< ProvidedBA.CompatVersion << DylibBA.CompatVersion;
return false;
}
if (ProvidedBA.AppExtensionSafe != DylibBA.AppExtensionSafe) {
Ctx.Diag->Report(diag::err_appextension_safe_mismatch)
<< (ProvidedBA.AppExtensionSafe ? "true" : "false")
<< (DylibBA.AppExtensionSafe ? "true" : "false");
return false;
}
if (!DylibBA.TwoLevelNamespace) {
Ctx.Diag->Report(diag::err_no_twolevel_namespace);
return false;
}
if (ProvidedBA.OSLibNotForSharedCache != DylibBA.OSLibNotForSharedCache) {
Ctx.Diag->Report(diag::err_shared_cache_eligiblity_mismatch)
<< (ProvidedBA.OSLibNotForSharedCache ? "true" : "false")
<< (DylibBA.OSLibNotForSharedCache ? "true" : "false");
return false;
}
if (ProvidedBA.ParentUmbrella.empty() && !DylibBA.ParentUmbrella.empty()) {
Ctx.Diag->Report(diag::err_parent_umbrella_missing)
<< "installAPI option" << DylibBA.ParentUmbrella;
return false;
}
if (!ProvidedBA.ParentUmbrella.empty() && DylibBA.ParentUmbrella.empty()) {
Ctx.Diag->Report(diag::err_parent_umbrella_missing)
<< "binary file" << ProvidedBA.ParentUmbrella;
return false;
}
if ((!ProvidedBA.ParentUmbrella.empty()) &&
(ProvidedBA.ParentUmbrella != DylibBA.ParentUmbrella)) {
Ctx.Diag->Report(diag::err_parent_umbrella_mismatch)
<< ProvidedBA.ParentUmbrella << DylibBA.ParentUmbrella;
return false;
}
auto CompareLibraries = [&](const LibAttrs &Provided, const LibAttrs &Dylib,
unsigned DiagID_missing, unsigned DiagID_mismatch,
bool Fatal = true) {
if (Provided == Dylib)
return true;
for (const llvm::StringMapEntry<ArchitectureSet> &PAttr : Provided) {
const auto DAttrIt = Dylib.find(PAttr.getKey());
if (DAttrIt == Dylib.end()) {
Ctx.Diag->Report(DiagID_missing) << "binary file" << PAttr;
if (Fatal)
return false;
}
if (PAttr.getValue() != DAttrIt->getValue()) {
Ctx.Diag->Report(DiagID_mismatch) << PAttr << *DAttrIt;
if (Fatal)
return false;
}
}
for (const llvm::StringMapEntry<ArchitectureSet> &DAttr : Dylib) {
const auto PAttrIt = Provided.find(DAttr.getKey());
if (PAttrIt == Provided.end()) {
Ctx.Diag->Report(DiagID_missing) << "installAPI option" << DAttr;
if (!Fatal)
continue;
return false;
}
if (PAttrIt->getValue() != DAttr.getValue()) {
if (Fatal)
llvm_unreachable("this case was already covered above.");
}
}
return true;
};
if (!CompareLibraries(ProvidedReexports, DylibReexports,
diag::err_reexported_libraries_missing,
diag::err_reexported_libraries_mismatch))
return false;
if (!CompareLibraries(ProvidedClients, DylibClients,
diag::err_allowable_clients_missing,
diag::err_allowable_clients_mismatch))
return false;
if (FT >= FileType::TBD_V5) {
// Ignore rpath differences if building an asan variant, since the
// compiler injects additional paths.
// FIXME: Building with sanitizers does not always change the install
// name, so this is not a foolproof solution.
if (!ProvidedBA.InstallName.ends_with("_asan")) {
if (!CompareLibraries(ProvidedRPaths, DylibRPaths,
diag::warn_rpaths_missing,
diag::warn_rpaths_mismatch,
/*Fatal=*/false))
return true;
}
}
return true;
}
} // namespace installapi
} // namespace clang

View File

@@ -162,4 +162,58 @@ std::unique_ptr<MemoryBuffer> createInputBuffer(InstallAPIContext &Ctx) {
return llvm::MemoryBuffer::getMemBufferCopy(Contents, BufferName);
}
std::string findLibrary(StringRef InstallName, FileManager &FM,
ArrayRef<std::string> FrameworkSearchPaths,
ArrayRef<std::string> LibrarySearchPaths,
ArrayRef<std::string> SearchPaths) {
auto getLibrary =
[&](const StringRef FullPath) -> std::optional<std::string> {
// Prefer TextAPI files when possible.
SmallString<PATH_MAX> TextAPIFilePath = FullPath;
replace_extension(TextAPIFilePath, ".tbd");
if (FM.getOptionalFileRef(TextAPIFilePath))
return std::string(TextAPIFilePath);
if (FM.getOptionalFileRef(FullPath))
return std::string(FullPath);
return std::nullopt;
};
const StringRef Filename = sys::path::filename(InstallName);
const bool IsFramework = sys::path::parent_path(InstallName)
.ends_with((Filename + ".framework").str());
if (IsFramework) {
for (const StringRef Path : FrameworkSearchPaths) {
SmallString<PATH_MAX> FullPath(Path);
sys::path::append(FullPath, Filename + StringRef(".framework"), Filename);
if (auto LibOrNull = getLibrary(FullPath))
return *LibOrNull;
}
} else {
// Copy Apple's linker behavior: If this is a .dylib inside a framework, do
// not search -L paths.
bool IsEmbeddedDylib = (sys::path::extension(InstallName) == ".dylib") &&
InstallName.contains(".framework/");
if (!IsEmbeddedDylib) {
for (const StringRef Path : LibrarySearchPaths) {
SmallString<PATH_MAX> FullPath(Path);
sys::path::append(FullPath, Filename);
if (auto LibOrNull = getLibrary(FullPath))
return *LibOrNull;
}
}
}
for (const StringRef Path : SearchPaths) {
SmallString<PATH_MAX> FullPath(Path);
sys::path::append(FullPath, InstallName);
if (auto LibOrNull = getLibrary(FullPath))
return *LibOrNull;
}
return {};
}
} // namespace clang::installapi

View File

@@ -0,0 +1,76 @@
; RUN: rm -rf %t
; RUN: split-file %s %t
; RUN: mkdir -p %t/System/Library/Frameworks
; RUN: cp -r %S/Inputs/Simple/Simple.framework %t/System/Library/Frameworks/
; RUN: yaml2obj %S/Inputs/Simple/Simple.yaml -o %t/Simple
; RUN: not clang-installapi -target x86_64h-apple-macos10.12 \
; RUN: -install_name Simple -current_version 3 -compatibility_version 2 \
; RUN: -o tmp.tbd --verify-against=%t/Simple 2>&1 | FileCheck -check-prefix=ARCHITECTURE %s
; ARCHITECTURE: error: architectures do not match: 'x86_64h' (provided) vs 'x86_64' (found)
; RUN: not clang-installapi -target x86_64-apple-macos10.12 \
; RUN: -install_name Simple -current_version 3 -compatibility_version 2 \
; RUN: -o tmp.tbd --verify-against=%t/Simple 2>&1 | FileCheck -check-prefix=INSTALL_NAME %s
; INSTALL_NAME: error: install_name does not match: 'Simple' (provided) vs '/System/Library/Frameworks/Simple.framework/Versions/A/Simple' (found)
; RUN: not clang-installapi -target x86_64-apple-macos10.12 \
; RUN: -install_name /System/Library/Frameworks/Simple.framework/Versions/A/Simple \
; RUN: -current_version 3 -compatibility_version 2 \
; RUN: -o tmp.tbd --verify-against=%t/Simple 2>&1 | FileCheck -check-prefix=CURRENT_VERSION %s
; CURRENT_VERSION: error: current_version does not match: '3' (provided) vs '1.2.3' (found)
; RUN: not clang-installapi -target x86_64-apple-macos10.12 \
; RUN: -install_name /System/Library/Frameworks/Simple.framework/Versions/A/Simple \
; RUN: -current_version 1.2.3 -compatibility_version 2 \
; RUN: -o tmp.tbd --verify-against=%t/Simple 2>&1 | FileCheck -check-prefix=COMPATIBILITY_VERSION %s
; COMPATIBILITY_VERSION: error: compatibility_version does not match: '2' (provided) vs '1' (found)
; RUN: not clang-installapi -target x86_64-apple-macos10.12 \
; RUN: -install_name /System/Library/Frameworks/Simple.framework/Versions/A/Simple \
; RUN: -current_version 1.2.3 -compatibility_version 1 -fapplication-extension \
; RUN: -o tmp.tbd --verify-against=%t/Simple 2>&1 | FileCheck -check-prefix=APPEXTSAFE %s
; APPEXTSAFE: error: ApplicationExtensionSafe flag does not match: 'true' (provided) vs 'false' (found)
; RUN: not clang-installapi -target x86_64-apple-macos10.12 \
; RUN: -install_name /System/Library/Frameworks/Simple.framework/Versions/A/Simple \
; RUN: -current_version 1.2.3 -compatibility_version 1 -not_for_dyld_shared_cache \
; RUN: -o tmp.tbd --verify-against=%t/Simple 2>&1 | FileCheck -check-prefix=SHARED_CACHE %s
; SHARED_CACHE: error: NotForDyldSharedCache flag does not match: 'true' (provided) vs 'false' (found)
; RUN: not clang-installapi -target x86_64-apple-macos10.12 \
; RUN: -install_name /System/Library/Frameworks/Simple.framework/Versions/A/Simple \
; RUN: -current_version 1.2.3 -compatibility_version 1 \
; RUN: -allowable_client Foo -allowable_client Bar \
; RUN: -o tmp.tbd --verify-against=%t/Simple 2>&1 | FileCheck -check-prefix=ALLOWABLE %s
; ALLOWABLE: error: allowable client missing from binary file: 'Foo [ x86_64 ]'
; RUN: not clang-installapi -target x86_64-apple-macos10.12 \
; RUN: -install_name /System/Library/Frameworks/Simple.framework/Versions/A/Simple \
; RUN: -current_version 1.2.3 -compatibility_version 1 -reexport_library %t/Foo.tbd \
; RUN: -o tmp.tbd --verify-against=%t/Simple 2>&1 | FileCheck -check-prefix=REEXPORT %s
; REEXPORT: error: re-exported library missing from binary file: 'Foo [ x86_64 ]'
; RUN: not clang-installapi -target x86_64-apple-macos10.12 \
; RUN: -install_name /System/Library/Frameworks/Simple.framework/Versions/A/Simple \
; RUN: -current_version 1.2.3 -compatibility_version 1 -umbrella Bogus \
; RUN: -o tmp.tbd --verify-against=%t/Simple 2>&1 | FileCheck -check-prefix=UMBRELLA %s
; UMBRELLA: error: parent umbrella missing from binary file: 'Bogus'
;--- Foo.tbd
{
"main_library": {
"install_names": [
{
"name": "Foo"
}
],
"target_info": [
{
"min_deployment": "13.0",
"target": "arm64-macos"
}
]
},
"tapi_tbd_version": 5
}

View File

@@ -13,3 +13,9 @@
// RUN: --verify-mode=Invalid -o tmp.tbd 2> %t
// RUN: FileCheck --check-prefix INVALID_VERIFY_MODE -input-file %t %s
// INVALID_VERIFY_MODE: error: invalid value 'Invalid' in '--verify-mode=Invalid'
/// Check that invalid sysroot is fatal.
// RUN: not clang-installapi -install_name Foo -target arm64-apple-ios13 \
// RUN: -isysroot /no/such/path -o tmp.tbd 2> %t
// RUN: FileCheck --check-prefix INVALID_ISYSROOT -input-file %t %s
// INVALID_ISYSROOT: error: no such sysroot directory: {{.*}}no/such/path'

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,663 @@
; RUN: rm -rf %t
; RUN: split-file %s %t
; RUN: yaml2obj %t/RPath.yaml -o %t/RPath
; RUN: clang-installapi --filetype=tbd-v5 \
; RUN: -target arm64-apple-macos13.0 -target x86_64-apple-macos13.0 \
; RUN: -install_name @rpath/Frameworks/RPath.framework/Versions/A/RPath \
; RUN: -current_version 1 -compatibility_version 1 \
; RUN: --extra-public-header=%t/public.h \
; RUN: -o %t/RPath_warnings.tbd \
; RUN: --verify-against=%t/RPath \
; RUN: --verify-mode=Pedantic 2>&1 | FileCheck %s --check-prefix=MISSING
; RUN: llvm-readtapi --compare %t/RPath_warnings.tbd %t/expected_no_rpaths.tbd
; MISSING: warning: runpath search paths missing from installAPI option: '@loader_path/../../../SharedFrameworks/ [ x86_64 arm64 ]'
; MISSING: warning: runpath search paths missing from installAPI option: '@loader_path/../../PrivateFrameworks/ [ x86_64 arm64 ]'
; RUN: clang-installapi --filetype=tbd-v5 \
; RUN: -target arm64-apple-macos13.0 -target x86_64-apple-macos13.0 \
; RUN: -install_name @rpath/Frameworks/RPath.framework/Versions/A/RPath \
; RUN: -current_version 1 -compatibility_version 1 \
; RUN: --extra-public-header=%t/public.h \
; RUN: -Xarch_arm64 -rpath @loader_path/../../../SharedFrameworks/ \
; RUN: -o %t/RPath_Xarch.tbd \
; RUN: --verify-against=%t/RPath \
; RUN: --verify-mode=Pedantic 2>&1 | FileCheck %s --check-prefix=XARCH
; RUN: llvm-readtapi --compare %t/RPath_Xarch.tbd %t/expected_xarch_rpaths.tbd
; XARCH: warning: runpath search paths do not match: '@loader_path/../../../SharedFrameworks/ [ arm64 ]' (provided) vs '@loader_path/../../../SharedFrameworks/ [ x86_64 arm64 ]'
; XARCH: warning: runpath search paths missing from installAPI option: '@loader_path/../../PrivateFrameworks/ [ x86_64 arm64 ]'
; RUN: clang-installapi --filetype=tbd-v5 \
; RUN: -target arm64-apple-macos13.0 -target x86_64-apple-macos13.0 \
; RUN: -install_name @rpath/Frameworks/RPath.framework/Versions/A/RPath \
; RUN: -current_version 1 -compatibility_version 1 \
; RUN: --extra-public-header=%t/public.h \
; RUN: -rpath @loader_path/../../../SharedFrameworks/ \
; RUN: -rpath @loader_path/../../PrivateFrameworks/ \
; RUN: --verify-against=%t/RPath --verify-mode=Pedantic \
; RUN: -o %t/RPath.tbd 2>&1 | FileCheck -allow-empty %s
; RUN: llvm-readtapi --compare %t/RPath.tbd %t/expected.tbd
CHECK-NOT: error
CHECK-NOT: warning
;--- public.h
extern int publicGlobalVariable;
;--- expected.tbd
{
"main_library": {
"exported_symbols": [
{
"data": {
"global": [
"_publicGlobalVariable"
]
}
}
],
"flags": [
{
"attributes": [
"not_app_extension_safe"
]
}
],
"install_names": [
{
"name": "@rpath/Frameworks/RPath.framework/Versions/A/RPath"
}
],
"rpaths": [
{
"paths": [
"@loader_path/../../../SharedFrameworks/",
"@loader_path/../../PrivateFrameworks/"
]
}
],
"target_info": [
{
"min_deployment": "13.0",
"target": "x86_64-macos"
},
{
"min_deployment": "13.0",
"target": "arm64-macos"
}
]
},
"tapi_tbd_version": 5
}
;--- expected_no_rpaths.tbd
{
"main_library": {
"exported_symbols": [
{
"data": {
"global": [
"_publicGlobalVariable"
]
}
}
],
"flags": [
{
"attributes": [
"not_app_extension_safe"
]
}
],
"install_names": [
{
"name": "@rpath/Frameworks/RPath.framework/Versions/A/RPath"
}
],
"target_info": [
{
"min_deployment": "13.0",
"target": "x86_64-macos"
},
{
"min_deployment": "13.0",
"target": "arm64-macos"
}
]
},
"tapi_tbd_version": 5
}
;--- expected_xarch_rpaths.tbd
{
"main_library": {
"exported_symbols": [
{
"data": {
"global": [
"_publicGlobalVariable"
]
}
}
],
"flags": [
{
"attributes": [
"not_app_extension_safe"
]
}
],
"install_names": [
{
"name": "@rpath/Frameworks/RPath.framework/Versions/A/RPath"
}
],
"rpaths": [
{
"paths": [
"@loader_path/../../../SharedFrameworks/"
],
"targets": [
"arm64-macos"
]
}
],
"target_info": [
{
"min_deployment": "13.0",
"target": "x86_64-macos"
},
{
"min_deployment": "13.0",
"target": "arm64-macos"
}
]
},
"tapi_tbd_version": 5
}
;--- RPath.yaml
--- !fat-mach-o
FatHeader:
magic: 0xCAFEBABE
nfat_arch: 2
FatArchs:
- cputype: 0x1000007
cpusubtype: 0x3
offset: 0x1000
size: 12408
align: 12
- cputype: 0x100000C
cpusubtype: 0x0
offset: 0x8000
size: 33312
align: 14
Slices:
- !mach-o
FileHeader:
magic: 0xFEEDFACF
cputype: 0x1000007
cpusubtype: 0x3
filetype: 0x6
ncmds: 16
sizeofcmds: 1072
flags: 0x100085
reserved: 0x0
LoadCommands:
- cmd: LC_SEGMENT_64
cmdsize: 152
segname: __TEXT
vmaddr: 0
vmsize: 8192
fileoff: 0
filesize: 8192
maxprot: 5
initprot: 5
nsects: 1
flags: 0
Sections:
- sectname: __text
segname: __TEXT
addr: 0x1050
size: 0
offset: 0x1050
align: 0
reloff: 0x0
nreloc: 0
flags: 0x80000000
reserved1: 0x0
reserved2: 0x0
reserved3: 0x0
content: ''
- cmd: LC_SEGMENT_64
cmdsize: 152
segname: __DATA_CONST
vmaddr: 8192
vmsize: 4096
fileoff: 8192
filesize: 4096
maxprot: 3
initprot: 3
nsects: 1
flags: 16
Sections:
- sectname: __objc_imageinfo
segname: __DATA_CONST
addr: 0x2000
size: 8
offset: 0x2000
align: 0
reloff: 0x0
nreloc: 0
flags: 0x0
reserved1: 0x0
reserved2: 0x0
reserved3: 0x0
content: '0000000040000000'
- cmd: LC_SEGMENT_64
cmdsize: 152
segname: __DATA
vmaddr: 12288
vmsize: 4096
fileoff: 12288
filesize: 0
maxprot: 3
initprot: 3
nsects: 1
flags: 0
Sections:
- sectname: __common
segname: __DATA
addr: 0x3000
size: 4
offset: 0x0
align: 2
reloff: 0x0
nreloc: 0
flags: 0x1
reserved1: 0x0
reserved2: 0x0
reserved3: 0x0
- cmd: LC_SEGMENT_64
cmdsize: 72
segname: __LINKEDIT
vmaddr: 16384
vmsize: 120
fileoff: 12288
filesize: 120
maxprot: 1
initprot: 1
nsects: 0
flags: 0
- cmd: LC_DYLD_INFO_ONLY
cmdsize: 48
rebase_off: 0
rebase_size: 0
bind_off: 0
bind_size: 0
weak_bind_off: 0
weak_bind_size: 0
lazy_bind_off: 0
lazy_bind_size: 0
export_off: 12288
export_size: 32
- cmd: LC_SYMTAB
cmdsize: 24
symoff: 12328
nsyms: 2
stroff: 12360
strsize: 48
- cmd: LC_DYSYMTAB
cmdsize: 80
ilocalsym: 0
nlocalsym: 0
iextdefsym: 0
nextdefsym: 1
iundefsym: 1
nundefsym: 1
tocoff: 0
ntoc: 0
modtaboff: 0
nmodtab: 0
extrefsymoff: 0
nextrefsyms: 0
indirectsymoff: 0
nindirectsyms: 0
extreloff: 0
nextrel: 0
locreloff: 0
nlocrel: 0
- cmd: LC_RPATH
cmdsize: 56
path: 12
Content: '@loader_path/../../../SharedFrameworks/'
ZeroPadBytes: 5
- cmd: LC_RPATH
cmdsize: 56
path: 12
Content: '@loader_path/../../PrivateFrameworks/'
ZeroPadBytes: 7
- cmd: LC_ID_DYLIB
cmdsize: 80
dylib:
name: 24
timestamp: 0
current_version: 65536
compatibility_version: 65536
Content: '@rpath/Frameworks/RPath.framework/Versions/A/RPath'
ZeroPadBytes: 6
- cmd: LC_UUID
cmdsize: 24
uuid: 4C4C4489-5555-3144-A1D1-28C8EA66FB24
- cmd: LC_BUILD_VERSION
cmdsize: 32
platform: 1
minos: 851968
sdk: 983040
ntools: 1
Tools:
- tool: 4
version: 1245184
- cmd: LC_LOAD_DYLIB
cmdsize: 56
dylib:
name: 24
timestamp: 0
current_version: 14942208
compatibility_version: 65536
Content: '/usr/lib/libobjc.A.dylib'
ZeroPadBytes: 8
- cmd: LC_LOAD_DYLIB
cmdsize: 56
dylib:
name: 24
timestamp: 0
current_version: 88539136
compatibility_version: 65536
Content: '/usr/lib/libSystem.B.dylib'
ZeroPadBytes: 6
- cmd: LC_FUNCTION_STARTS
cmdsize: 16
dataoff: 12320
datasize: 8
- cmd: LC_DATA_IN_CODE
cmdsize: 16
dataoff: 12328
datasize: 0
LinkEditData:
ExportTrie:
TerminalSize: 0
NodeOffset: 0
Name: ''
Flags: 0x0
Address: 0x0
Other: 0x0
ImportName: ''
Children:
- TerminalSize: 3
NodeOffset: 25
Name: _publicGlobalVariable
Flags: 0x0
Address: 0x3000
Other: 0x0
ImportName: ''
NameList:
- n_strx: 2
n_type: 0xF
n_sect: 3
n_desc: 0
n_value: 12288
- n_strx: 24
n_type: 0x1
n_sect: 0
n_desc: 512
n_value: 0
StringTable:
- ' '
- _publicGlobalVariable
- dyld_stub_binder
- ''
- ''
- ''
- ''
- ''
- ''
- ''
- !mach-o
FileHeader:
magic: 0xFEEDFACF
cputype: 0x100000C
cpusubtype: 0x0
filetype: 0x6
ncmds: 17
sizeofcmds: 1088
flags: 0x100085
reserved: 0x0
LoadCommands:
- cmd: LC_SEGMENT_64
cmdsize: 152
segname: __TEXT
vmaddr: 0
vmsize: 16384
fileoff: 0
filesize: 16384
maxprot: 5
initprot: 5
nsects: 1
flags: 0
Sections:
- sectname: __text
segname: __TEXT
addr: 0x1060
size: 0
offset: 0x1060
align: 0
reloff: 0x0
nreloc: 0
flags: 0x80000000
reserved1: 0x0
reserved2: 0x0
reserved3: 0x0
content: ''
- cmd: LC_SEGMENT_64
cmdsize: 152
segname: __DATA_CONST
vmaddr: 16384
vmsize: 16384
fileoff: 16384
filesize: 16384
maxprot: 3
initprot: 3
nsects: 1
flags: 16
Sections:
- sectname: __objc_imageinfo
segname: __DATA_CONST
addr: 0x4000
size: 8
offset: 0x4000
align: 0
reloff: 0x0
nreloc: 0
flags: 0x0
reserved1: 0x0
reserved2: 0x0
reserved3: 0x0
content: '0000000040000000'
- cmd: LC_SEGMENT_64
cmdsize: 152
segname: __DATA
vmaddr: 32768
vmsize: 16384
fileoff: 32768
filesize: 0
maxprot: 3
initprot: 3
nsects: 1
flags: 0
Sections:
- sectname: __common
segname: __DATA
addr: 0x8000
size: 4
offset: 0x0
align: 2
reloff: 0x0
nreloc: 0
flags: 0x1
reserved1: 0x0
reserved2: 0x0
reserved3: 0x0
- cmd: LC_SEGMENT_64
cmdsize: 72
segname: __LINKEDIT
vmaddr: 49152
vmsize: 544
fileoff: 32768
filesize: 544
maxprot: 1
initprot: 1
nsects: 0
flags: 0
- cmd: LC_DYLD_INFO_ONLY
cmdsize: 48
rebase_off: 0
rebase_size: 0
bind_off: 0
bind_size: 0
weak_bind_off: 0
weak_bind_size: 0
lazy_bind_off: 0
lazy_bind_size: 0
export_off: 32768
export_size: 32
- cmd: LC_SYMTAB
cmdsize: 24
symoff: 32808
nsyms: 2
stroff: 32840
strsize: 48
- cmd: LC_DYSYMTAB
cmdsize: 80
ilocalsym: 0
nlocalsym: 0
iextdefsym: 0
nextdefsym: 1
iundefsym: 1
nundefsym: 1
tocoff: 0
ntoc: 0
modtaboff: 0
nmodtab: 0
extrefsymoff: 0
nextrefsyms: 0
indirectsymoff: 0
nindirectsyms: 0
extreloff: 0
nextrel: 0
locreloff: 0
nlocrel: 0
- cmd: LC_RPATH
cmdsize: 56
path: 12
Content: '@loader_path/../../../SharedFrameworks/'
ZeroPadBytes: 5
- cmd: LC_RPATH
cmdsize: 56
path: 12
Content: '@loader_path/../../PrivateFrameworks/'
ZeroPadBytes: 7
- cmd: LC_ID_DYLIB
cmdsize: 80
dylib:
name: 24
timestamp: 0
current_version: 65536
compatibility_version: 65536
Content: '@rpath/Frameworks/RPath.framework/Versions/A/RPath'
ZeroPadBytes: 6
- cmd: LC_UUID
cmdsize: 24
uuid: 4C4C440D-5555-3144-A18B-DB67A0A12202
- cmd: LC_BUILD_VERSION
cmdsize: 32
platform: 1
minos: 851968
sdk: 983040
ntools: 1
Tools:
- tool: 4
version: 1245184
- cmd: LC_LOAD_DYLIB
cmdsize: 56
dylib:
name: 24
timestamp: 0
current_version: 14942208
compatibility_version: 65536
Content: '/usr/lib/libobjc.A.dylib'
ZeroPadBytes: 8
- cmd: LC_LOAD_DYLIB
cmdsize: 56
dylib:
name: 24
timestamp: 0
current_version: 88539136
compatibility_version: 65536
Content: '/usr/lib/libSystem.B.dylib'
ZeroPadBytes: 6
- cmd: LC_FUNCTION_STARTS
cmdsize: 16
dataoff: 32800
datasize: 8
- cmd: LC_DATA_IN_CODE
cmdsize: 16
dataoff: 32808
datasize: 0
- cmd: LC_CODE_SIGNATURE
cmdsize: 16
dataoff: 32896
datasize: 416
LinkEditData:
ExportTrie:
TerminalSize: 0
NodeOffset: 0
Name: ''
Flags: 0x0
Address: 0x0
Other: 0x0
ImportName: ''
Children:
- TerminalSize: 4
NodeOffset: 25
Name: _publicGlobalVariable
Flags: 0x0
Address: 0x8000
Other: 0x0
ImportName: ''
NameList:
- n_strx: 2
n_type: 0xF
n_sect: 3
n_desc: 0
n_value: 32768
- n_strx: 24
n_type: 0x1
n_sect: 0
n_desc: 512
n_value: 0
StringTable:
- ' '
- _publicGlobalVariable
- dyld_stub_binder
- ''
- ''
- ''
- ''
- ''
- ''
- ''
FunctionStarts: [ 0x1060 ]
...

View File

@@ -1,4 +1,5 @@
set(LLVM_LINK_COMPONENTS
BinaryFormat
Support
TargetParser
TextAPI

View File

@@ -101,6 +101,16 @@ static bool run(ArrayRef<const char *> Args, const char *ProgName) {
if (Diag->hasErrorOccurred())
return EXIT_FAILURE;
if (!Opts.DriverOpts.DylibToVerify.empty()) {
TargetList Targets;
llvm::for_each(Opts.DriverOpts.Targets,
[&](const auto &T) { Targets.push_back(T.first); });
if (!Ctx.Verifier->verifyBinaryAttrs(Targets, Ctx.BA, Ctx.Reexports,
Opts.LinkerOpts.AllowableClients,
Opts.LinkerOpts.RPaths, Ctx.FT))
return EXIT_FAILURE;
};
// Set up compilation.
std::unique_ptr<CompilerInstance> CI(new CompilerInstance());
CI->setFileManager(FM.get());
@@ -108,7 +118,7 @@ static bool run(ArrayRef<const char *> Args, const char *ProgName) {
if (!CI->hasDiagnostics())
return EXIT_FAILURE;
// Execute and gather AST results.
// Execute, verify and gather AST results.
// An invocation is ran for each unique target triple and for each header
// access level.
for (const auto &[Targ, Trip] : Opts.DriverOpts.Targets) {
@@ -136,10 +146,25 @@ static bool run(ArrayRef<const char *> Args, const char *ProgName) {
// Assign attributes for serialization.
InterfaceFile IF(Ctx.Verifier->getExports());
// Assign attributes that are the same per slice first.
for (const auto &TargetInfo : Opts.DriverOpts.Targets) {
IF.addTarget(TargetInfo.first);
IF.setFromBinaryAttrs(Ctx.BA, TargetInfo.first);
}
// Then assign potentially different attributes per slice after.
auto assignLibAttrs =
[&IF](
const auto &Attrs,
std::function<void(InterfaceFile *, StringRef, const Target &)> Add) {
for (const auto &Lib : Attrs)
for (const auto &T : IF.targets(Lib.getValue()))
Add(&IF, Lib.getKey(), T);
};
assignLibAttrs(Opts.LinkerOpts.AllowableClients,
&InterfaceFile::addAllowableClient);
assignLibAttrs(Opts.LinkerOpts.RPaths, &InterfaceFile::addRPath);
assignLibAttrs(Ctx.Reexports, &InterfaceFile::addReexportedLibrary);
// Write output file and perform CI cleanup.
if (auto Err = TextAPIWriter::writeToStream(*Out, IF, Ctx.FT)) {

View File

@@ -17,11 +17,23 @@ include "llvm/Option/OptParser.td"
/////////
// Options
// TextAPI options.
//
/// TextAPI options.
//
def filetype : Joined<["--"], "filetype=">,
HelpText<"Specify the output file type (tbd-v4 or tbd-v5)">;
def not_for_dyld_shared_cache : Joined<["-"], "not_for_dyld_shared_cache">,
HelpText<"Mark library as shared cache ineligible">;
// Verification options.
//
/// Debugging or logging options.
//
def t: Flag<["-"], "t">,
HelpText<"Logs each dylib loaded for InstallAPI. Useful for debugging problems with search paths where the wrong library is loaded.">;
//
/// Verification options.
//
def verify_against : Separate<["-"], "verify-against">,
HelpText<"Verify the specified dynamic library/framework against the headers">;
def verify_against_EQ : Joined<["--"], "verify-against=">, Alias<verify_against>;
@@ -32,7 +44,9 @@ def demangle : Flag<["--", "-"], "demangle">,
def dsym: Joined<["--"], "dsym=">,
MetaVarName<"<path>">, HelpText<"Specify dSYM path for enriched diagnostics.">;
// Additional input options.
//
/// Additional input options.
//
def extra_project_header : Separate<["-"], "extra-project-header">,
MetaVarName<"<path>">,
HelpText<"Add additional project header location for parsing">;
@@ -75,3 +89,23 @@ def project_umbrella_header : Separate<["-"], "project-umbrella-header">,
MetaVarName<"<path>">, HelpText<"Specify the project umbrella header location">;
def project_umbrella_header_EQ : Joined<["--"], "project-umbrella-header=">,
Alias<project_umbrella_header>;
//
/// Overidden clang options for different behavior.
//
// Clang's Xarch does not support options that require arguments.
// But is supported for InstallAPI generation.
def Xarch__ : Joined<["-"], "Xarch_">;
def allowable_client : Separate<["-"], "allowable_client">,
HelpText<"Restricts what can link against the dynamic library being created">;
def rpath: Separate<["-"], "rpath">,
HelpText<"Add path to the runpath search path list for the dynamic library being created.">;
def reexport_l : Joined<["-"], "reexport-l">,
HelpText<"Re-export the specified library">;
def reexport_library : Separate<["-"], "reexport_library">, MetaVarName<"<path>">,
HelpText<"Re-export the specified library">;
def reexport_framework : Separate<["-"], "reexport_framework">,
HelpText<"Re-export the specified framework">;

View File

@@ -7,14 +7,17 @@
//===----------------------------------------------------------------------===//
#include "Options.h"
#include "clang/Basic/DiagnosticIDs.h"
#include "clang/Driver/Driver.h"
#include "clang/Frontend/FrontendDiagnostic.h"
#include "clang/InstallAPI/FileList.h"
#include "clang/InstallAPI/HeaderFile.h"
#include "clang/InstallAPI/InstallAPIDiagnostic.h"
#include "llvm/BinaryFormat/Magic.h"
#include "llvm/Support/Program.h"
#include "llvm/TargetParser/Host.h"
#include "llvm/TextAPI/DylibReader.h"
#include "llvm/TextAPI/TextAPIError.h"
#include "llvm/TextAPI/TextAPIReader.h"
#include "llvm/TextAPI/TextAPIWriter.h"
using namespace llvm;
@@ -137,6 +140,56 @@ bool Options::processDriverOptions(InputArgList &Args) {
return true;
}
bool Options::processInstallAPIXOptions(InputArgList &Args) {
for (arg_iterator It = Args.begin(), End = Args.end(); It != End; ++It) {
if ((*It)->getOption().matches(OPT_Xarch__)) {
if (!processXarchOption(Args, It))
return false;
}
}
// TODO: Add support for the all of the X* options installapi supports.
return true;
}
bool Options::processXarchOption(InputArgList &Args, arg_iterator Curr) {
Arg *CurrArg = *Curr;
Architecture Arch = getArchitectureFromName(CurrArg->getValue(0));
if (Arch == AK_unknown) {
Diags->Report(diag::err_drv_invalid_arch_name)
<< CurrArg->getAsString(Args);
return false;
}
auto NextIt = std::next(Curr);
if (NextIt == Args.end()) {
Diags->Report(diag::err_drv_missing_argument)
<< CurrArg->getAsString(Args) << 1;
return false;
}
// InstallAPI has a limited understanding of supported Xarch options.
// Currently this is restricted to linker inputs.
const Arg *NextArg = *NextIt;
switch (NextArg->getOption().getID()) {
case OPT_allowable_client:
case OPT_reexport_l:
case OPT_reexport_framework:
case OPT_reexport_library:
case OPT_rpath:
break;
default:
Diags->Report(diag::err_drv_invalid_argument_to_option)
<< NextArg->getAsString(Args) << CurrArg->getAsString(Args);
return false;
}
ArgToArchMap[NextArg] = Arch;
CurrArg->claim();
return true;
}
bool Options::processLinkerOptions(InputArgList &Args) {
// Handle required arguments.
if (const Arg *A = Args.getLastArg(drv::OPT_install__name))
@@ -153,6 +206,12 @@ bool Options::processLinkerOptions(InputArgList &Args) {
if (auto *Arg = Args.getLastArg(drv::OPT_compatibility__version))
LinkerOpts.CompatVersion.parse64(Arg->getValue());
if (auto *Arg = Args.getLastArg(drv::OPT_compatibility__version))
LinkerOpts.CompatVersion.parse64(Arg->getValue());
if (auto *Arg = Args.getLastArg(drv::OPT_umbrella))
LinkerOpts.ParentUmbrella = Arg->getValue();
LinkerOpts.IsDylib = Args.hasArg(drv::OPT_dynamiclib);
LinkerOpts.AppExtensionSafe = Args.hasFlag(
@@ -164,12 +223,24 @@ bool Options::processLinkerOptions(InputArgList &Args) {
if (::getenv("LD_APPLICATION_EXTENSION_SAFE") != nullptr)
LinkerOpts.AppExtensionSafe = true;
// Capture library paths.
PathSeq LibraryPaths;
for (const Arg *A : Args.filtered(drv::OPT_L)) {
LibraryPaths.emplace_back(A->getValue());
A->claim();
}
if (!LibraryPaths.empty())
LinkerOpts.LibPaths = std::move(LibraryPaths);
return true;
}
// NOTE: Do not claim any arguments, as they will be passed along for CC1
// invocations.
bool Options::processFrontendOptions(InputArgList &Args) {
// Do not claim any arguments, as they will be passed along for CC1
// invocations.
// Capture language mode.
if (auto *A = Args.getLastArgNoClaim(drv::OPT_x)) {
FEOpts.LangMode = llvm::StringSwitch<clang::Language>(A->getValue())
.Case("c", clang::Language::C)
@@ -191,6 +262,54 @@ bool Options::processFrontendOptions(InputArgList &Args) {
FEOpts.LangMode = clang::Language::ObjCXX;
}
// Capture Sysroot.
if (const Arg *A = Args.getLastArgNoClaim(drv::OPT_isysroot)) {
SmallString<PATH_MAX> Path(A->getValue());
FM->makeAbsolutePath(Path);
if (!FM->getOptionalDirectoryRef(Path)) {
Diags->Report(diag::err_missing_sysroot) << Path;
return false;
}
FEOpts.ISysroot = std::string(Path);
} else if (FEOpts.ISysroot.empty()) {
// Mirror CLANG and obtain the isysroot from the SDKROOT environment
// variable, if it wasn't defined by the command line.
if (auto *Env = ::getenv("SDKROOT")) {
if (StringRef(Env) != "/" && llvm::sys::path::is_absolute(Env) &&
FM->getOptionalFileRef(Env))
FEOpts.ISysroot = Env;
}
}
// Capture system frameworks.
// TODO: Support passing framework paths per platform.
for (const Arg *A : Args.filtered(drv::OPT_iframework))
FEOpts.SystemFwkPaths.emplace_back(A->getValue());
// Capture framework paths.
PathSeq FrameworkPaths;
for (const Arg *A : Args.filtered(drv::OPT_F))
FrameworkPaths.emplace_back(A->getValue());
if (!FrameworkPaths.empty())
FEOpts.FwkPaths = std::move(FrameworkPaths);
// Add default framework/library paths.
PathSeq DefaultLibraryPaths = {"/usr/lib", "/usr/local/lib"};
PathSeq DefaultFrameworkPaths = {"/Library/Frameworks",
"/System/Library/Frameworks"};
for (const StringRef LibPath : DefaultLibraryPaths) {
SmallString<PATH_MAX> Path(FEOpts.ISysroot);
sys::path::append(Path, LibPath);
LinkerOpts.LibPaths.emplace_back(Path.str());
}
for (const StringRef FwkPath : DefaultFrameworkPaths) {
SmallString<PATH_MAX> Path(FEOpts.ISysroot);
sys::path::append(Path, FwkPath);
FEOpts.SystemFwkPaths.emplace_back(Path.str());
}
return true;
}
@@ -224,6 +343,9 @@ Options::processAndFilterOutInstallAPIOptions(ArrayRef<const char *> Args) {
MissingArgCount, Visibility());
// Capture InstallAPI only driver options.
if (!processInstallAPIXOptions(ParsedArgs))
return {};
DriverOpts.Demangle = ParsedArgs.hasArg(OPT_demangle);
if (auto *A = ParsedArgs.getLastArg(OPT_filetype)) {
@@ -256,6 +378,42 @@ Options::processAndFilterOutInstallAPIOptions(ArrayRef<const char *> Args) {
if (const Arg *A = ParsedArgs.getLastArg(OPT_dsym))
DriverOpts.DSYMPath = A->getValue();
DriverOpts.TraceLibraryLocation = ParsedArgs.hasArg(OPT_t);
// Linker options not handled by clang driver.
LinkerOpts.OSLibNotForSharedCache =
ParsedArgs.hasArg(OPT_not_for_dyld_shared_cache);
for (const Arg *A : ParsedArgs.filtered(OPT_allowable_client)) {
LinkerOpts.AllowableClients[A->getValue()] =
ArgToArchMap.count(A) ? ArgToArchMap[A] : ArchitectureSet();
A->claim();
}
for (const Arg *A : ParsedArgs.filtered(OPT_reexport_l)) {
LinkerOpts.ReexportedLibraries[A->getValue()] =
ArgToArchMap.count(A) ? ArgToArchMap[A] : ArchitectureSet();
A->claim();
}
for (const Arg *A : ParsedArgs.filtered(OPT_reexport_library)) {
LinkerOpts.ReexportedLibraryPaths[A->getValue()] =
ArgToArchMap.count(A) ? ArgToArchMap[A] : ArchitectureSet();
A->claim();
}
for (const Arg *A : ParsedArgs.filtered(OPT_reexport_framework)) {
LinkerOpts.ReexportedFrameworks[A->getValue()] =
ArgToArchMap.count(A) ? ArgToArchMap[A] : ArchitectureSet();
A->claim();
}
for (const Arg *A : ParsedArgs.filtered(OPT_rpath)) {
LinkerOpts.RPaths[A->getValue()] =
ArgToArchMap.count(A) ? ArgToArchMap[A] : ArchitectureSet();
A->claim();
}
// Handle exclude & extra header directories or files.
auto handleAdditionalInputArgs = [&](PathSeq &Headers,
clang::installapi::ID OptID) {
@@ -336,6 +494,22 @@ Options::Options(DiagnosticsEngine &Diag, FileManager *FM,
if (!processFrontendOptions(ArgList))
return;
// After all InstallAPI necessary arguments have been collected. Go back and
// assign values that were unknown before the clang driver opt table was used.
ArchitectureSet AllArchs;
llvm::for_each(DriverOpts.Targets,
[&AllArchs](const auto &T) { AllArchs.set(T.first.Arch); });
auto assignDefaultLibAttrs = [&AllArchs](LibAttrs &Attrs) {
for (StringMapEntry<ArchitectureSet> &Entry : Attrs)
if (Entry.getValue().empty())
Entry.setValue(AllArchs);
};
assignDefaultLibAttrs(LinkerOpts.AllowableClients);
assignDefaultLibAttrs(LinkerOpts.ReexportedFrameworks);
assignDefaultLibAttrs(LinkerOpts.ReexportedLibraries);
assignDefaultLibAttrs(LinkerOpts.ReexportedLibraryPaths);
assignDefaultLibAttrs(LinkerOpts.RPaths);
/// Force cc1 options that should always be on.
FrontendArgs = {"-fsyntax-only", "-Wprivate-extern"};
@@ -357,6 +531,89 @@ static StringRef getFrameworkNameFromInstallName(StringRef InstallName) {
return Match.back();
}
static Expected<std::unique_ptr<InterfaceFile>>
getInterfaceFile(const StringRef Filename) {
ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr =
MemoryBuffer::getFile(Filename);
if (auto Err = BufferOrErr.getError())
return errorCodeToError(std::move(Err));
auto Buffer = std::move(*BufferOrErr);
std::unique_ptr<InterfaceFile> IF;
switch (identify_magic(Buffer->getBuffer())) {
case file_magic::macho_dynamically_linked_shared_lib:
LLVM_FALLTHROUGH;
case file_magic::macho_dynamically_linked_shared_lib_stub:
LLVM_FALLTHROUGH;
case file_magic::macho_universal_binary:
return DylibReader::get(Buffer->getMemBufferRef());
break;
case file_magic::tapi_file:
return TextAPIReader::get(Buffer->getMemBufferRef());
default:
return make_error<TextAPIError>(TextAPIErrorCode::InvalidInputFormat,
"unsupported library file format");
}
llvm_unreachable("unexpected failure in getInterface");
}
std::pair<LibAttrs, ReexportedInterfaces> Options::getReexportedLibraries() {
LibAttrs Reexports;
ReexportedInterfaces ReexportIFs;
auto AccumulateReexports = [&](StringRef Path, const ArchitectureSet &Archs) {
auto ReexportIFOrErr = getInterfaceFile(Path);
if (!ReexportIFOrErr)
return false;
std::unique_ptr<InterfaceFile> Reexport = std::move(*ReexportIFOrErr);
StringRef InstallName = Reexport->getInstallName();
assert(!InstallName.empty() && "Parse error for install name");
Reexports.insert({InstallName, Archs});
ReexportIFs.emplace_back(std::move(*Reexport));
return true;
};
// Populate search paths by looking at user paths before system ones.
PathSeq FwkSearchPaths(FEOpts.FwkPaths.begin(), FEOpts.FwkPaths.end());
// FIXME: System framework paths need to reset if installapi is invoked with
// different platforms.
FwkSearchPaths.insert(FwkSearchPaths.end(), FEOpts.SystemFwkPaths.begin(),
FEOpts.SystemFwkPaths.end());
for (const StringMapEntry<ArchitectureSet> &Lib :
LinkerOpts.ReexportedLibraries) {
std::string Name = "lib" + Lib.getKey().str() + ".dylib";
std::string Path = findLibrary(Name, *FM, {}, LinkerOpts.LibPaths, {});
if (Path.empty()) {
Diags->Report(diag::err_cannot_find_reexport) << true << Lib.getKey();
return {};
}
if (DriverOpts.TraceLibraryLocation)
errs() << Path << "\n";
AccumulateReexports(Path, Lib.getValue());
}
for (const StringMapEntry<ArchitectureSet> &Lib :
LinkerOpts.ReexportedLibraryPaths)
AccumulateReexports(Lib.getKey(), Lib.getValue());
for (const StringMapEntry<ArchitectureSet> &Lib :
LinkerOpts.ReexportedFrameworks) {
std::string Name = (Lib.getKey() + ".framework/" + Lib.getKey()).str();
std::string Path = findLibrary(Name, *FM, FwkSearchPaths, {}, {});
if (Path.empty()) {
Diags->Report(diag::err_cannot_find_reexport) << false << Lib.getKey();
return {};
}
if (DriverOpts.TraceLibraryLocation)
errs() << Path << "\n";
AccumulateReexports(Path, Lib.getValue());
}
return {std::move(Reexports), std::move(ReexportIFs)};
}
InstallAPIContext Options::createContext() {
InstallAPIContext Ctx;
Ctx.FM = FM;
@@ -369,10 +626,17 @@ InstallAPIContext Options::createContext() {
Ctx.BA.CurrentVersion = LinkerOpts.CurrentVersion;
Ctx.BA.CompatVersion = LinkerOpts.CompatVersion;
Ctx.BA.AppExtensionSafe = LinkerOpts.AppExtensionSafe;
Ctx.BA.ParentUmbrella = LinkerOpts.ParentUmbrella;
Ctx.BA.OSLibNotForSharedCache = LinkerOpts.OSLibNotForSharedCache;
Ctx.FT = DriverOpts.OutFT;
Ctx.OutputLoc = DriverOpts.OutputPath;
Ctx.LangMode = FEOpts.LangMode;
auto [Reexports, ReexportedIFs] = getReexportedLibraries();
if (Diags->hasErrorOccurred())
return Ctx;
Ctx.Reexports = Reexports;
// Attempt to find umbrella headers by capturing framework name.
StringRef FrameworkName;
if (!LinkerOpts.IsDylib)
@@ -532,13 +796,14 @@ InstallAPIContext Options::createContext() {
Expected<Records> Slices =
DylibReader::readFile((*Buffer)->getMemBufferRef(), PO);
if (auto Err = Slices.takeError()) {
Diags->Report(diag::err_cannot_open_file) << DriverOpts.DylibToVerify;
Diags->Report(diag::err_cannot_open_file)
<< DriverOpts.DylibToVerify << std::move(Err);
return Ctx;
}
Ctx.Verifier = std::make_unique<DylibVerifier>(
std::move(*Slices), Diags, DriverOpts.VerifyMode, DriverOpts.Demangle,
DriverOpts.DSYMPath);
std::move(*Slices), std::move(ReexportedIFs), Diags,
DriverOpts.VerifyMode, DriverOpts.Demangle, DriverOpts.DSYMPath);
return Ctx;
}

View File

@@ -20,7 +20,6 @@
#include "llvm/Option/Option.h"
#include "llvm/Support/Program.h"
#include "llvm/TargetParser/Triple.h"
#include <set>
#include <string>
#include <vector>
@@ -81,9 +80,30 @@ struct DriverOptions {
/// \brief Print verbose output.
bool Verbose = false;
/// \brief Log libraries loaded.
bool TraceLibraryLocation = false;
};
struct LinkerOptions {
/// \brief List of allowable clients to use for the dynamic library.
LibAttrs AllowableClients;
/// \brief List of reexported libraries to use for the dynamic library.
LibAttrs ReexportedLibraries;
/// \brief List of reexported libraries to use for the dynamic library.
LibAttrs ReexportedLibraryPaths;
/// \brief List of reexported frameworks to use for the dynamic library.
LibAttrs ReexportedFrameworks;
/// \brief List of rpaths to use for the dynamic library.
LibAttrs RPaths;
/// \brief Additional library search paths.
PathSeq LibPaths;
/// \brief The install name to use for the dynamic library.
std::string InstallName;
@@ -93,18 +113,34 @@ struct LinkerOptions {
/// \brief The compatibility version to use for the dynamic library.
PackedVersion CompatVersion;
/// \brief Name of the umbrella library.
std::string ParentUmbrella;
/// \brief Is application extension safe.
bool AppExtensionSafe = false;
/// \brief Set if we should scan for a dynamic library and not a framework.
bool IsDylib = false;
/// \brief Is an OS library that is not shared cache eligible.
bool OSLibNotForSharedCache = false;
};
struct FrontendOptions {
/// \brief The language mode to parse headers in.
Language LangMode = Language::ObjC;
/// \brief The sysroot to search for SDK headers or libraries.
std::string ISysroot;
/// \brief Additional framework search paths.
PathSeq FwkPaths;
/// \brief Additional SYSTEM framework search paths.
PathSeq SystemFwkPaths;
};
using arg_iterator = llvm::opt::arg_iterator<llvm::opt::Arg **>;
class Options {
private:
bool processDriverOptions(llvm::opt::InputArgList &Args);
@@ -112,6 +148,8 @@ private:
bool processFrontendOptions(llvm::opt::InputArgList &Args);
std::vector<const char *>
processAndFilterOutInstallAPIOptions(ArrayRef<const char *> Args);
bool processInstallAPIXOptions(llvm::opt::InputArgList &Args);
bool processXarchOption(llvm::opt::InputArgList &Args, arg_iterator Curr);
public:
/// The various options grouped together.
@@ -136,9 +174,12 @@ private:
bool addFilePaths(llvm::opt::InputArgList &Args, PathSeq &Headers,
llvm::opt::OptSpecifier ID);
std::pair<LibAttrs, ReexportedInterfaces> getReexportedLibraries();
DiagnosticsEngine *Diags;
FileManager *FM;
std::vector<std::string> FrontendArgs;
llvm::DenseMap<const llvm::opt::Arg *, Architecture> ArgToArchMap;
};
enum ID {