Skip to content

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

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Apr 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions clang/include/clang/Basic/DiagnosticDriverKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -663,6 +663,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">;

Expand Down
21 changes: 21 additions & 0 deletions clang/include/clang/Basic/DiagnosticInstallAPIKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -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>;
Expand All @@ -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
17 changes: 17 additions & 0 deletions clang/include/clang/InstallAPI/Context.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -80,6 +83,20 @@ struct InstallAPIContext {
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

Expand Down
29 changes: 24 additions & 5 deletions clang/include/clang/InstallAPI/DylibVerifier.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -63,11 +66,12 @@ class DylibVerifier : llvm::MachO::RecordVisitor {

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);
Expand All @@ -77,6 +81,14 @@ class DylibVerifier : llvm::MachO::RecordVisitor {
// 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);

Expand Down Expand Up @@ -105,6 +117,10 @@ class DylibVerifier : llvm::MachO::RecordVisitor {
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,
Expand Down Expand Up @@ -154,6 +170,9 @@ class DylibVerifier : llvm::MachO::RecordVisitor {
// 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;

Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/InstallAPI/MachO.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions clang/lib/InstallAPI/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ set(LLVM_LINK_COMPONENTS
)

add_clang_library(clangInstallAPI
DiagnosticBuilderWrappers.cpp
DylibVerifier.cpp
FileList.cpp
Frontend.cpp
Expand Down
109 changes: 109 additions & 0 deletions clang/lib/InstallAPI/DiagnosticBuilderWrappers.cpp
Original file line number Diff line number Diff line change
@@ -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
49 changes: 49 additions & 0 deletions clang/lib/InstallAPI/DiagnosticBuilderWrappers.h
Original file line number Diff line number Diff line change
@@ -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
Loading