That's an automated patch generated from clang-tidy performance-use-std-move as a follow-up to #184136
1148 lines
36 KiB
C++
1148 lines
36 KiB
C++
//===- LibraryScanner.cpp - Provide Library Scanning Implementation ----===//
|
|
//
|
|
// 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 "llvm/ExecutionEngine/Orc/TargetProcess/LibraryScanner.h"
|
|
#include "llvm/ExecutionEngine/Orc/TargetProcess/LibraryResolver.h"
|
|
|
|
#include "llvm/ADT/StringExtras.h"
|
|
#include "llvm/Object/COFF.h"
|
|
#include "llvm/Object/ELF.h"
|
|
#include "llvm/Object/ELFObjectFile.h"
|
|
#include "llvm/Object/ELFTypes.h"
|
|
#include "llvm/Object/MachO.h"
|
|
#include "llvm/Object/MachOUniversal.h"
|
|
#include "llvm/Object/ObjectFile.h"
|
|
#include "llvm/Support/Error.h"
|
|
#include "llvm/Support/FileSystem.h"
|
|
#include "llvm/Support/MemoryBuffer.h"
|
|
#include "llvm/Support/Path.h"
|
|
#include "llvm/Support/Program.h"
|
|
#include "llvm/TargetParser/Host.h"
|
|
#include "llvm/TargetParser/Triple.h"
|
|
|
|
#ifdef LLVM_ON_UNIX
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
#endif // LLVM_ON_UNIX
|
|
|
|
#ifdef __APPLE__
|
|
#include <sys/stat.h>
|
|
#undef LC_LOAD_DYLIB
|
|
#undef LC_RPATH
|
|
#endif // __APPLE__
|
|
|
|
#define DEBUG_TYPE "orc-scanner"
|
|
|
|
namespace llvm::orc {
|
|
|
|
void handleError(Error Err, StringRef context = "") {
|
|
consumeError(handleErrors(std::move(Err), [&](const ErrorInfoBase &EIB) {
|
|
dbgs() << "LLVM Error";
|
|
if (!context.empty())
|
|
dbgs() << " [" << context << "]";
|
|
dbgs() << ": " << EIB.message() << "\n";
|
|
}));
|
|
}
|
|
|
|
bool ObjectFileLoader::isArchitectureCompatible(const object::ObjectFile &Obj) {
|
|
static const llvm::Triple HostTriple(llvm::sys::getProcessTriple());
|
|
|
|
if (HostTriple.getArch() != Obj.getArch())
|
|
return false;
|
|
|
|
if (Obj.getTripleObjectFormat() != HostTriple.getObjectFormat())
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
Expected<object::OwningBinary<object::ObjectFile>>
|
|
ObjectFileLoader::loadObjectFileWithOwnership(StringRef FilePath) {
|
|
LLVM_DEBUG(dbgs() << "ObjectFileLoader: Attempting to open file " << FilePath
|
|
<< "\n";);
|
|
if (auto ObjOrErr = object::ObjectFile::createObjectFile(FilePath)) {
|
|
|
|
LLVM_DEBUG(dbgs() << "ObjectFileLoader: Detected object file\n";);
|
|
|
|
auto OwningBin = std::move(*ObjOrErr);
|
|
|
|
if (!isArchitectureCompatible(*OwningBin.getBinary())) {
|
|
LLVM_DEBUG(dbgs() << "ObjectFileLoader: Incompatible architecture: "
|
|
<< FilePath << "\n";);
|
|
return createStringError(inconvertibleErrorCode(),
|
|
"Incompatible object file: %s",
|
|
FilePath.str().c_str());
|
|
}
|
|
|
|
LLVM_DEBUG(dbgs() << "ObjectFileLoader: Object file is compatible\n";);
|
|
|
|
return std::move(OwningBin);
|
|
} else {
|
|
#if defined(__APPLE__)
|
|
consumeError(ObjOrErr.takeError());
|
|
auto BinOrErr = object::createBinary(FilePath);
|
|
if (!BinOrErr) {
|
|
LLVM_DEBUG(dbgs() << "ObjectFileLoader: Failed to open file " << FilePath
|
|
<< "\n";);
|
|
return BinOrErr.takeError();
|
|
}
|
|
|
|
LLVM_DEBUG(dbgs() << "ObjectFileLoader: Successfully opened file "
|
|
<< FilePath << "\n";);
|
|
|
|
auto OwningBin = BinOrErr->takeBinary();
|
|
object::Binary *Bin = OwningBin.first.get();
|
|
|
|
if (Bin->isArchive()) {
|
|
LLVM_DEBUG(
|
|
dbgs() << "ObjectFileLoader: File is an archive, not supported: "
|
|
<< FilePath << "\n";);
|
|
return createStringError(std::errc::invalid_argument,
|
|
"Archive files are not supported: %s",
|
|
FilePath.str().c_str());
|
|
}
|
|
|
|
if (auto *UB = dyn_cast<object::MachOUniversalBinary>(Bin)) {
|
|
LLVM_DEBUG(
|
|
dbgs() << "ObjectFileLoader: Detected Mach-O universal binary: "
|
|
<< FilePath << "\n";);
|
|
for (auto ObjForArch : UB->objects()) {
|
|
auto ObjOrErr = ObjForArch.getAsObjectFile();
|
|
if (!ObjOrErr) {
|
|
LLVM_DEBUG(dbgs() << "ObjectFileLoader: Skipping invalid "
|
|
"architecture slice\n";);
|
|
|
|
consumeError(ObjOrErr.takeError());
|
|
continue;
|
|
}
|
|
|
|
std::unique_ptr<object::ObjectFile> Obj = std::move(ObjOrErr.get());
|
|
if (isArchitectureCompatible(*Obj)) {
|
|
LLVM_DEBUG(
|
|
dbgs() << "ObjectFileLoader: Found compatible object slice\n";);
|
|
|
|
return object::OwningBinary<object::ObjectFile>(
|
|
std::move(Obj), std::move(OwningBin.second));
|
|
|
|
} else {
|
|
LLVM_DEBUG(dbgs() << "ObjectFileLoader: Incompatible architecture "
|
|
"slice skipped\n";);
|
|
}
|
|
}
|
|
LLVM_DEBUG(dbgs() << "ObjectFileLoader: No compatible slices found in "
|
|
"universal binary\n";);
|
|
return createStringError(inconvertibleErrorCode(),
|
|
"No compatible object found in fat binary: %s",
|
|
FilePath.str().c_str());
|
|
}
|
|
return ObjOrErr.takeError();
|
|
#else
|
|
LLVM_DEBUG(dbgs() << "ObjectFileLoader: Failed to open file " << FilePath
|
|
<< "\n";);
|
|
return ObjOrErr.takeError();
|
|
#endif
|
|
}
|
|
|
|
return createStringError(inconvertibleErrorCode(),
|
|
"Not a compatible object file : %s",
|
|
FilePath.str().c_str());
|
|
}
|
|
|
|
template <class ELFT>
|
|
bool isELFSharedLibrary(const object::ELFFile<ELFT> &ELFObj) {
|
|
if (ELFObj.getHeader().e_type != ELF::ET_DYN)
|
|
return false;
|
|
|
|
auto PHOrErr = ELFObj.program_headers();
|
|
if (!PHOrErr) {
|
|
consumeError(PHOrErr.takeError());
|
|
return true;
|
|
}
|
|
|
|
for (auto Phdr : *PHOrErr) {
|
|
if (Phdr.p_type == ELF::PT_INTERP)
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool isSharedLibraryObject(object::ObjectFile &Obj) {
|
|
if (Obj.isELF()) {
|
|
if (auto *ELF32LE = dyn_cast<object::ELF32LEObjectFile>(&Obj))
|
|
return isELFSharedLibrary(ELF32LE->getELFFile());
|
|
if (auto *ELF64LE = dyn_cast<object::ELF64LEObjectFile>(&Obj))
|
|
return isELFSharedLibrary(ELF64LE->getELFFile());
|
|
if (auto *ELF32BE = dyn_cast<object::ELF32BEObjectFile>(&Obj))
|
|
return isELFSharedLibrary(ELF32BE->getELFFile());
|
|
if (auto *ELF64BE = dyn_cast<object::ELF64BEObjectFile>(&Obj))
|
|
return isELFSharedLibrary(ELF64BE->getELFFile());
|
|
} else if (Obj.isMachO()) {
|
|
const object::MachOObjectFile *MachO =
|
|
dyn_cast<object::MachOObjectFile>(&Obj);
|
|
if (!MachO) {
|
|
LLVM_DEBUG(dbgs() << "Failed to cast to MachOObjectFile.\n";);
|
|
return false;
|
|
}
|
|
LLVM_DEBUG({
|
|
bool Result =
|
|
MachO->getHeader().filetype == MachO::HeaderFileType::MH_DYLIB;
|
|
dbgs() << "Mach-O filetype: " << MachO->getHeader().filetype
|
|
<< " (MH_DYLIB == " << MachO::HeaderFileType::MH_DYLIB
|
|
<< "), shared: " << Result << "\n";
|
|
});
|
|
|
|
return MachO->getHeader().filetype == MachO::HeaderFileType::MH_DYLIB;
|
|
} else if (Obj.isCOFF()) {
|
|
const object::COFFObjectFile *coff = dyn_cast<object::COFFObjectFile>(&Obj);
|
|
if (!coff)
|
|
return false;
|
|
return coff->getCharacteristics() & COFF::IMAGE_FILE_DLL;
|
|
} else {
|
|
LLVM_DEBUG(dbgs() << "Binary is not an ObjectFile.\n";);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool DylibPathValidator::isSharedLibrary(StringRef Path) const {
|
|
LLVM_DEBUG(dbgs() << "Checking if path is a shared library: " << Path
|
|
<< "\n";);
|
|
|
|
auto FileType = sys::fs::get_file_type(Path, /*Follow*/ true);
|
|
if (FileType != sys::fs::file_type::regular_file) {
|
|
LLVM_DEBUG(dbgs() << "File type is not a regular file for path: " << Path
|
|
<< "\n";);
|
|
return false;
|
|
}
|
|
|
|
file_magic MagicCode;
|
|
identify_magic(Path, MagicCode);
|
|
|
|
// Skip archives.
|
|
if (MagicCode == file_magic::archive)
|
|
return false;
|
|
|
|
// Object file inspection for PE/COFF, ELF, and Mach-O
|
|
bool NeedsObjectInspection =
|
|
#if defined(_WIN32)
|
|
(MagicCode == file_magic::pecoff_executable);
|
|
#elif defined(__APPLE__)
|
|
(MagicCode == file_magic::macho_universal_binary ||
|
|
MagicCode == file_magic::macho_fixed_virtual_memory_shared_lib ||
|
|
MagicCode == file_magic::macho_dynamically_linked_shared_lib ||
|
|
MagicCode == file_magic::macho_dynamically_linked_shared_lib_stub);
|
|
#elif defined(LLVM_ON_UNIX)
|
|
#ifdef __CYGWIN__
|
|
(MagicCode == file_magic::pecoff_executable);
|
|
#else
|
|
(MagicCode == file_magic::elf_shared_object);
|
|
#endif
|
|
#else
|
|
#error "Unsupported platform."
|
|
#endif
|
|
|
|
if (!NeedsObjectInspection) {
|
|
LLVM_DEBUG(dbgs() << "Path is not identified as a shared library: " << Path
|
|
<< "\n";);
|
|
return false;
|
|
}
|
|
|
|
ObjectFileLoader ObjLoader(Path);
|
|
auto ObjOrErr = ObjLoader.getObjectFile();
|
|
if (!ObjOrErr) {
|
|
consumeError(ObjOrErr.takeError());
|
|
return false;
|
|
}
|
|
|
|
bool IsShared = isSharedLibraryObject(ObjOrErr.get());
|
|
|
|
if (IsShared && ObjCache)
|
|
ObjCache->insert(Path, std::move(ObjLoader));
|
|
|
|
return IsShared;
|
|
}
|
|
|
|
void DylibSubstitutor::configure(StringRef LoaderPath) {
|
|
SmallString<512> ExecPath(sys::fs::getMainExecutable(nullptr, nullptr));
|
|
sys::path::remove_filename(ExecPath);
|
|
|
|
SmallString<512> LoaderDir;
|
|
if (LoaderPath.empty()) {
|
|
LoaderDir = std::move(ExecPath);
|
|
} else {
|
|
LoaderDir = LoaderPath.str();
|
|
if (!sys::fs::is_directory(LoaderPath))
|
|
sys::path::remove_filename(LoaderDir);
|
|
}
|
|
|
|
#ifdef __APPLE__
|
|
Placeholders.push_back({"@loader_path", std::string(LoaderDir)});
|
|
Placeholders.push_back({"@executable_path", std::string(ExecPath)});
|
|
#else
|
|
Placeholders.push_back({"$origin", std::string(LoaderDir)});
|
|
#endif
|
|
}
|
|
|
|
std::optional<std::string>
|
|
SearchPathResolver::resolve(StringRef Stem, const DylibSubstitutor &Subst,
|
|
DylibPathValidator &Validator) const {
|
|
for (const auto &SP : Paths) {
|
|
std::string Base = Subst.substitute(SP);
|
|
|
|
SmallString<512> FullPath(Base);
|
|
if (!PlaceholderPrefix.empty() &&
|
|
Stem.starts_with_insensitive(PlaceholderPrefix))
|
|
FullPath.append(Stem.drop_front(PlaceholderPrefix.size()));
|
|
else
|
|
sys::path::append(FullPath, Stem);
|
|
|
|
LLVM_DEBUG(dbgs() << "SearchPathResolver::resolve FullPath = " << FullPath
|
|
<< "\n";);
|
|
|
|
if (auto Valid = Validator.validate(FullPath.str()))
|
|
return Valid;
|
|
}
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
std::optional<std::string>
|
|
DylibResolverImpl::tryWithExtensions(StringRef LibStem) const {
|
|
LLVM_DEBUG(dbgs() << "tryWithExtensions: baseName = " << LibStem << "\n";);
|
|
SmallVector<SmallString<256>, 8> Candidates;
|
|
|
|
// Add extensions by platform
|
|
#if defined(__APPLE__)
|
|
Candidates.emplace_back(LibStem);
|
|
Candidates.back() += ".dylib";
|
|
#elif defined(_WIN32)
|
|
Candidates.emplace_back(LibStem);
|
|
Candidates.back() += ".dll";
|
|
#else
|
|
Candidates.emplace_back(LibStem);
|
|
Candidates.back() += ".so";
|
|
#endif
|
|
|
|
// Optionally try "lib" prefix if not already there
|
|
StringRef FileName = sys::path::filename(LibStem);
|
|
StringRef Base = sys::path::parent_path(LibStem);
|
|
if (!FileName.starts_with("lib")) {
|
|
SmallString<256> WithPrefix(Base);
|
|
if (!WithPrefix.empty())
|
|
sys::path::append(WithPrefix, ""); // ensure separator if needed
|
|
WithPrefix += "lib";
|
|
WithPrefix += FileName;
|
|
|
|
#if defined(__APPLE__)
|
|
WithPrefix += ".dylib";
|
|
#elif defined(_WIN32)
|
|
WithPrefix += ".dll";
|
|
#else
|
|
WithPrefix += ".so";
|
|
#endif
|
|
|
|
Candidates.push_back(std::move(WithPrefix));
|
|
}
|
|
|
|
LLVM_DEBUG({
|
|
dbgs() << " Candidates to try:\n";
|
|
for (const auto &C : Candidates)
|
|
dbgs() << " " << C << "\n";
|
|
});
|
|
|
|
// Try all variants using tryAllPaths
|
|
for (const auto &Name : Candidates) {
|
|
|
|
LLVM_DEBUG(dbgs() << " Trying candidate: " << Name << "\n";);
|
|
|
|
for (const auto &R : Resolvers) {
|
|
if (auto Res = R.resolve(Name, Substitutor, Validator))
|
|
return Res;
|
|
}
|
|
}
|
|
|
|
LLVM_DEBUG(dbgs() << " -> No candidate Resolved.\n";);
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
std::optional<std::string>
|
|
DylibResolverImpl::resolve(StringRef LibStem, bool VariateLibStem) const {
|
|
LLVM_DEBUG(dbgs() << "Resolving library stem: " << LibStem << "\n";);
|
|
|
|
// If it is an absolute path, don't try iterate over the paths.
|
|
if (sys::path::is_absolute(LibStem)) {
|
|
LLVM_DEBUG(dbgs() << " -> Absolute path detected.\n";);
|
|
return Validator.validate(LibStem);
|
|
}
|
|
|
|
if (!LibStem.starts_with_insensitive("@rpath")) {
|
|
if (auto norm = Validator.validate(Substitutor.substitute(LibStem))) {
|
|
LLVM_DEBUG(dbgs() << " -> Resolved after substitution: " << *norm
|
|
<< "\n";);
|
|
|
|
return norm;
|
|
}
|
|
}
|
|
|
|
for (const auto &R : Resolvers) {
|
|
LLVM_DEBUG(dbgs() << " -> Resolving via search path ... \n";);
|
|
if (auto Result = R.resolve(LibStem, Substitutor, Validator)) {
|
|
LLVM_DEBUG(dbgs() << " -> Resolved via search path: " << *Result
|
|
<< "\n";);
|
|
|
|
return Result;
|
|
}
|
|
}
|
|
|
|
// Expand libStem with paths, extensions, etc.
|
|
// std::string foundName;
|
|
if (VariateLibStem) {
|
|
LLVM_DEBUG(dbgs() << " -> Trying with extensions...\n";);
|
|
|
|
if (auto Norm = tryWithExtensions(LibStem)) {
|
|
LLVM_DEBUG(dbgs() << " -> Resolved via tryWithExtensions: " << *Norm
|
|
<< "\n";);
|
|
return Norm;
|
|
}
|
|
}
|
|
|
|
LLVM_DEBUG(dbgs() << " -> Could not resolve: " << LibStem << "\n";);
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
#ifndef _WIN32
|
|
mode_t PathResolver::lstatCached(StringRef Path) {
|
|
// If already cached - retun cached result
|
|
if (auto Cache = LibPathCache->read_lstat(Path))
|
|
return *Cache;
|
|
|
|
// Not cached: perform lstat and store
|
|
struct stat buf{};
|
|
mode_t st_mode = (lstat(Path.str().c_str(), &buf) == -1) ? 0 : buf.st_mode;
|
|
|
|
LibPathCache->insert_lstat(Path, st_mode);
|
|
|
|
return st_mode;
|
|
}
|
|
|
|
std::optional<std::string> PathResolver::readlinkCached(StringRef Path) {
|
|
// If already cached - retun cached result
|
|
if (auto Cache = LibPathCache->read_link(Path))
|
|
return Cache;
|
|
|
|
// If result not in cache - call system function and cache result
|
|
char buf[PATH_MAX];
|
|
ssize_t len;
|
|
if ((len = readlink(Path.str().c_str(), buf, sizeof(buf))) != -1) {
|
|
buf[len] = '\0';
|
|
std::string s(buf);
|
|
LibPathCache->insert_link(Path, s);
|
|
return s;
|
|
}
|
|
return std::nullopt;
|
|
}
|
|
|
|
void createComponent(StringRef Path, StringRef BasePath, bool BaseIsResolved,
|
|
SmallVector<StringRef, 16> &Component) {
|
|
StringRef Separator = sys::path::get_separator();
|
|
if (!BaseIsResolved) {
|
|
if (Path[0] == '~' &&
|
|
(Path.size() == 1 || sys::path::is_separator(Path[1]))) {
|
|
static SmallString<128> HomeP;
|
|
if (HomeP.str().empty())
|
|
sys::path::home_directory(HomeP);
|
|
StringRef(HomeP).split(Component, Separator, /*MaxSplit*/ -1,
|
|
/*KeepEmpty*/ false);
|
|
} else if (BasePath.empty()) {
|
|
static SmallString<256> CurrentPath;
|
|
if (CurrentPath.str().empty())
|
|
sys::fs::current_path(CurrentPath);
|
|
StringRef(CurrentPath)
|
|
.split(Component, Separator, /*MaxSplit*/ -1, /*KeepEmpty*/ false);
|
|
} else {
|
|
BasePath.split(Component, Separator, /*MaxSplit*/ -1,
|
|
/*KeepEmpty*/ false);
|
|
}
|
|
}
|
|
|
|
Path.split(Component, Separator, /*MaxSplit*/ -1, /*KeepEmpty*/ false);
|
|
}
|
|
|
|
void normalizePathSegments(SmallVector<StringRef, 16> &PathParts) {
|
|
SmallVector<StringRef, 16> NormalizedPath;
|
|
for (auto &Part : PathParts) {
|
|
if (Part == ".") {
|
|
continue;
|
|
} else if (Part == "..") {
|
|
if (!NormalizedPath.empty() && NormalizedPath.back() != "..") {
|
|
NormalizedPath.pop_back();
|
|
} else {
|
|
NormalizedPath.push_back("..");
|
|
}
|
|
} else {
|
|
NormalizedPath.push_back(Part);
|
|
}
|
|
}
|
|
PathParts.swap(NormalizedPath);
|
|
}
|
|
#endif
|
|
|
|
std::optional<std::string> PathResolver::realpathCached(StringRef Path,
|
|
std::error_code &EC,
|
|
StringRef Base,
|
|
bool BaseIsResolved,
|
|
long SymLoopLevel) {
|
|
EC.clear();
|
|
|
|
if (Path.empty()) {
|
|
EC = std::make_error_code(std::errc::no_such_file_or_directory);
|
|
LLVM_DEBUG(dbgs() << "PathResolver::realpathCached: Empty path\n";);
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
if (SymLoopLevel <= 0) {
|
|
EC = std::make_error_code(std::errc::too_many_symbolic_link_levels);
|
|
LLVM_DEBUG(
|
|
dbgs() << "PathResolver::realpathCached: Too many Symlink levels: "
|
|
<< Path << "\n";);
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
// If already cached - retun cached result
|
|
bool isRelative = sys::path::is_relative(Path);
|
|
if (!isRelative) {
|
|
if (auto Cached = LibPathCache->read_realpath(Path)) {
|
|
EC = Cached->ErrnoCode;
|
|
if (EC) {
|
|
LLVM_DEBUG(dbgs() << "PathResolver::realpathCached: Cached (error) for "
|
|
<< Path << "\n";);
|
|
} else {
|
|
LLVM_DEBUG(
|
|
dbgs() << "PathResolver::realpathCached: Cached (success) for "
|
|
<< Path << " => " << Cached->canonicalPath << "\n";);
|
|
}
|
|
return Cached->canonicalPath.empty()
|
|
? std::nullopt
|
|
: std::make_optional(Cached->canonicalPath);
|
|
}
|
|
}
|
|
|
|
LLVM_DEBUG(dbgs() << "PathResolver::realpathCached: Resolving path: " << Path
|
|
<< "\n";);
|
|
|
|
// If result not in cache - call system function and cache result
|
|
|
|
StringRef Separator(sys::path::get_separator());
|
|
SmallString<256> Resolved(Separator);
|
|
#ifndef _WIN32
|
|
SmallVector<StringRef, 16> Components;
|
|
|
|
if (isRelative) {
|
|
if (BaseIsResolved) {
|
|
Resolved.assign(Base);
|
|
LLVM_DEBUG(dbgs() << " Using Resolved base: " << Base << "\n";);
|
|
}
|
|
createComponent(Path, Base, BaseIsResolved, Components);
|
|
} else {
|
|
Path.split(Components, Separator, /*MaxSplit*/ -1, /*KeepEmpty*/ false);
|
|
}
|
|
|
|
normalizePathSegments(Components);
|
|
LLVM_DEBUG({
|
|
for (auto &C : Components)
|
|
dbgs() << " " << C << " ";
|
|
|
|
dbgs() << "\n";
|
|
});
|
|
|
|
// Handle path list items
|
|
for (const auto &Component : Components) {
|
|
if (Component == ".")
|
|
continue;
|
|
if (Component == "..") {
|
|
// collapse "a/b/../c" to "a/c"
|
|
size_t S = Resolved.rfind(Separator);
|
|
if (S != llvm::StringRef::npos)
|
|
Resolved.resize(S);
|
|
if (Resolved.empty())
|
|
Resolved = Separator;
|
|
continue;
|
|
}
|
|
|
|
size_t oldSize = Resolved.size();
|
|
sys::path::append(Resolved, Component);
|
|
const char *ResolvedPath = Resolved.c_str();
|
|
LLVM_DEBUG(dbgs() << " Processing Component: " << Component << " => "
|
|
<< ResolvedPath << "\n";);
|
|
mode_t st_mode = lstatCached(ResolvedPath);
|
|
|
|
if (S_ISLNK(st_mode)) {
|
|
LLVM_DEBUG(dbgs() << " Found symlink: " << ResolvedPath << "\n";);
|
|
|
|
auto SymlinkOpt = readlinkCached(ResolvedPath);
|
|
if (!SymlinkOpt) {
|
|
EC = std::make_error_code(std::errc::no_such_file_or_directory);
|
|
LibPathCache->insert_realpath(Path, LibraryPathCache::PathInfo{"", EC});
|
|
LLVM_DEBUG(dbgs() << " Failed to read symlink: " << ResolvedPath
|
|
<< "\n";);
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
StringRef Symlink = *SymlinkOpt;
|
|
LLVM_DEBUG(dbgs() << " Symlink points to: " << Symlink << "\n";);
|
|
|
|
std::string resolvedBase = "";
|
|
if (sys::path::is_relative(Symlink)) {
|
|
Resolved.resize(oldSize);
|
|
resolvedBase = Resolved.str().str();
|
|
}
|
|
|
|
auto RealSymlink =
|
|
realpathCached(Symlink, EC, resolvedBase,
|
|
/*BaseIsResolved=*/true, SymLoopLevel - 1);
|
|
if (!RealSymlink) {
|
|
LibPathCache->insert_realpath(Path, LibraryPathCache::PathInfo{"", EC});
|
|
LLVM_DEBUG(dbgs() << " Failed to resolve symlink target: " << Symlink
|
|
<< "\n";);
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
Resolved.assign(*RealSymlink);
|
|
LLVM_DEBUG(dbgs() << " Symlink Resolved to: " << Resolved << "\n";);
|
|
|
|
} else if (st_mode == 0) {
|
|
EC = std::make_error_code(std::errc::no_such_file_or_directory);
|
|
LibPathCache->insert_realpath(Path, LibraryPathCache::PathInfo{"", EC});
|
|
LLVM_DEBUG(dbgs() << " Component does not exist: " << ResolvedPath
|
|
<< "\n";);
|
|
|
|
return std::nullopt;
|
|
}
|
|
}
|
|
#else
|
|
EC = sys::fs::real_path(Path, Resolved); // Windows fallback
|
|
#endif
|
|
|
|
std::string Canonical = Resolved.str().str();
|
|
{
|
|
LibPathCache->insert_realpath(Path, LibraryPathCache::PathInfo{
|
|
Canonical,
|
|
std::error_code() // success
|
|
});
|
|
}
|
|
LLVM_DEBUG(dbgs() << "PathResolver::realpathCached: Final Resolved: " << Path
|
|
<< " => " << Canonical << "\n";);
|
|
return Canonical;
|
|
}
|
|
|
|
void LibraryScanHelper::addBasePath(const std::string &Path, PathType K) {
|
|
std::error_code EC;
|
|
std::string Canon = resolveCanonical(Path, EC);
|
|
if (EC) {
|
|
LLVM_DEBUG(
|
|
dbgs()
|
|
<< "LibraryScanHelper::addBasePath: Failed to canonicalize path: "
|
|
<< Path << "\n";);
|
|
return;
|
|
}
|
|
std::unique_lock<std::shared_mutex> Lock(Mtx);
|
|
if (LibSearchPaths.count(Canon)) {
|
|
LLVM_DEBUG(dbgs() << "LibraryScanHelper::addBasePath: Already added: "
|
|
<< Canon << "\n";);
|
|
return;
|
|
}
|
|
K = K == PathType::Unknown ? classifyKind(Canon) : K;
|
|
LibSearchPaths[Canon] = std::make_unique<LibrarySearchPath>(Canon, K);
|
|
auto &SP = LibSearchPaths[Canon];
|
|
|
|
if (K == PathType::User) {
|
|
LLVM_DEBUG(dbgs() << "LibraryScanHelper::addBasePath: Added User path: "
|
|
<< Canon << "\n";);
|
|
UnscannedUsr.push_back(StringRef(SP->BasePath));
|
|
} else {
|
|
LLVM_DEBUG(dbgs() << "LibraryScanHelper::addBasePath: Added System path: "
|
|
<< Canon << "\n";);
|
|
UnscannedSys.push_back(StringRef(SP->BasePath));
|
|
}
|
|
}
|
|
|
|
void LibraryScanHelper::getNextBatch(
|
|
PathType K, size_t BatchSize,
|
|
SmallVectorImpl<const LibrarySearchPath *> &Result) {
|
|
auto &Queue = (K == PathType::User) ? UnscannedUsr : UnscannedSys;
|
|
|
|
std::unique_lock<std::shared_mutex> Lock(Mtx);
|
|
|
|
while (!Queue.empty() && (BatchSize == 0 || Result.size() < BatchSize)) {
|
|
StringRef Base = Queue.front();
|
|
auto It = LibSearchPaths.find(Base);
|
|
if (It != LibSearchPaths.end()) {
|
|
auto &SP = It->second;
|
|
ScanState Expected = ScanState::NotScanned;
|
|
if (SP->State.compare_exchange_strong(Expected, ScanState::Scanning)) {
|
|
Result.push_back(SP.get());
|
|
}
|
|
}
|
|
Queue.pop_front();
|
|
}
|
|
}
|
|
|
|
bool LibraryScanHelper::isTrackedBasePath(StringRef Path) const {
|
|
std::error_code EC;
|
|
std::string Canon = resolveCanonical(Path, EC);
|
|
if (EC)
|
|
return false;
|
|
|
|
std::shared_lock<std::shared_mutex> Lock(Mtx);
|
|
return LibSearchPaths.count(Canon) > 0;
|
|
}
|
|
|
|
bool LibraryScanHelper::leftToScan(PathType K) const {
|
|
std::shared_lock<std::shared_mutex> Lock(Mtx);
|
|
for (const auto &KV : LibSearchPaths) {
|
|
const auto &SP = KV.second;
|
|
if (SP->Kind == K && SP->State == ScanState::NotScanned)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void LibraryScanHelper::resetToScan() {
|
|
std::shared_lock<std::shared_mutex> Lock(Mtx);
|
|
|
|
for (auto &[_, SP] : LibSearchPaths) {
|
|
ScanState Expected = ScanState::Scanned;
|
|
|
|
if (!SP->State.compare_exchange_strong(Expected, ScanState::NotScanned))
|
|
continue;
|
|
|
|
auto &TargetList =
|
|
(SP->Kind == PathType::User) ? UnscannedUsr : UnscannedSys;
|
|
TargetList.emplace_back(SP->BasePath);
|
|
}
|
|
}
|
|
|
|
std::string LibraryScanHelper::resolveCanonical(StringRef Path,
|
|
std::error_code &EC) const {
|
|
auto Canon = LibPathResolver->resolve(Path, EC);
|
|
return EC ? Path.str() : *Canon;
|
|
}
|
|
|
|
PathType LibraryScanHelper::classifyKind(StringRef Path) const {
|
|
// Detect home directory
|
|
const char *Home = getenv("HOME");
|
|
if (Home && Path.starts_with(Home))
|
|
return PathType::User;
|
|
|
|
static const std::array<std::string, 5> UserPrefixes = {
|
|
"/usr/local", // often used by users for manual installs
|
|
"/opt/homebrew", // common on macOS
|
|
"/opt/local", // MacPorts
|
|
"/home", // Linux home dirs
|
|
"/Users", // macOS user dirs
|
|
};
|
|
|
|
for (const auto &Prefix : UserPrefixes) {
|
|
if (Path.starts_with(Prefix))
|
|
return PathType::User;
|
|
}
|
|
|
|
return PathType::System;
|
|
}
|
|
|
|
Expected<LibraryDepsInfo> parseMachODeps(const object::MachOObjectFile &Obj) {
|
|
LibraryDepsInfo Libdeps;
|
|
LLVM_DEBUG(dbgs() << "Parsing Mach-O dependencies...\n";);
|
|
for (const auto &Command : Obj.load_commands()) {
|
|
switch (Command.C.cmd) {
|
|
case MachO::LC_LOAD_DYLIB: {
|
|
MachO::dylib_command dylibCmd = Obj.getDylibIDLoadCommand(Command);
|
|
const char *name = Command.Ptr + dylibCmd.dylib.name;
|
|
Libdeps.addDep(name);
|
|
LLVM_DEBUG(dbgs() << " Found LC_LOAD_DYLIB: " << name << "\n";);
|
|
} break;
|
|
case MachO::LC_LOAD_WEAK_DYLIB:
|
|
case MachO::LC_REEXPORT_DYLIB:
|
|
case MachO::LC_LOAD_UPWARD_DYLIB:
|
|
case MachO::LC_LAZY_LOAD_DYLIB:
|
|
break;
|
|
case MachO::LC_RPATH: {
|
|
// Extract RPATH
|
|
MachO::rpath_command rpathCmd = Obj.getRpathCommand(Command);
|
|
const char *rpath = Command.Ptr + rpathCmd.path;
|
|
LLVM_DEBUG(dbgs() << " Found LC_RPATH: " << rpath << "\n";);
|
|
|
|
SmallVector<StringRef, 4> RawPaths;
|
|
SplitString(StringRef(rpath), RawPaths,
|
|
sys::EnvPathSeparator == ':' ? ":" : ";");
|
|
|
|
for (const auto &raw : RawPaths) {
|
|
Libdeps.addRPath(raw.str()); // Convert to std::string
|
|
LLVM_DEBUG(dbgs() << " Parsed RPATH entry: " << raw << "\n";);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return Expected<LibraryDepsInfo>(std::move(Libdeps));
|
|
}
|
|
|
|
template <class ELFT>
|
|
static Expected<StringRef> getDynamicStrTab(const object::ELFFile<ELFT> &Elf) {
|
|
auto DynamicEntriesOrError = Elf.dynamicEntries();
|
|
if (!DynamicEntriesOrError)
|
|
return DynamicEntriesOrError.takeError();
|
|
|
|
for (const typename ELFT::Dyn &Dyn : *DynamicEntriesOrError) {
|
|
if (Dyn.d_tag == ELF::DT_STRTAB) {
|
|
auto MappedAddrOrError = Elf.toMappedAddr(Dyn.getPtr());
|
|
if (!MappedAddrOrError)
|
|
return MappedAddrOrError.takeError();
|
|
return StringRef(reinterpret_cast<const char *>(*MappedAddrOrError));
|
|
}
|
|
}
|
|
|
|
// If the dynamic segment is not present, we fall back on the sections.
|
|
auto SectionsOrError = Elf.sections();
|
|
if (!SectionsOrError)
|
|
return SectionsOrError.takeError();
|
|
|
|
for (const typename ELFT::Shdr &Sec : *SectionsOrError) {
|
|
if (Sec.sh_type == ELF::SHT_DYNSYM)
|
|
return Elf.getStringTableForSymtab(Sec);
|
|
}
|
|
|
|
return make_error<StringError>("dynamic string table not found",
|
|
inconvertibleErrorCode());
|
|
}
|
|
|
|
template <typename ELFT>
|
|
Expected<LibraryDepsInfo> parseELF(const object::ELFFile<ELFT> &Elf) {
|
|
LibraryDepsInfo Deps;
|
|
Expected<StringRef> StrTabOrErr = getDynamicStrTab(Elf);
|
|
if (!StrTabOrErr)
|
|
return StrTabOrErr.takeError();
|
|
|
|
const char *Data = StrTabOrErr->data();
|
|
|
|
auto DynamicEntriesOrError = Elf.dynamicEntries();
|
|
if (!DynamicEntriesOrError) {
|
|
return DynamicEntriesOrError.takeError();
|
|
}
|
|
|
|
for (const typename ELFT::Dyn &Dyn : *DynamicEntriesOrError) {
|
|
switch (Dyn.d_tag) {
|
|
case ELF::DT_NEEDED:
|
|
Deps.addDep(Data + Dyn.d_un.d_val);
|
|
break;
|
|
case ELF::DT_RPATH: {
|
|
SmallVector<StringRef, 4> RawPaths;
|
|
SplitString(Data + Dyn.d_un.d_val, RawPaths,
|
|
sys::EnvPathSeparator == ':' ? ":" : ";");
|
|
for (const auto &raw : RawPaths)
|
|
Deps.addRPath(raw.str());
|
|
break;
|
|
}
|
|
case ELF::DT_RUNPATH: {
|
|
SmallVector<StringRef, 4> RawPaths;
|
|
SplitString(Data + Dyn.d_un.d_val, RawPaths,
|
|
sys::EnvPathSeparator == ':' ? ":" : ";");
|
|
for (const auto &raw : RawPaths)
|
|
Deps.addRunPath(raw.str());
|
|
break;
|
|
}
|
|
case ELF::DT_FLAGS_1:
|
|
// Check if this is not a pie executable.
|
|
if (Dyn.d_un.d_val & ELF::DF_1_PIE)
|
|
Deps.isPIE = true;
|
|
break;
|
|
// (Dyn.d_tag == ELF::DT_NULL) continue;
|
|
// (Dyn.d_tag == ELF::DT_AUXILIARY || Dyn.d_tag == ELF::DT_FILTER)
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
return Expected<LibraryDepsInfo>(std::move(Deps));
|
|
}
|
|
|
|
Expected<LibraryDepsInfo> parseELFDeps(const object::ELFObjectFileBase &Obj) {
|
|
using namespace object;
|
|
LLVM_DEBUG(dbgs() << "parseELFDeps: Detected ELF object\n";);
|
|
if (const auto *ELF = dyn_cast<ELF32LEObjectFile>(&Obj))
|
|
return parseELF(ELF->getELFFile());
|
|
else if (const auto *ELF = dyn_cast<ELF32BEObjectFile>(&Obj))
|
|
return parseELF(ELF->getELFFile());
|
|
else if (const auto *ELF = dyn_cast<ELF64LEObjectFile>(&Obj))
|
|
return parseELF(ELF->getELFFile());
|
|
else if (const auto *ELF = dyn_cast<ELF64BEObjectFile>(&Obj))
|
|
return parseELF(ELF->getELFFile());
|
|
|
|
LLVM_DEBUG(dbgs() << "parseELFDeps: Unknown ELF format\n";);
|
|
return createStringError(std::errc::not_supported, "Unknown ELF format");
|
|
}
|
|
|
|
Expected<LibraryDepsInfo> parseDependencies(StringRef FilePath,
|
|
object::ObjectFile *Obj) {
|
|
|
|
if (auto *elfObj = dyn_cast<object::ELFObjectFileBase>(Obj)) {
|
|
LLVM_DEBUG(dbgs() << "extractDeps: File " << FilePath
|
|
<< " is an ELF object\n";);
|
|
|
|
return parseELFDeps(*elfObj);
|
|
}
|
|
|
|
if (auto *macho = dyn_cast<object::MachOObjectFile>(Obj)) {
|
|
LLVM_DEBUG(dbgs() << "extractDeps: File " << FilePath
|
|
<< " is a Mach-O object\n";);
|
|
return parseMachODeps(*macho);
|
|
}
|
|
|
|
if (Obj->isCOFF()) {
|
|
// TODO: COFF support
|
|
return LibraryDepsInfo();
|
|
}
|
|
|
|
LLVM_DEBUG(dbgs() << "extractDeps: Unsupported binary format for file "
|
|
<< FilePath << "\n";);
|
|
return createStringError(inconvertibleErrorCode(),
|
|
"Unsupported binary format: %s",
|
|
FilePath.str().c_str());
|
|
}
|
|
|
|
Expected<LibraryDepsInfo> LibraryScanner::extractDeps(StringRef FilePath) {
|
|
LLVM_DEBUG(dbgs() << "extractDeps: Attempting to open file " << FilePath
|
|
<< "\n";);
|
|
// check cache first
|
|
if (auto Cached = ObjCache.take(FilePath)) {
|
|
auto ObjOrErr = Cached->getObjectFile();
|
|
if (!ObjOrErr)
|
|
return ObjOrErr.takeError();
|
|
return parseDependencies(FilePath, &*ObjOrErr);
|
|
}
|
|
|
|
// fall back to normal loading
|
|
ObjectFileLoader ObjLoader(FilePath);
|
|
auto ObjOrErr = ObjLoader.getObjectFile();
|
|
if (!ObjOrErr) {
|
|
LLVM_DEBUG(dbgs() << "extractDeps: Failed to open " << FilePath << "\n";);
|
|
return ObjOrErr.takeError();
|
|
}
|
|
|
|
return parseDependencies(FilePath, &*ObjOrErr);
|
|
}
|
|
|
|
bool LibraryScanner::shouldScan(StringRef FilePath, bool IsResolvingDep) {
|
|
LLVM_DEBUG(dbgs() << "[shouldScan] Checking: " << FilePath << "\n";);
|
|
|
|
LibraryPathCache &Cache = ScanHelper.getCache();
|
|
// [1] Skip if we've already seen this path (via cache)
|
|
if (Cache.hasSeen(FilePath)) {
|
|
LLVM_DEBUG(dbgs() << " -> Skipped: already seen.\n";);
|
|
return false;
|
|
}
|
|
|
|
// [2] Already tracked in LibraryManager?
|
|
/*if (LibMgr.hasLibrary(FilePath)) {
|
|
LLVM_DEBUG(dbgs() << " -> Skipped: already tracked by LibraryManager.\n";);
|
|
return false;
|
|
}*/
|
|
|
|
// [3] Skip if it's not a shared library.
|
|
if (!IsResolvingDep && !Validator.isSharedLibrary(FilePath)) {
|
|
LLVM_DEBUG(dbgs() << " -> Skipped: not a shared library.\n";);
|
|
return false;
|
|
}
|
|
|
|
// Mark seen this path
|
|
Cache.markSeen(FilePath.str());
|
|
|
|
// [4] Run user-defined hook (default: always true)
|
|
if (!ShouldScanCall(FilePath)) {
|
|
LLVM_DEBUG(dbgs() << " -> Skipped: user-defined hook rejected.\n";);
|
|
return false;
|
|
}
|
|
|
|
LLVM_DEBUG(dbgs() << " -> Accepted: ready to scan " << FilePath << "\n";);
|
|
return true;
|
|
}
|
|
|
|
void LibraryScanner::handleLibrary(StringRef FilePath, PathType K, int level) {
|
|
LLVM_DEBUG(dbgs() << "LibraryScanner::handleLibrary: Scanning: " << FilePath
|
|
<< ", level=" << level << "\n";);
|
|
if (!shouldScan(FilePath, level > 0)) {
|
|
LLVM_DEBUG(dbgs() << " Skipped (shouldScan returned false): " << FilePath
|
|
<< "\n";);
|
|
return;
|
|
}
|
|
|
|
auto DepsOrErr = extractDeps(FilePath);
|
|
if (!DepsOrErr) {
|
|
LLVM_DEBUG(dbgs() << " Failed to extract deps for: " << FilePath << "\n";);
|
|
handleError(DepsOrErr.takeError());
|
|
return;
|
|
}
|
|
|
|
LibraryDepsInfo &Deps = *DepsOrErr;
|
|
|
|
LLVM_DEBUG({
|
|
dbgs() << " Found deps : \n";
|
|
for (const auto &dep : Deps.deps)
|
|
dbgs() << " : " << dep << "\n";
|
|
dbgs() << " Found @rpath : " << Deps.rpath.size() << "\n";
|
|
for (const auto &r : Deps.rpath)
|
|
dbgs() << " : " << r << "\n";
|
|
dbgs() << " Found @runpath : \n";
|
|
for (const auto &r : Deps.runPath)
|
|
dbgs() << " : " << r << "\n";
|
|
});
|
|
|
|
if (Deps.isPIE && level == 0) {
|
|
LLVM_DEBUG(dbgs() << " Skipped PIE executable at top level: " << FilePath
|
|
<< "\n";);
|
|
|
|
return;
|
|
}
|
|
|
|
bool Added = LibMgr.addLibrary(FilePath.str(), K);
|
|
if (!Added) {
|
|
LLVM_DEBUG(dbgs() << " Already added: " << FilePath << "\n";);
|
|
return;
|
|
}
|
|
|
|
// Heuristic 1: No RPATH/RUNPATH, skip deps
|
|
if (Deps.rpath.empty() && Deps.runPath.empty()) {
|
|
LLVM_DEBUG(
|
|
dbgs() << "LibraryScanner::handleLibrary: Skipping deps (Heuristic1): "
|
|
<< FilePath << "\n";);
|
|
return;
|
|
}
|
|
|
|
// Heuristic 2: All RPATH and RUNPATH already tracked
|
|
auto allTracked = [&](const auto &Paths) {
|
|
LLVM_DEBUG(dbgs() << " Checking : " << Paths.size() << "\n";);
|
|
return std::all_of(Paths.begin(), Paths.end(), [&](StringRef P) {
|
|
LLVM_DEBUG(dbgs() << " Checking isTrackedBasePath : " << P << "\n";);
|
|
return ScanHelper.isTrackedBasePath(
|
|
DylibResolver::resolvelinkerFlag(P, FilePath));
|
|
});
|
|
};
|
|
|
|
if (allTracked(Deps.rpath) && allTracked(Deps.runPath)) {
|
|
LLVM_DEBUG(
|
|
dbgs() << "LibraryScanner::handleLibrary: Skipping deps (Heuristic2): "
|
|
<< FilePath << "\n";);
|
|
return;
|
|
}
|
|
|
|
DylibResolver Resolver(Validator);
|
|
Resolver.configure(FilePath,
|
|
{{Deps.rpath, SearchPathType::RPath},
|
|
{ScanHelper.getSearchPaths(), SearchPathType::UsrOrSys},
|
|
{Deps.runPath, SearchPathType::RunPath}});
|
|
for (StringRef Dep : Deps.deps) {
|
|
LLVM_DEBUG(dbgs() << " Resolving dep: " << Dep << "\n";);
|
|
auto DepFullOpt = Resolver.resolve(Dep);
|
|
if (!DepFullOpt) {
|
|
LLVM_DEBUG(dbgs() << " Failed to resolve dep: " << Dep << "\n";);
|
|
continue;
|
|
}
|
|
LLVM_DEBUG(dbgs() << " Resolved dep to: " << *DepFullOpt << "\n";);
|
|
|
|
handleLibrary(*DepFullOpt, K, level + 1);
|
|
}
|
|
}
|
|
|
|
void LibraryScanner::scanBaseDir(LibrarySearchPath *SP) {
|
|
if (!sys::fs::is_directory(SP->BasePath) || SP->BasePath.empty()) {
|
|
LLVM_DEBUG(
|
|
dbgs() << "LibraryScanner::scanBaseDir: Invalid or empty basePath: "
|
|
<< SP->BasePath << "\n";);
|
|
return;
|
|
}
|
|
|
|
LLVM_DEBUG(dbgs() << "LibraryScanner::scanBaseDir: Scanning directory: "
|
|
<< SP->BasePath << "\n";);
|
|
std::error_code EC;
|
|
|
|
SP->State.store(ScanState::Scanning);
|
|
|
|
for (sys::fs::directory_iterator It(SP->BasePath, EC), end; It != end && !EC;
|
|
It.increment(EC)) {
|
|
auto Entry = *It;
|
|
if (!Entry.status())
|
|
continue;
|
|
|
|
auto Status = *Entry.status();
|
|
if (sys::fs::is_regular_file(Status) || sys::fs::is_symlink_file(Status)) {
|
|
LLVM_DEBUG(dbgs() << " Found file: " << Entry.path() << "\n";);
|
|
|
|
std::string FinalPath;
|
|
bool IsSymlink = sys::fs::is_symlink_file(Status);
|
|
|
|
// Resolve symlink
|
|
if (IsSymlink) {
|
|
LLVM_DEBUG(dbgs() << " Symlink → resolving...\n");
|
|
|
|
auto CanonicalOpt = ScanHelper.resolve(Entry.path(), EC);
|
|
if (EC || !CanonicalOpt) {
|
|
LLVM_DEBUG(dbgs() << " -> Skipped: resolve failed (EC="
|
|
<< EC.message() << ")\n");
|
|
continue;
|
|
}
|
|
|
|
FinalPath = std::move(*CanonicalOpt);
|
|
|
|
LLVM_DEBUG(dbgs() << " Canonical: " << FinalPath << "\n");
|
|
|
|
} else {
|
|
// make absolute
|
|
SmallString<256> Abs(Entry.path());
|
|
sys::fs::make_absolute(Abs);
|
|
FinalPath = Abs.str().str();
|
|
|
|
LLVM_DEBUG(dbgs() << " Regular: absolute = " << FinalPath << "\n");
|
|
}
|
|
|
|
// Check if it's a directory — skip directories
|
|
if (sys::fs::is_directory(Status)) {
|
|
LLVM_DEBUG(dbgs() << " -> Skipped: path is a directory.\n";);
|
|
continue;
|
|
}
|
|
|
|
// async support ?
|
|
handleLibrary(FinalPath, SP->Kind);
|
|
}
|
|
}
|
|
|
|
SP->State.store(ScanState::Scanned);
|
|
}
|
|
|
|
void LibraryScanner::scanNext(PathType K, size_t BatchSize) {
|
|
LLVM_DEBUG(dbgs() << "LibraryScanner::scanNext: Scanning next batch of size "
|
|
<< BatchSize << " for kind "
|
|
<< (K == PathType::User ? "User" : "System") << "\n";);
|
|
|
|
SmallVector<const LibrarySearchPath *> SearchPaths;
|
|
ScanHelper.getNextBatch(K, BatchSize, SearchPaths);
|
|
for (const auto *SP : SearchPaths) {
|
|
LLVM_DEBUG(dbgs() << " Scanning unit with basePath: " << SP->BasePath
|
|
<< "\n";);
|
|
scanBaseDir(const_cast<LibrarySearchPath *>(SP));
|
|
}
|
|
}
|
|
} // end namespace llvm::orc
|