[llvm] [ORC] Add auto-loading of shared libraries for unresolved symbols. (PR #148410)
via llvm-commits
llvm-commits at lists.llvm.org
Sun Jul 13 03:47:36 PDT 2025
https://github.com/SahilPatidar updated https://github.com/llvm/llvm-project/pull/148410
>From 90341d6796276e9cc9d1a0c16a7cd13058cb14aa Mon Sep 17 00:00:00 2001
From: SahilPatidar <patidarsahil2001 at gmail.com>
Date: Wed, 2 Jul 2025 14:47:27 +0530
Subject: [PATCH 1/6] [ORC] Add auto-loading of shared libraries for unresolved
symbols
---
.../ExecutionEngine/Orc/Shared/SymbolFilter.h | 173 ++++
.../Orc/TargetProcess/DynamicLoader.h | 490 +++++++++++
.../Orc/TargetProcess/LibraryScanner.h | 253 ++++++
.../Orc/TargetProcess/CMakeLists.txt | 2 +
.../Orc/TargetProcess/DynamicLoader.cpp | 274 ++++++
.../Orc/TargetProcess/LibraryScanner.cpp | 789 ++++++++++++++++++
6 files changed, 1981 insertions(+)
create mode 100644 llvm/include/llvm/ExecutionEngine/Orc/Shared/SymbolFilter.h
create mode 100644 llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/DynamicLoader.h
create mode 100644 llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/LibraryScanner.h
create mode 100644 llvm/lib/ExecutionEngine/Orc/TargetProcess/DynamicLoader.cpp
create mode 100644 llvm/lib/ExecutionEngine/Orc/TargetProcess/LibraryScanner.cpp
diff --git a/llvm/include/llvm/ExecutionEngine/Orc/Shared/SymbolFilter.h b/llvm/include/llvm/ExecutionEngine/Orc/Shared/SymbolFilter.h
new file mode 100644
index 0000000000000..8848564d1faef
--- /dev/null
+++ b/llvm/include/llvm/ExecutionEngine/Orc/Shared/SymbolFilter.h
@@ -0,0 +1,173 @@
+//===--- SymbolFilter.h - Utils for Symbol Filter ---*- 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_EXECUTIONENGINE_ORC_SHARED_SYMBOLFILTER_H
+#define LLVM_EXECUTIONENGINE_ORC_SHARED_SYMBOLFILTER_H
+
+#include "llvm/ExecutionEngine/Orc/Shared/SimplePackedSerialization.h"
+
+#include <math.h>
+#include <type_traits>
+#include <vector>
+
+namespace llvm {
+namespace orc {
+
+namespace shared {
+using SPSBloomFilter =
+ SPSTuple<bool, uint32_t, uint32_t, uint32_t, SPSSequence<uint64_t>>;
+}
+
+class BloomFilter {
+public:
+ using HashFunc = std::function<uint32_t(StringRef)>;
+
+ BloomFilter() = default;
+ BloomFilter(BloomFilter &&) noexcept = default;
+ BloomFilter &operator=(BloomFilter &&) noexcept = default;
+ BloomFilter(const BloomFilter &) = delete;
+ BloomFilter &operator=(const BloomFilter &) = delete;
+
+ BloomFilter(uint32_t symbolCount, float falsePositiveRate, HashFunc hashFn)
+ : hashFunc(std::move(hashFn)) {
+ initialize(symbolCount, falsePositiveRate);
+ }
+ bool IsInitialized() const { return initialized; }
+
+ void add(StringRef symbol) {
+ assert(initialized);
+ addHash(hashFunc(symbol));
+ }
+
+ bool mayContain(StringRef symbol) const {
+ return !isEmpty() && testHash(hashFunc(symbol));
+ }
+
+ bool isEmpty() const { return symbolCount_ == 0; }
+
+private:
+ friend class shared::SPSSerializationTraits<shared::SPSBloomFilter,
+ BloomFilter>;
+ static constexpr uint32_t bitsPerEntry = 64;
+
+ bool initialized = false;
+ uint32_t symbolCount_ = 0;
+ uint32_t bloomSize = 0;
+ uint32_t bloomShift = 0;
+ std::vector<uint64_t> bloomTable;
+ HashFunc hashFunc;
+
+ void initialize(uint32_t symbolCount, float falsePositiveRate) {
+ assert(symbolCount > 0);
+ symbolCount_ = symbolCount;
+ initialized = true;
+
+ float ln2 = std::log(2.0f);
+ float m = -1.0f * symbolCount * std::log(falsePositiveRate) / (ln2 * ln2);
+ bloomSize = static_cast<uint32_t>(std::ceil(m / bitsPerEntry));
+ bloomShift = std::min(6u, log2ceil(symbolCount));
+ bloomTable.resize(bloomSize, 0);
+ }
+
+ void addHash(uint32_t hash) {
+ uint32_t hash2 = hash >> bloomShift;
+ uint32_t n = (hash / bitsPerEntry) % bloomSize;
+ uint64_t mask =
+ (1ULL << (hash % bitsPerEntry)) | (1ULL << (hash2 % bitsPerEntry));
+ bloomTable[n] |= mask;
+ }
+
+ bool testHash(uint32_t hash) const {
+ uint32_t hash2 = hash >> bloomShift;
+ uint32_t n = (hash / bitsPerEntry) % bloomSize;
+ uint64_t mask =
+ (1ULL << (hash % bitsPerEntry)) | (1ULL << (hash2 % bitsPerEntry));
+ return (bloomTable[n] & mask) == mask;
+ }
+
+ static constexpr uint32_t log2ceil(uint32_t v) {
+ return v <= 1 ? 0 : 32 - countl_zero(v - 1);
+ }
+};
+
+class BloomFilterBuilder {
+public:
+ using HashFunc = BloomFilter::HashFunc;
+
+ BloomFilterBuilder() = default;
+
+ BloomFilterBuilder &setFalsePositiveRate(float rate) {
+ assert(rate > 0.0f && rate < 1.0f);
+ falsePositiveRate = rate;
+ return *this;
+ }
+
+ BloomFilterBuilder &setHashFunction(HashFunc func) {
+ hashFunc = std::move(func);
+ return *this;
+ }
+
+ BloomFilter build(const std::vector<std::string> &symbols) const {
+ assert(!symbols.empty() && "Cannot build filter from empty symbol list.");
+ BloomFilter filter(static_cast<uint32_t>(symbols.size()), falsePositiveRate,
+ hashFunc);
+ for (const auto &sym : symbols) {
+ filter.add(sym);
+ }
+ return filter;
+ }
+
+private:
+ float falsePositiveRate = 0.02f;
+ HashFunc hashFunc = [](StringRef s) -> uint32_t {
+ uint32_t h = 5381;
+ for (char c : s)
+ h = ((h << 5) + h) + static_cast<uint8_t>(c); // h * 33 + c
+ return h;
+ };
+};
+
+namespace shared {
+
+template <> class SPSSerializationTraits<SPSBloomFilter, BloomFilter> {
+public:
+ static size_t size(const BloomFilter &Filter) {
+ return SPSBloomFilter::AsArgList::size(
+ Filter.initialized, Filter.symbolCount_, Filter.bloomSize,
+ Filter.bloomShift, Filter.bloomTable);
+ }
+
+ static bool serialize(SPSOutputBuffer &OB, const BloomFilter &Filter) {
+ return SPSBloomFilter::AsArgList::serialize(
+ OB, Filter.initialized, Filter.symbolCount_, Filter.bloomSize,
+ Filter.bloomShift, Filter.bloomTable);
+ }
+
+ static bool deserialize(SPSInputBuffer &IB, BloomFilter &Filter) {
+ bool IsInitialized;
+ uint32_t symbolCount_ = 0, bloomSize = 0, bloomShift = 0;
+ std::vector<uint64_t> bloomTable;
+
+ if (!SPSBloomFilter::AsArgList::deserialize(
+ IB, IsInitialized, symbolCount_, bloomSize, bloomShift, bloomTable))
+ return false;
+
+ Filter.initialized = IsInitialized;
+ Filter.symbolCount_ = symbolCount_;
+ Filter.bloomSize = bloomSize;
+ Filter.bloomShift = bloomShift;
+ Filter.bloomTable = std::move(bloomTable);
+
+ return true;
+ }
+};
+
+} // end namespace shared
+} // end namespace orc
+} // end namespace llvm
+#endif // LLVM_EXECUTIONENGINE_ORC_SHARED_SYMBOLFILTER_H
diff --git a/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/DynamicLoader.h b/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/DynamicLoader.h
new file mode 100644
index 0000000000000..0b98be4cd9bf1
--- /dev/null
+++ b/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/DynamicLoader.h
@@ -0,0 +1,490 @@
+//===- DynamicLoader.h - Automatic Dynamic Library Symbol Resolution -*- 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
+//
+//===----------------------------------------------------------------------===//
+//
+// This file provides support for automatically searching symbols across
+// dynamic libraries that have not yet been loaded.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_EXECUTIONENGINE_ORC_TARGETPROCESS_DYNAMICLOADER_H
+#define LLVM_EXECUTIONENGINE_ORC_TARGETPROCESS_DYNAMICLOADER_H
+
+#include "llvm/ADT/FunctionExtras.h"
+#include "llvm/ExecutionEngine/Orc/Shared/SymbolFilter.h"
+#include "llvm/ExecutionEngine/Orc/TargetProcess/LibraryScanner.h"
+#include "llvm/Support/Path.h"
+
+#include <shared_mutex>
+#include <unordered_map>
+
+namespace llvm {
+namespace orc {
+
+// Represents a collection of libraries, each optionally associated with a
+// symbol filter and hash map.
+// class LibraryCollection {
+// public:
+// class LibraryInfo {
+// public:
+// LibraryInfo(const LibraryInfo &) = delete;
+// LibraryInfo &operator=(const LibraryInfo &) = delete;
+
+// LibraryInfo(std::string basePath,
+// std::optional<BloomFilter> filter = std::nullopt
+// : basePath(std::move(basePath)), filter(std::move(filter)) {}
+
+// StringRef getBasePath() const { return basePath; }
+// StringRef getFileName() const { return fileName; }
+
+// std::string getFullPath() const {
+// llvm::SmallString<512> fullPath(basePath);
+// llvm::sys::path::append(fullPath, llvm::StringRef(fileName));
+// return std::string(fullPath.str());
+// }
+
+// bool setFilter(const BloomFilter &f);
+// bool buildFilter(const std::vector<std::string> &symbols);
+// bool ensureFilterBuilt(const std::vector<std::string> &symbols);
+// bool buildHashMap(const std::vector<std::string> &symbols);
+
+// bool mayContain(StringRef symbol) const;
+// bool hasFilter() const { return filter.has_value(); }
+// bool hasHashMap() const { return hashMap.has_value(); }
+
+// private:
+// std::string basePath;
+// std::string fileName;
+// std::optional<BloomFilter> filter;
+// std::mutex mutex;
+// };
+
+// using LibraryVisitor = unique_function<bool(const LibraryInfo &)>;
+
+// // Visit each library. Stops early if visitor returns false.
+// bool forEachLibrary(LibraryVisitor &&visitor) const;
+
+// // Visit each library. Removes those for which the visitor returns true.
+// bool removeIf(LibraryVisitor &&shouldRemove);
+
+// // Adds a new library with optional filter and hash map.
+// bool addLibrary(std::string basePath,
+// std::optional<BloomFilter> filter = std::nullopt);
+
+// // Removes a library by base path.
+// bool removeLibrary(const std::string &basePath);
+
+// private:
+// struct LibraryInfoHash {
+// size_t operator()(const LibraryInfo &lib) const {
+// return std::hash<size_t>()(lib.getBasePath().length()) ^
+// std::hash<std::string>()(std::string(lib.getFileName()));
+// }
+// };
+
+// std::unordered_set<LibraryInfo, LibraryInfoHash> libraries;
+// std::vector<const LibraryInfo *> Libs;
+// mutable std::mutex mutex;
+// };
+
+/// Manages library metadata and state for symbol resolution.
+///
+/// Tracks libraries by load state and kind (user/system), and stores associated
+/// Bloom filters and hash maps to speed up symbol lookups. Thread-safe for
+/// concurrent access.
+class LibraryManager {
+public:
+ enum class State : uint8_t { Unloaded = 0, Loaded = 1, Queried = 2 };
+ enum class Kind : uint8_t { User = 0, System = 1 };
+
+ class LibraryInfo {
+ public:
+ LibraryInfo(const LibraryInfo &) = delete;
+ LibraryInfo &operator=(const LibraryInfo &) = delete;
+
+ LibraryInfo(std::string filePath, State s, Kind k,
+ std::optional<BloomFilter> filter = std::nullopt)
+ : filePath(std::move(filePath)), state(s), kind(k),
+ filter(std::move(filter)) {}
+
+ StringRef getBasePath() const { return sys::path::parent_path(filePath); }
+ StringRef getFileName() const { return sys::path::filename(filePath); }
+
+ std::string getFullPath() const { return filePath; }
+
+ bool setFilter(BloomFilter F) {
+ std::lock_guard lock(mutex);
+ if (filter)
+ return false;
+ filter.emplace(std::move(F));
+ return true;
+ }
+
+ bool ensureFilterBuilt(const BloomFilterBuilder &FB,
+ const std::vector<std::string> &symbols) {
+ std::lock_guard lock(mutex);
+ if (filter)
+ return false;
+ filter.emplace(FB.build(symbols));
+ return true;
+ }
+
+ bool mayContain(StringRef symbol) const {
+ assert(hasFilter());
+ std::shared_lock lock(mutex);
+ return filter->mayContain(symbol);
+ }
+
+ bool hasFilter() const {
+ std::shared_lock lock(mutex);
+ return filter.has_value();
+ }
+
+ State getState() const { return state.load(); }
+ Kind getKind() const { return kind; }
+
+ void setState(State s) { state.store(s); }
+
+ bool operator==(const LibraryInfo &other) const {
+ return filePath == other.filePath;
+ }
+
+ private:
+ std::string filePath;
+ std::atomic<State> state;
+ Kind kind;
+ std::optional<BloomFilter> filter;
+ mutable std::shared_mutex mutex;
+ };
+
+ class FilteredView {
+ public:
+ using Map = std::unordered_map<std::string, std::shared_ptr<LibraryInfo>>;
+ using Iterator = typename Map::const_iterator;
+ class FilterIterator {
+ public:
+ FilterIterator(Iterator _it, Iterator _end, State s, Kind k)
+ : it(_it), end(_end), state(s), kind(k) {
+ advance();
+ }
+
+ bool operator!=(const FilterIterator &other) const {
+ return it != other.it;
+ }
+ const std::shared_ptr<LibraryInfo> &operator*() const {
+ return it->second;
+ }
+
+ FilterIterator &operator++() {
+ ++it;
+ advance();
+ return *this;
+ }
+
+ private:
+ void advance() {
+ while (it != end) {
+ const auto &lib = it->second;
+ if (lib->getState() == state && lib->getKind() == kind)
+ break;
+ ++it;
+ }
+ }
+ Iterator it, end;
+ State state;
+ Kind kind;
+ };
+ FilteredView(Iterator begin, Iterator end, State s, Kind k)
+ : begin_(begin), end_(end), state_(s), kind_(k) {}
+
+ FilterIterator begin() const {
+ return FilterIterator(begin_, end_, state_, kind_);
+ }
+
+ FilterIterator end() const {
+ return FilterIterator(end_, end_, state_, kind_);
+ }
+
+ private:
+ Iterator begin_, end_;
+ State state_;
+ Kind kind_;
+ };
+
+private:
+ std::unordered_map<std::string, std::shared_ptr<LibraryInfo>> libraries;
+ mutable std::shared_mutex mutex;
+
+public:
+ using LibraryVisitor = std::function<bool(const LibraryInfo &)>;
+
+ LibraryManager() = default;
+ ~LibraryManager() = default;
+
+ bool addLibrary(std::string path, Kind kind,
+ std::optional<BloomFilter> filter = std::nullopt) {
+ std::unique_lock<std::shared_mutex> lock(mutex);
+ // SmallString<256> nativePath(path);
+ // sys::path::native(nativePath);
+
+ // auto P = nativePath.str();
+
+ if (libraries.count(path) > 0)
+ return false;
+ libraries.emplace(std::move(path),
+ std::make_shared<LibraryInfo>(path, State::Unloaded, kind,
+ std::move(filter)));
+ return true;
+ }
+
+ bool hasLibrary(StringRef path) {
+ std::shared_lock<std::shared_mutex> lock(mutex);
+ if (libraries.count(path.str()) > 0)
+ return true;
+ return false;
+ }
+
+ bool removeLibrary(const std::string &path) {
+ std::unique_lock<std::shared_mutex> lock(mutex);
+ // auto P = sys::path::native(path);
+ auto I = libraries.find(path);
+ if (I == libraries.end())
+ return false;
+ libraries.erase(I);
+ return true;
+ }
+
+ void markLoaded(const std::string &path) {
+ std::unique_lock<std::shared_mutex> lock(mutex);
+ if (auto it = libraries.find(path); it != libraries.end()) {
+ it->second->setState(State::Loaded);
+ }
+ }
+
+ void markQueried(const std::string &path) {
+ std::unique_lock<std::shared_mutex> lock(mutex);
+ if (auto it = libraries.find(path); it != libraries.end()) {
+ it->second->setState(State::Queried);
+ }
+ }
+
+ std::shared_ptr<LibraryInfo> getLibrary(const std::string &path) {
+ std::shared_lock<std::shared_mutex> lock(mutex);
+ if (auto it = libraries.find(path); it != libraries.end())
+ return it->second;
+ return nullptr;
+ }
+
+ FilteredView getView(State s, Kind k) const {
+ std::shared_lock lock(mutex);
+ return FilteredView(libraries.begin(), libraries.end(), s, k);
+ }
+
+ void forEachLibrary(const LibraryVisitor &visitor) const {
+ std::unique_lock lock(mutex);
+ for (const auto &[_, entry] : libraries) {
+ if (!visitor(*entry))
+ break;
+ }
+ }
+
+ bool isLoaded(StringRef path) const {
+ std::unique_lock lock(mutex);
+ if (auto it = libraries.find(path.str()); it != libraries.end())
+ return it->second->getState() == State::Loaded;
+ return false;
+ }
+
+ bool isQueried(StringRef path) const {
+ std::unique_lock lock(mutex);
+ if (auto it = libraries.find(path.str()); it != libraries.end())
+ return it->second->getState() == State::Queried;
+ return false;
+ }
+
+ void clear() {
+ std::unique_lock lock(mutex);
+ libraries.clear();
+ }
+};
+
+using LibraryInfo = LibraryManager::LibraryInfo;
+
+/// Scans libraries and resolves symbols across user and system paths.
+///
+/// Supports symbol enumeration and filtering via SymbolEnumerator, and tracks
+/// symbol resolution results through SymbolQuery. Thread-safe and uses
+/// LibraryScanHelper for efficient path resolution and caching.
+class DynamicLoader {
+public:
+ class SymbolEnumerator {
+ public:
+ enum class Result { Continue, Stop, Error };
+
+ using OnEachSymbolFn = std::function<Result(const std::string &)>;
+
+ enum class Filter : uint32_t {
+ None = 0,
+ IgnoreUndefined = 1 << 0,
+ IgnoreWeak = 1 << 1,
+ IgnoreIndirect = 1 << 2,
+
+ Default = IgnoreUndefined
+ };
+
+ struct Options {
+ uint32_t FilterFlags = static_cast<uint32_t>(Filter::Default);
+ };
+
+ static bool enumerateSymbols(llvm::StringRef Path, OnEachSymbolFn OnEach,
+ const Options &Opts);
+ };
+
+ class SymbolQuery {
+ public:
+ struct Result {
+ std::string Name;
+ std::string ResolvedLibPath;
+ };
+
+ private:
+ mutable std::shared_mutex mtx;
+ std::unordered_map<std::string, Result> results;
+ std::atomic<size_t> resolvedCount = 0;
+
+ public:
+ explicit SymbolQuery(const std::vector<std::string> &symbols) {
+ for (const auto &s : symbols)
+ results.emplace(s, Result{s, ""});
+ }
+
+ std::vector<std::string> getUnresolvedSymbols() const {
+ std::vector<std::string> unresolved;
+ std::shared_lock lock(mtx);
+ for (const auto &[name, res] : results) {
+ if (res.ResolvedLibPath.empty())
+ unresolved.push_back(name);
+ }
+ return unresolved;
+ }
+
+ void resolve(const std::string &symbol, const std::string &libPath) {
+ std::unique_lock lock(mtx);
+ auto it = results.find(symbol);
+ if (it != results.end() && it->second.ResolvedLibPath.empty()) {
+ it->second.ResolvedLibPath = libPath;
+ ++resolvedCount;
+ }
+ }
+
+ bool allResolved() const {
+ return resolvedCount.load(std::memory_order_relaxed) == results.size();
+ }
+
+ bool hasUnresolved() const {
+ return resolvedCount.load(std::memory_order_relaxed) < results.size();
+ }
+
+ std::optional<std::string> getResolvedLib(const std::string &symbol) const {
+ std::shared_lock lock(mtx);
+ auto it = results.find(symbol);
+ if (it != results.end() && !it->second.ResolvedLibPath.empty())
+ return it->second.ResolvedLibPath;
+ return std::nullopt;
+ }
+
+ bool isResolved(const std::string &symbol) const {
+ std::shared_lock lock(mtx);
+ auto it = results.find(symbol);
+ return it != results.end() && !it->second.ResolvedLibPath.empty();
+ }
+
+ std::vector<Result> getAllResults() const {
+ std::shared_lock lock(mtx);
+ std::vector<Result> out;
+ out.reserve(results.size());
+ for (const auto &[_, res] : results)
+ out.push_back(res);
+ return out;
+ }
+ };
+
+ struct Setup {
+ std::vector<std::string> basePaths;
+ std::shared_ptr<LibraryPathCache> cache;
+ std::shared_ptr<PathResolver> resolver;
+ // std::shared_ptr<DylibPathResolver> dylibResolver;
+
+ bool includeSys = false;
+
+ LibraryScanner::shouldScanFn shouldScan = [](StringRef) { return true; };
+
+ BloomFilterBuilder filterBuilder = BloomFilterBuilder();
+
+ static Setup create(
+ std::vector<std::string> basePaths,
+ std::shared_ptr<LibraryPathCache> existingCache = nullptr,
+ std::shared_ptr<PathResolver> existingResolver = nullptr,
+ // std::shared_ptr<DylibPathResolver> existingDylibResolver = nullptr,
+ LibraryScanner::shouldScanFn customShouldScan = nullptr) {
+ Setup setup;
+ setup.basePaths = std::move(basePaths);
+
+ setup.cache =
+ existingCache ? existingCache : std::make_shared<LibraryPathCache>();
+
+ setup.resolver = existingResolver
+ ? existingResolver
+ : std::make_shared<PathResolver>(setup.cache);
+
+ // setup.dylibResolver = std::move(existingDylibResolver);
+
+ if (customShouldScan)
+ setup.shouldScan = std::move(customShouldScan);
+
+ return setup;
+ }
+ };
+
+ DynamicLoader() = delete;
+ explicit DynamicLoader(const Setup &setup);
+ ~DynamicLoader() = default;
+
+ using OnSearchComplete = unique_function<void(SymbolQuery &)>;
+
+ void searchSymbolsInLibraries(std::vector<std::string> &symbolNames,
+ OnSearchComplete callback);
+
+private:
+ void scanLibrariesIfNeeded(LibraryManager::Kind K);
+ void resolveSymbolsInLibrary(LibraryInfo &library, SymbolQuery &query);
+ bool
+ symbolExistsInLibrary(const LibraryInfo &library, llvm::StringRef symbol,
+ std::vector<std::string> *matchedSymbols = nullptr);
+
+ bool symbolExistsInLibrary(const LibraryInfo &lib, llvm::StringRef symbolName,
+ std::vector<std::string> *allSymbols,
+ const SymbolEnumerator::Options &opts);
+
+ std::shared_ptr<LibraryPathCache> m_cache;
+ std::shared_ptr<PathResolver> m_PathResolver;
+ LibraryScanHelper ScanH;
+ BloomFilterBuilder FB;
+ LibraryManager LibMgr;
+ LibraryScanner::shouldScanFn m_shouldScan;
+ // std::shared_ptr<DylibPathResolver> m_DylibPathResolver;
+ bool includeSys;
+};
+
+using SymbolEnumerator = DynamicLoader::SymbolEnumerator;
+using EnumerateResult = SymbolEnumerator::Result;
+
+} // end namespace orc
+} // end namespace llvm
+
+#endif // LLVM_EXECUTIONENGINE_ORC_TARGETPROCESS_DYNAMICLOADER_H
diff --git a/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/LibraryScanner.h b/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/LibraryScanner.h
new file mode 100644
index 0000000000000..d4be0bce2b125
--- /dev/null
+++ b/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/LibraryScanner.h
@@ -0,0 +1,253 @@
+//===- LibraryScanner.h - Scan Library -*- 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
+//
+//===----------------------------------------------------------------------===//
+//
+// This file provides support for scanning dynamic library.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_EXECUTIONENGINE_ORC_TARGETPROCESS_LIBRARYSCANNER_H
+#define LLVM_EXECUTIONENGINE_ORC_TARGETPROCESS_LIBRARYSCANNER_H
+
+#include "llvm/ADT/FunctionExtras.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Error.h"
+
+#include <mutex>
+#include <queue>
+#include <shared_mutex>
+#include <string>
+#include <unordered_map>
+#include <unordered_set>
+
+namespace llvm {
+namespace orc {
+
+class LibraryManager;
+
+class LibraryPathCache {
+ friend class PathResolver;
+
+public:
+ LibraryPathCache() = default;
+
+ void clear();
+
+ void markSeen(const std::string &canon_path) { m_seen.insert(canon_path); }
+
+ bool hasSeen(StringRef canon_path, bool cache = true) {
+ std::shared_lock lock(m_mutex);
+ std::string s = canon_path.str();
+ if (m_seen.count(s) > 0)
+ return true;
+ if (cache)
+ markSeen(s);
+ return false;
+ }
+
+private:
+ mutable std::shared_mutex m_mutex;
+
+ struct PathInfo {
+ std::string canonicalPath;
+ std::error_code errnoCode;
+ };
+
+ std::unordered_map<std::string, std::string> m_readlinkCache;
+ std::unordered_map<std::string, PathInfo> m_realpathCache;
+ std::unordered_map<std::string, mode_t> m_lstatCache;
+ std::unordered_set<std::string> m_seen;
+};
+
+/// Resolves file system paths with optional caching of results.
+///
+/// Supports lstat, readlink, and realpath operations. Can resolve paths
+/// relative to a base and handle symbolic links. Caches results to reduce
+/// repeated system calls when enabled.
+class PathResolver {
+public:
+ PathResolver(std::shared_ptr<LibraryPathCache> cache)
+ : m_cache(std::move(cache)) {}
+
+ std::optional<std::string> resolve(const std::string &path,
+ std::error_code &ec) {
+ return realpathCached(path, ec);
+ }
+ mode_t lstatCached(const std::string &path);
+ std::optional<std::string> readlinkCached(const std::string &path);
+ std::optional<std::string> realpathCached(StringRef path, std::error_code &ec,
+ StringRef base = "",
+ bool baseIsResolved = false,
+ long symloopLevel = 40);
+
+private:
+ mutable std::shared_mutex m_mutex;
+ std::shared_ptr<LibraryPathCache> m_cache;
+};
+
+class LibraryScanHelper;
+
+class DylibPathResolver {
+public:
+ DylibPathResolver(LibraryScanHelper &m_helper) : m_helper(m_helper) {}
+
+ /// Resolve a dynamic library path considering RPath, RunPath, and
+ /// substitutions.
+ std::optional<std::string> resolve(StringRef libStem,
+ SmallVector<StringRef, 2> RPath = {},
+ SmallVector<StringRef, 2> RunPath = {},
+ StringRef libLoader = "",
+ bool variateLibStem = true);
+
+private:
+ LibraryScanHelper &m_helper;
+
+ std::optional<std::string> substOne(StringRef path, StringRef pattern,
+ StringRef replacement);
+
+ /// Apply all known loader substitutions to the path
+ std::optional<std::string> substAll(StringRef path, StringRef loaderPath);
+
+ std::optional<std::string> tryWithBasePaths(ArrayRef<StringRef> basePaths,
+ StringRef stem,
+ StringRef loaderPath);
+
+ /// Try resolving the path using RPATH, searchPaths, and RUNPATH (in that
+ /// order)
+ std::optional<std::string> tryAllPaths(StringRef stem,
+ ArrayRef<StringRef> RPath,
+ ArrayRef<StringRef> RunPath,
+ StringRef loaderPath);
+
+ std::optional<std::string> tryWithExtensions(StringRef baseName,
+ ArrayRef<StringRef> RPath,
+ ArrayRef<StringRef> RunPath,
+ StringRef loaderPath);
+
+ std::optional<std::string> normalizeIfShared(StringRef path);
+};
+
+enum class PathKind { User, System };
+
+enum class ScanState { NotScanned, Scanning, Scanned };
+
+struct LibraryUnit {
+ std::string basePath; // Canonical base directory path
+ PathKind kind; // User or System
+ std::atomic<ScanState> state;
+
+ LibraryUnit(std::string base, PathKind k)
+ : basePath(std::move(base)), kind(k), state(ScanState::NotScanned) {}
+};
+
+/// Scans and tracks libraries for symbol resolution.
+///
+/// Maintains a list of library paths to scan, caches scanned units,
+/// and resolves paths canonically for consistent tracking.
+class LibraryScanHelper {
+public:
+ explicit LibraryScanHelper(const std::vector<std::string> &paths,
+ std::shared_ptr<LibraryPathCache> m_cache,
+ std::shared_ptr<PathResolver> m_resolver)
+ : m_cache(std::move(m_cache)), m_resolver(std::move(m_resolver)) {
+ for (const auto &p : paths)
+ addBasePath(p);
+ }
+
+ void addBasePath(
+ const std::string &path); // Add a canonical directory for scanning
+ std::vector<std::shared_ptr<LibraryUnit>> getNextBatch(PathKind kind,
+ size_t batchSize);
+
+ bool isTrackedBasePath(const std::string &path) const;
+ std::vector<std::shared_ptr<LibraryUnit>> getAllUnits() const;
+
+ PathResolver &getPathResolver() const { return *m_resolver; }
+
+ LibraryPathCache &getCache() const { return *m_cache; }
+
+ bool hasSeen(StringRef path) const { return m_cache->hasSeen(path); }
+
+ std::optional<std::string> resolve(StringRef path,
+ std::error_code &ec) const {
+ return m_resolver->resolve(path.str(), ec);
+ }
+
+private:
+ std::string resolveCanonical(const std::string &path,
+ std::error_code &ec) const;
+ PathKind classifyKind(const std::string &path) const;
+
+ mutable std::shared_mutex m_fileMutex;
+ mutable std::shared_mutex m_mutex;
+ std::shared_ptr<LibraryPathCache> m_cache;
+ std::shared_ptr<PathResolver> m_resolver;
+
+ std::unordered_map<std::string, std::shared_ptr<LibraryUnit>>
+ m_units; // key: canonical path
+ std::deque<std::string> m_unscannedUsr;
+ std::deque<std::string> m_unscannedSys;
+};
+
+class LibraryScanner {
+public:
+ using shouldScanFn = std::function<bool(StringRef)>;
+
+ LibraryScanner(
+ LibraryScanHelper &H, LibraryManager &m_libMgr,
+ shouldScanFn shouldScanCall = [](StringRef path) { return true; })
+ : m_helper(H), m_libMgr(m_libMgr), m_libResolver(DylibPathResolver(H)),
+ shouldScanCall(std::move(shouldScanCall)) {}
+
+ void scanNext(PathKind kind, size_t batchSize = 1);
+
+ struct LibraryDepsInfo {
+ std::vector<std::string> storage;
+
+ SmallVector<StringRef, 2> rpath;
+ SmallVector<StringRef, 2> runPath;
+ SmallVector<StringRef, 4> deps;
+ bool isPIE = false;
+
+ void addRPath(StringRef s) {
+ storage.emplace_back(s);
+ rpath.push_back(storage.back());
+ }
+
+ void addRunPath(StringRef s) {
+ storage.emplace_back(s);
+ runPath.push_back(storage.back());
+ }
+
+ void addDep(StringRef s) {
+ storage.emplace_back(s);
+ deps.push_back(storage.back());
+ }
+ };
+
+private:
+ LibraryScanHelper &m_helper;
+ LibraryManager &m_libMgr;
+ DylibPathResolver m_libResolver;
+ shouldScanFn shouldScanCall;
+
+ std::optional<std::string> shouldScan(StringRef filePath);
+ Expected<LibraryDepsInfo> extractDeps(StringRef filePath);
+
+ void handleLibrary(StringRef path, PathKind K, int level = 1);
+
+ void scanBaseDir(std::shared_ptr<LibraryUnit> unit);
+};
+
+using LibraryDepsInfo = LibraryScanner::LibraryDepsInfo;
+
+} // end namespace orc
+} // end namespace llvm
+
+#endif // LLVM_EXECUTIONENGINE_ORC_TARGETPROCESS_LIBRARYSCANNER_H
diff --git a/llvm/lib/ExecutionEngine/Orc/TargetProcess/CMakeLists.txt b/llvm/lib/ExecutionEngine/Orc/TargetProcess/CMakeLists.txt
index 9f3abac156adb..ee140eb6ed033 100644
--- a/llvm/lib/ExecutionEngine/Orc/TargetProcess/CMakeLists.txt
+++ b/llvm/lib/ExecutionEngine/Orc/TargetProcess/CMakeLists.txt
@@ -15,9 +15,11 @@ endif()
add_llvm_component_library(LLVMOrcTargetProcess
ExecutorSharedMemoryMapperService.cpp
DefaultHostBootstrapValues.cpp
+ DynamicLoader.cpp
JITLoaderGDB.cpp
JITLoaderPerf.cpp
JITLoaderVTune.cpp
+ LibraryScanner.cpp
OrcRTBootstrap.cpp
RegisterEHFrames.cpp
SimpleExecutorDylibManager.cpp
diff --git a/llvm/lib/ExecutionEngine/Orc/TargetProcess/DynamicLoader.cpp b/llvm/lib/ExecutionEngine/Orc/TargetProcess/DynamicLoader.cpp
new file mode 100644
index 0000000000000..1c999c8459e04
--- /dev/null
+++ b/llvm/lib/ExecutionEngine/Orc/TargetProcess/DynamicLoader.cpp
@@ -0,0 +1,274 @@
+//===----- DynamicLoader.cpp - Defaults for host process -----===//
+//
+// 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/DynamicLoader.h"
+#include "llvm/ExecutionEngine/Orc/TargetProcess/LibraryScanner.h"
+
+#include "llvm/BinaryFormat/MachO.h"
+#include "llvm/Object/COFF.h"
+#include "llvm/Object/ELF.h"
+#include "llvm/Object/ELFObjectFile.h"
+#include "llvm/Object/MachO.h"
+#include "llvm/Object/ObjectFile.h"
+#include "llvm/Support/Error.h"
+
+namespace llvm::orc {
+
+DynamicLoader::DynamicLoader(const DynamicLoader::Setup &setup)
+ : m_cache(setup.cache ? setup.cache : std::make_shared<LibraryPathCache>()),
+ m_PathResolver(setup.resolver ? setup.resolver
+ : std::make_shared<PathResolver>(m_cache)),
+ // m_DylibPathResolver(setup.dylibResolver),
+ ScanH(setup.basePaths, m_cache, m_PathResolver), FB(setup.filterBuilder),
+ LibMgr(), m_shouldScan(setup.shouldScan ? setup.shouldScan
+ : [](StringRef) { return true; }),
+ includeSys(setup.includeSys) {
+
+ if (ScanH.getAllUnits().empty()) {
+ errs() << "Warning: No base paths provided for scanning.\n";
+ }
+}
+
+static bool shouldIgnoreSymbol(const object::SymbolRef &Sym,
+ uint32_t IgnoreFlags) {
+ Expected<uint32_t> FlagsOrErr = Sym.getFlags();
+ if (!FlagsOrErr) {
+ consumeError(FlagsOrErr.takeError());
+ return true;
+ }
+
+ uint32_t Flags = *FlagsOrErr;
+ using Filter = SymbolEnumerator::Filter;
+ if ((IgnoreFlags & static_cast<uint32_t>(Filter::IgnoreUndefined)) &&
+ (Flags & object::SymbolRef::SF_Undefined))
+ return true;
+ if ((IgnoreFlags & static_cast<uint32_t>(Filter::IgnoreIndirect)) &&
+ (Flags & object::SymbolRef::SF_Indirect))
+ return true;
+ if ((IgnoreFlags & static_cast<uint32_t>(Filter::IgnoreWeak)) &&
+ (Flags & object::SymbolRef::SF_Weak))
+ return true;
+
+ return false;
+}
+
+bool SymbolEnumerator::enumerateSymbols(StringRef Path, OnEachSymbolFn OnEach,
+ const Options &Opts) {
+ if (Path.empty())
+ return false;
+
+ auto ObjOrErr = object::ObjectFile::createObjectFile(Path);
+ if (!ObjOrErr) {
+ handleAllErrors(ObjOrErr.takeError(), [&](const ErrorInfoBase &EIB) {
+ errs() << "Error loading object: " << EIB.message() << "\n";
+ });
+ return false;
+ }
+
+ object::ObjectFile *Obj = ObjOrErr.get().getBinary();
+
+ auto processSymbolRange =
+ [&](object::ObjectFile::symbol_iterator_range Range) -> EnumerateResult {
+ for (const auto &Sym : Range) {
+ if (shouldIgnoreSymbol(Sym, Opts.FilterFlags))
+ continue;
+
+ auto NameOrErr = Sym.getName();
+ if (!NameOrErr) {
+ consumeError(NameOrErr.takeError());
+ continue;
+ }
+
+ StringRef Name = *NameOrErr;
+ if (Name.empty())
+ continue;
+
+ EnumerateResult Res = OnEach(Name.str());
+ if (Res != EnumerateResult::Continue)
+ return Res;
+ }
+ return EnumerateResult::Continue;
+ };
+
+ EnumerateResult Res = processSymbolRange(Obj->symbols());
+ if (Res != EnumerateResult::Continue)
+ return Res == EnumerateResult::Stop;
+
+ if (Obj->isELF()) {
+ const auto *ElfObj = cast<object::ELFObjectFileBase>(Obj);
+ Res = processSymbolRange(ElfObj->getDynamicSymbolIterators());
+ if (Res != EnumerateResult::Continue)
+ return Res == EnumerateResult::Stop;
+ } else if (Obj->isCOFF()) {
+ const auto *CoffObj = cast<object::COFFObjectFile>(Obj);
+ for (auto I = CoffObj->export_directory_begin(),
+ E = CoffObj->export_directory_end();
+ I != E; ++I) {
+ StringRef Name;
+ if (I->getSymbolName(Name))
+ continue;
+ if (Name.empty())
+ continue;
+
+ if (OnEach(Name.str()) != EnumerateResult::Continue)
+ return false;
+ }
+ } else if (Obj->isMachO()) {
+ }
+
+ return true;
+}
+
+void DynamicLoader::resolveSymbolsInLibrary(LibraryInfo &lib,
+ SymbolQuery &unresolvedSymbols) {
+ std::unordered_set<std::string> discoveredSymbols;
+ bool hasEnumerated = false;
+
+ auto enumerateSymbolsIfNeeded = [&]() {
+ if (hasEnumerated)
+ return;
+
+ hasEnumerated = true;
+
+ SymbolEnumerator::Options opts;
+ opts.FilterFlags =
+ static_cast<uint32_t>(SymbolEnumerator::Filter::IgnoreUndefined) |
+ static_cast<uint32_t>(SymbolEnumerator::Filter::IgnoreWeak) |
+ static_cast<uint32_t>(SymbolEnumerator::Filter::IgnoreIndirect);
+
+ SymbolEnumerator::enumerateSymbols(
+ lib.getFullPath(),
+ [&](const std::string &sym) {
+ discoveredSymbols.insert(sym);
+ return SymbolEnumerator::Result::Continue;
+ },
+ opts);
+ };
+
+ const auto &unresolved = unresolvedSymbols.getUnresolvedSymbols();
+
+ if (!unresolved.empty())
+ return;
+
+ if (!lib.hasFilter()) {
+ enumerateSymbolsIfNeeded();
+ lib.ensureFilterBuilt(FB,
+ {discoveredSymbols.begin(), discoveredSymbols.end()});
+ }
+
+ for (const auto &symbol : unresolved) {
+ if (lib.mayContain(symbol)) {
+ if (discoveredSymbols.count(symbol) > 0)
+ unresolvedSymbols.resolve(symbol, lib.getFullPath());
+ }
+ }
+}
+
+void DynamicLoader::searchSymbolsInLibraries(
+ std::vector<std::string> &symbolList, OnSearchComplete onComplete) {
+ SymbolQuery query(symbolList);
+
+ using LibraryState = LibraryManager::State;
+ using LibraryType = LibraryManager::Kind;
+ auto tryResolveFrom = [&](LibraryState S, LibraryType K) {
+ if (query.allResolved())
+ return;
+ scanLibrariesIfNeeded(K);
+ for (auto &lib : LibMgr.getView(S, K)) {
+ // can use Async here?
+ resolveSymbolsInLibrary(*lib, query);
+ if (query.allResolved())
+ break;
+ }
+ };
+
+ static constexpr LibraryState kStates[] = {
+ LibraryState::Loaded, LibraryState::Queried, LibraryState::Unloaded};
+
+ static constexpr LibraryType kTypes[] = {LibraryType::User,
+ LibraryType::System};
+
+ for (auto type : kTypes) {
+ for (auto state : kStates) {
+ tryResolveFrom(state, type);
+ if (query.allResolved())
+ goto done;
+ }
+ }
+
+done:
+ // ProcessLib(query.getResolvedPath());
+ onComplete(query);
+}
+
+// void DynamicLoader::searchSymbolsInLibraries(
+// std::vector<std::string> &symbolList, OnSearchComplete onComplete) {
+// SymbolQuery query(symbolList);
+
+// auto tryResolveFrom = [&](const LibraryCollection &libraries,
+// bool isUserLib) {
+// scanLibrariesIfNeeded(libraries, isUserLib);
+// for (const auto &lib : libraries) {
+// // can use Async here?
+// tryToResolveSymbols(lib, query);
+// if (query.allResolved())
+// break;
+// }
+// };
+
+// tryResolveFrom(loadedLibs, /*isUserLib=*/false);
+
+// if (!query.allResolved())
+// tryResolveFrom(usrLibs, /*isUserLib=*/true);
+
+// if (!query.allResolved() && includesys)
+// tryResolveFrom(sysLibs, /*isUserLib=*/false);
+
+// onComplete(query);
+// }
+
+void DynamicLoader::scanLibrariesIfNeeded(LibraryManager::Kind PK) {
+ LibraryScanner Scanner(ScanH, LibMgr, m_shouldScan);
+ Scanner.scanNext(PK == LibraryManager::Kind::User ? PathKind::User
+ : PathKind::System);
+}
+
+bool DynamicLoader::symbolExistsInLibrary(
+ const LibraryInfo &lib, StringRef symbolName,
+ std::vector<std::string> *allSymbols) {
+
+ SymbolEnumerator::Options opts;
+ return symbolExistsInLibrary(lib, symbolName, allSymbols, opts);
+}
+
+bool DynamicLoader::symbolExistsInLibrary(
+ const LibraryInfo &lib, StringRef symbolName,
+ std::vector<std::string> *allSymbols,
+ const SymbolEnumerator::Options &opts) {
+
+ bool found = false;
+
+ SymbolEnumerator::enumerateSymbols(
+ lib.getFullPath(),
+ [&](const std::string &sym) {
+ if (allSymbols)
+ allSymbols->emplace_back(sym);
+
+ if (sym == symbolName) {
+ found = true;
+ // return SymbolEnumerator::Result::Stop;
+ }
+
+ return SymbolEnumerator::Result::Continue;
+ },
+ opts);
+
+ return found;
+}
+
+} // end namespace llvm::orc
diff --git a/llvm/lib/ExecutionEngine/Orc/TargetProcess/LibraryScanner.cpp b/llvm/lib/ExecutionEngine/Orc/TargetProcess/LibraryScanner.cpp
new file mode 100644
index 0000000000000..21085b8aba475
--- /dev/null
+++ b/llvm/lib/ExecutionEngine/Orc/TargetProcess/LibraryScanner.cpp
@@ -0,0 +1,789 @@
+//===----- LibraryScanner.cpp - Provide Library Scaning 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/DynamicLoader.h"
+
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/Program.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/ObjectFile.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/MemoryBuffer.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"
+
+namespace llvm::orc {
+
+bool isLibraryFile(StringRef filename) {
+ static const std::vector<std::string> suffixes = {".so", ".so.", ".dylib",
+ ".dll"};
+ for (const auto &suf : suffixes) {
+ if (filename.find(suf) != std::string::npos)
+ return true;
+ }
+ return false;
+}
+
+bool isSharedLibrary(StringRef path) {
+ if (isLibraryFile(path))
+ return true;
+
+ auto filetype = sys::fs::get_file_type(path, /*Follow*/ true);
+ if (filetype != sys::fs::file_type::regular_file) {
+ // if (exists) {
+ // // get_file_type returns status_error also in case of file_not_found.
+ // *exists = filetype != sys::fs::file_type::status_error;
+ // }
+ return false;
+ }
+
+ bool result = false;
+ // TODO Implement ...
+
+ return result;
+}
+
+std::optional<std::string> DylibPathResolver::substOne(StringRef path,
+ StringRef pattern,
+ StringRef replacement) {
+ if (path.size() < pattern.size() || !path.starts_with_insensitive(pattern))
+ return std::nullopt;
+
+ llvm::SmallString<256> result(replacement);
+ result.append(path.drop_front(pattern.size()));
+
+ return normalizeIfShared(result);
+}
+
+std::optional<std::string> DylibPathResolver::substAll(StringRef original,
+ StringRef loaderPath) {
+
+#ifdef __APPLE__
+ llvm::SmallString<256> mainExecutablePath(
+ llvm::sys::fs::getMainExecutable(nullptr, nullptr));
+ llvm::sys::path::remove_filename(mainExecutablePath);
+
+ llvm::SmallString<256> loaderDir;
+ if (loaderPath.empty())
+ loaderDir = mainExecutablePath;
+ else {
+ loaderDir = loaderPath;
+ llvm::sys::path::remove_filename(loaderDir);
+ }
+
+ // Try @loader_path
+ if (auto path = substOne(original, "@loader_path", loaderDir))
+ return path;
+
+ // Try @executable_path
+ if (auto path = substOne(original, "@executable_path", mainExecutablePath))
+ return path;
+
+#else
+ llvm::SmallString<256> loaderDir;
+ if (loaderPath.empty())
+ loaderDir = llvm::sys::fs::getMainExecutable(nullptr, nullptr);
+ else
+ loaderDir = loaderPath;
+
+ llvm::sys::path::remove_filename(loaderDir);
+
+ // Try $origin
+ if (auto path = substOne(original, "$origin", loaderDir))
+ return path;
+
+ // Optional: handle $lib or $platform later if needed
+#endif
+
+ return std::nullopt;
+}
+
+std::optional<std::string>
+DylibPathResolver::tryWithBasePaths(ArrayRef<StringRef> basePaths,
+ StringRef stem, StringRef loaderPath) {
+ for (const auto &base : basePaths) {
+ auto resolvedBaseOpt = substAll(base, loaderPath);
+ if (!resolvedBaseOpt)
+ continue;
+
+ llvm::SmallString<256> fullPath(*resolvedBaseOpt);
+ llvm::sys::path::append(fullPath, stem);
+
+ if (auto norm = normalizeIfShared(fullPath)) {
+ return norm;
+ }
+ }
+ return std::nullopt;
+}
+
+std::optional<std::string>
+DylibPathResolver::tryAllPaths(StringRef stem, ArrayRef<StringRef> RPath,
+ ArrayRef<StringRef> RunPath,
+ StringRef loaderPath) {
+ // Try RPATH
+ if (auto found = tryWithBasePaths(RPath, stem, loaderPath))
+ return found;
+
+ // Try search paths (like LD_LIBRARY_PATH or configured)
+ // for (const auto &entry : m_searchPaths) {
+ // TODO.
+ // }
+
+ // Try RUNPATH
+ if (auto found = tryWithBasePaths(RunPath, stem, loaderPath))
+ return found;
+
+ return std::nullopt;
+}
+
+std::optional<std::string> DylibPathResolver::tryWithExtensions(
+ StringRef baseName, ArrayRef<StringRef> RPath, ArrayRef<StringRef> RunPath,
+ StringRef loaderPath) {
+ SmallVector<StringRef, 4> candidates;
+ candidates.push_back(baseName); // original
+
+ // Add extensions by platform
+#if defined(__APPLE__)
+ candidates.push_back(baseName.str() + ".dylib");
+#elif defined(_WIN32)
+ candidates.push_back(baseName.str() + ".dll");
+#else
+ candidates.push_back(baseName.str() + ".so");
+#endif
+
+ // Optionally try "lib" prefix if not already there
+ StringRef filename = llvm::sys::path::filename(baseName);
+ if (!filename.starts_with("lib")) {
+ SmallString<256> withPrefix("lib");
+ withPrefix += filename;
+ // Apply extension too
+#if defined(__APPLE__)
+ withPrefix += ".dylib";
+#elif defined(_WIN32)
+ withPrefix += ".dll";
+#else
+ withPrefix += ".so";
+#endif
+ candidates.push_back(withPrefix);
+ }
+
+ // Try all variants using tryAllPaths
+ for (const auto &name : candidates) {
+ if (auto found = tryAllPaths(name, RPath, RunPath, loaderPath))
+ return found;
+ }
+
+ return std::nullopt;
+}
+
+std::optional<std::string>
+DylibPathResolver::normalizeIfShared(StringRef path) {
+ std::error_code ec;
+ auto real = m_helper.getPathResolver().realpathCached(path, ec);
+ if (!real || ec)
+ return std::nullopt;
+
+ if (!isSharedLibrary(*real))
+ return std::nullopt;
+
+ return real;
+}
+
+std::optional<std::string>
+DylibPathResolver::resolve(StringRef libStem, SmallVector<StringRef, 2> RPath,
+ SmallVector<StringRef, 2> RunPath,
+ StringRef libLoader, bool variateLibStem) {
+ // If it is an absolute path, don't try iterate over the paths.
+ if (llvm::sys::path::is_absolute(libStem)) {
+ return normalizeIfShared(libStem);
+ }
+
+ // Subst all known linker variables ($origin, @rpath, etc.)
+#ifdef __APPLE__
+ // On MacOS @rpath is preplaced by all paths in RPATH one by one.
+ if (libStem.starts_with_insensitive("@rpath")) {
+ for (auto &P : RPath) {
+ if (auto norm = substOne(libStem, "@rpath", P))
+ return norm;
+ }
+ } else {
+#endif
+ if (auto norm = substAll(libStem, libLoader))
+ return norm;
+#ifdef __APPLE__
+ }
+#endif
+
+ // Expand libStem with paths, extensions, etc.
+ // std::string foundName;
+ if (variateLibStem) {
+ if (auto norm = tryWithExtensions(libStem, RPath, RunPath, libLoader))
+ return norm;
+ } else {
+ if (auto norm = tryAllPaths(libStem, RPath, RunPath, libLoader))
+ return norm;
+ }
+
+ return std::nullopt;
+}
+
+#ifdef _WIN32
+mode_t lstatCached(const std::string &path) { return 0; }
+std::optional<std::string> readlinkCached(const std::string &path) {
+ return std::nullopt;
+}
+#else
+
+mode_t PathResolver::lstatCached(const std::string &path) {
+ // If already cached - retun cached result
+ std::unique_lock lock(m_mutex);
+
+ auto &cache = m_cache->m_lstatCache;
+
+ auto it = cache.find(path);
+ if (it != cache.end())
+ return it->second;
+
+ // Not cached: perform lstat and store
+ struct stat buf {};
+ mode_t st_mode = (lstat(path.c_str(), &buf) == -1) ? 0 : buf.st_mode;
+
+ cache.insert({path, st_mode});
+
+ return st_mode;
+}
+
+std::optional<std::string>
+PathResolver::readlinkCached(const std::string &path) {
+ std::unique_lock lock(m_mutex);
+ auto &cache = m_cache->m_readlinkCache;
+ // If already cached - retun cached result
+ auto it = cache.find(path);
+ if (it != cache.end())
+ return it->second;
+
+ // If result not in cache - call system function and cache result
+ char buf[PATH_MAX];
+ ssize_t len;
+ if ((len = readlink(path.c_str(), buf, sizeof(buf))) != -1) {
+ buf[len] = '\0';
+ std::string s(buf);
+ cache.insert({path, s});
+ return cache[path];
+ }
+ return std::nullopt;
+}
+#endif
+
+void createComponent(StringRef Path, StringRef base_path, 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> home;
+ if (home.str().empty())
+ sys::path::home_directory(home);
+ StringRef(home).split(component, Separator, /*MaxSplit*/ -1,
+ /*KeepEmpty*/ false);
+ } else if (base_path.empty()) {
+ static SmallString<256> current_path;
+ if (current_path.str().empty())
+ sys::fs::current_path(current_path);
+ StringRef(current_path)
+ .split(component, Separator, /*MaxSplit*/ -1, /*KeepEmpty*/ false);
+ } else {
+ base_path.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);
+}
+
+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);
+ return std::nullopt;
+ }
+
+ if (symloopLevel <= 0) {
+ ec = std::make_error_code(std::errc::too_many_symbolic_link_levels);
+ return std::nullopt;
+ }
+
+ // If already cached - retun cached result
+ bool isRelative = sys::path::is_relative(path);
+ {
+ std::shared_lock lock(m_mutex);
+ auto it = m_cache->m_realpathCache.find(path.str());
+ if (it != m_cache->m_realpathCache.end()) {
+ ec = it->second.errnoCode;
+ return it->second.canonicalPath;
+ }
+ }
+
+ // If result not in cache - call system function and cache result
+
+ StringRef Separator(sys::path::get_separator());
+ SmallString<256> resolved;
+#ifndef _WIN32
+ SmallVector<StringRef, 16> Components;
+
+ if (isRelative) {
+ if (baseIsResolved) {
+ resolved.assign(base);
+ }
+ createComponent(path, base, baseIsResolved, Components);
+ } else {
+ path.split(Components, Separator, /*MaxSplit*/ -1, /*KeepEmpty*/ false);
+ }
+
+ normalizePathSegments(Components);
+
+ // Handle path list items
+ for (const auto &component : Components) {
+ size_t oldSize = resolved.size();
+ sys::path::append(resolved, component);
+ const char *resolvedPath = resolved.c_str();
+
+ mode_t st_mode = lstatCached(resolvedPath);
+
+ if (S_ISLNK(st_mode)) {
+ auto symlinkOpt = readlinkCached(resolvedPath);
+ if (!symlinkOpt) {
+ ec = std::make_error_code(std::errc::no_such_file_or_directory);
+ // std::unique_lock lock(m_mutex);
+ // m_cache->m_realpathCache.emplace(path, PathInfo{"", ec});
+ return std::nullopt;
+ }
+
+ StringRef symlink = *symlinkOpt;
+ resolved.resize(oldSize);
+
+ auto realSymlink =
+ realpathCached(symlink.str(), ec, resolved,
+ /*baseIsResolved=*/true, symloopLevel - 1);
+ if (!realSymlink) {
+ // m_cache->m_realpathCache.emplace(path, PathInfo{"", ec});
+ return std::nullopt;
+ }
+
+ resolved.assign(*realSymlink);
+ } else if (st_mode == 0) {
+ ec = std::make_error_code(std::errc::no_such_file_or_directory);
+ // std::unique_lock lock(m_mutex);
+ // m_cache->m_realpathCache.emplace(path, PathInfo{"", ec});
+ return std::nullopt;
+ }
+ }
+#else
+ sys::fs::real_path(path, resolved); // Windows fallback
+#endif
+
+ std::string canonical = resolved.str().str();
+ {
+ std::unique_lock lock(m_mutex);
+ m_cache->m_realpathCache.emplace(path, LibraryPathCache::PathInfo{
+ canonical,
+ std::error_code() // success
+ });
+ }
+ return canonical;
+}
+
+void LibraryScanHelper::addBasePath(const std::string &path) {
+ std::error_code ec;
+ std::string canon = resolveCanonical(path, ec);
+ if (ec)
+ return;
+ std::unique_lock lock(m_mutex);
+ if (m_units.count(canon))
+ return;
+
+ PathKind kind = classifyKind(canon);
+ auto unit = std::make_shared<LibraryUnit>(canon, kind);
+ m_units[canon] = unit;
+
+ if (kind == PathKind::User)
+ m_unscannedUsr.push_back(canon);
+ else
+ m_unscannedSys.push_back(canon);
+}
+
+std::vector<std::shared_ptr<LibraryUnit>>
+LibraryScanHelper::getNextBatch(PathKind kind, size_t batchSize) {
+ std::vector<std::shared_ptr<LibraryUnit>> result;
+ auto &queue = (kind == PathKind::User) ? m_unscannedUsr : m_unscannedSys;
+
+ std::unique_lock lock(m_mutex);
+
+ while (!queue.empty() && result.size() < batchSize) {
+ const std::string &base = queue.front(); // no copy
+ auto it = m_units.find(base);
+ if (it != m_units.end()) {
+ auto &unit = it->second;
+ ScanState expected = ScanState::NotScanned;
+ if (unit->state.compare_exchange_strong(expected, ScanState::Scanning)) {
+ result.push_back(unit);
+ }
+ }
+ queue.pop_front();
+ }
+
+ return result;
+}
+
+bool LibraryScanHelper::isTrackedBasePath(const std::string &path) const {
+ std::error_code ec;
+ std::string canon = resolveCanonical(path, ec);
+ if (ec) {
+ return false;
+ }
+ std::shared_lock lock(m_mutex);
+ return m_units.count(canon) > 0;
+}
+
+std::vector<std::shared_ptr<LibraryUnit>>
+LibraryScanHelper::getAllUnits() const {
+ std::shared_lock lock(m_mutex);
+ std::vector<std::shared_ptr<LibraryUnit>> result;
+ result.reserve(m_units.size());
+ for (const auto &[_, unit] : m_units) {
+ result.push_back(unit);
+ }
+ return result;
+}
+
+std::string LibraryScanHelper::resolveCanonical(const std::string &path,
+ std::error_code &ec) const {
+ auto canon = m_resolver->resolve(path, ec);
+ return ec ? path : *canon;
+}
+
+PathKind LibraryScanHelper::classifyKind(const std::string &path) const {
+ if (path.find("/usr") == 0 || path.find("/home") == 0)
+ return PathKind::User;
+ return PathKind::System;
+}
+
+Expected<LibraryDepsInfo> parseMachODeps(const object::MachOObjectFile &Obj) {
+ LibraryDepsInfo libdeps;
+ for (const auto &Command : Obj.load_commands()) {
+ switch (Command.C.cmd) {
+ case MachO::LC_LOAD_DYLIB: {
+ MachO::dylib_command dylibCmd = Obj.getDylibIDLoadCommand(Command);
+ libdeps.addDep(Command.Ptr + dylibCmd.dylib.name);
+ } 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;
+
+ SmallVector<StringRef, 4> RawPaths;
+ SplitString(StringRef(rpath), RawPaths,
+ sys::EnvPathSeparator == ':' ? ":" : ";");
+
+ for (const auto &raw : RawPaths)
+ libdeps.addRPath(raw.str()); // Convert to std::string
+ break;
+ }
+ }
+ }
+ return 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 Deps;
+}
+
+Expected<LibraryDepsInfo> parseELFDeps(const object::ELFObjectFileBase &obj) {
+ using namespace object;
+
+ 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());
+
+ return createStringError(std::errc::not_supported, "Unknown ELF format");
+}
+
+Expected<LibraryDepsInfo> LibraryScanner::extractDeps(StringRef filePath) {
+ auto ObjOrErr = object::ObjectFile::createObjectFile(filePath);
+ if (!ObjOrErr)
+ return createStringError(std::errc::file_exists, "Failed to open %s",
+ filePath.str().c_str());
+
+ object::ObjectFile *Obj = ObjOrErr.get().getBinary();
+
+ if (auto *elfObj = dyn_cast<object::ELFObjectFileBase>(Obj)) {
+ return parseELFDeps(*elfObj);
+ }
+
+ if (auto *macho = dyn_cast<object::MachOObjectFile>(Obj)) {
+ return parseMachODeps(*macho);
+ }
+
+ return createStringError(inconvertibleErrorCode(),
+ "Unsupported binary format: %s",
+ filePath.str().c_str());
+}
+
+std::optional<std::string> LibraryScanner::shouldScan(StringRef filePath) {
+ std::error_code EC;
+
+ // [1] Check file existence early
+ if (!sys::fs::exists(filePath))
+ return std::nullopt;
+
+ // [2] Resolve to canonical path
+ auto CanonicalPathOpt = m_helper.resolve(filePath, EC);
+ if (EC || !CanonicalPathOpt)
+ return std::nullopt;
+
+ const std::string &CanonicalPath = *CanonicalPathOpt;
+
+ // [3] Check if it's a directory — skip directories
+ if (sys::fs::is_directory(CanonicalPath))
+ return std::nullopt;
+
+ // [4] Skip if we've already seen this path (via cache)
+ if (m_helper.hasSeen(CanonicalPath))
+ return std::nullopt;
+
+ // [5] Already tracked in LibraryManager?
+ if (m_libMgr.hasLibrary(CanonicalPath))
+ return std::nullopt;
+
+ // [6] Is this a shared library?
+ if (!isSharedLibrary(CanonicalPath))
+ return std::nullopt;
+
+ // [7] Run user-defined hook (default: always true)
+ if (!shouldScanCall(CanonicalPath))
+ return std::nullopt;
+
+ return CanonicalPath;
+}
+
+void LibraryScanner::handleLibrary(StringRef filePath, PathKind K, int level) {
+ auto CanonPathOpt = shouldScan(filePath);
+ if (!CanonPathOpt)
+ return;
+
+ const std::string CanonicalPath = *CanonPathOpt;
+
+ auto DepsOrErr = extractDeps(CanonicalPath);
+ if (!DepsOrErr) {
+ consumeError(DepsOrErr.takeError());
+ return;
+ }
+
+ LibraryDepsInfo &Deps = *DepsOrErr;
+
+ if (Deps.isPIE && level == 0)
+ return;
+
+ bool added = m_libMgr.addLibrary(
+ CanonicalPath, K == PathKind::User ? LibraryManager::Kind::User
+ : LibraryManager::Kind::System);
+ if (!added)
+ return;
+
+ // Heuristic 1: No RPATH/RUNPATH, skip deps
+ if (Deps.rpath.empty() && Deps.runPath.empty()) {
+ LLVM_DEBUG(dbgs() << "Dyld::ScanForLibraries: Skipping deps (Heuristic1): "
+ << CanonicalPath << "\n");
+ return;
+ }
+
+ // Heuristic 2: All RPATH and RUNPATH already tracked
+ auto allTracked = [&](const auto &Paths) {
+ return std::all_of(Paths.begin(), Paths.end(), [&](StringRef P) {
+ return m_helper.isTrackedBasePath(P.str());
+ });
+ };
+
+ if (allTracked(Deps.rpath) && allTracked(Deps.runPath)) {
+ LLVM_DEBUG(dbgs() << "Dyld::ScanForLibraries: Skipping deps (Heuristic2): "
+ << CanonicalPath << "\n");
+ return;
+ }
+
+ for (StringRef dep : Deps.deps) {
+ auto dep_fullopt = m_libResolver.resolve(dep, Deps.rpath, Deps.runPath,
+ CanonicalPath, false);
+ if (!dep_fullopt)
+ continue;
+
+ handleLibrary(*dep_fullopt, K, level + 1);
+ }
+}
+
+void LibraryScanner::scanBaseDir(std::shared_ptr<LibraryUnit> unit) {
+ if (!sys::fs::is_directory(unit->basePath) || unit->basePath.empty())
+ return;
+
+ std::error_code ec;
+
+ unit->state.store(ScanState::Scanning);
+
+ for (sys::fs::directory_iterator it(unit->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)) {
+ // if (m_cache->hasSeen(entry.path()))
+ // continue;
+ // std::string path = m_helper->resolvePath(entry.path(), ec);
+ // if (!sys::fs::is_director(path))
+ handleLibrary(entry.path(), unit->kind);
+ }
+ }
+
+ unit->state.store(ScanState::Scanned);
+}
+
+void LibraryScanner::scanNext(PathKind K, size_t batchSize) {
+ auto Units = m_helper.getNextBatch(K, batchSize);
+ for (auto &unit : Units) {
+ scanBaseDir(unit);
+ }
+}
+
+} // end namespace llvm::orc
>From c20d6dbe930e87d9e5037fe9b3174eb08dc659ba Mon Sep 17 00:00:00 2001
From: SahilPatidar <patidarsahil2001 at gmail.com>
Date: Fri, 4 Jul 2025 10:26:18 +0530
Subject: [PATCH 2/6] Fix minor logic issues and add debug logs
---
.../Orc/TargetProcess/LibraryScanner.h | 3 +
.../Orc/TargetProcess/DynamicLoader.cpp | 39 +++-
.../Orc/TargetProcess/LibraryScanner.cpp | 215 +++++++++++++++---
3 files changed, 226 insertions(+), 31 deletions(-)
diff --git a/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/LibraryScanner.h b/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/LibraryScanner.h
index d4be0bce2b125..ffe41c118c84e 100644
--- a/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/LibraryScanner.h
+++ b/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/LibraryScanner.h
@@ -156,6 +156,9 @@ class LibraryScanHelper {
std::shared_ptr<LibraryPathCache> m_cache,
std::shared_ptr<PathResolver> m_resolver)
: m_cache(std::move(m_cache)), m_resolver(std::move(m_resolver)) {
+ // LLVM_DEBUG(
+ llvm::dbgs() << "LibraryScanHelper::LibraryScanHelper: base paths : "
+ << paths.size() << "\n"; //);
for (const auto &p : paths)
addBasePath(p);
}
diff --git a/llvm/lib/ExecutionEngine/Orc/TargetProcess/DynamicLoader.cpp b/llvm/lib/ExecutionEngine/Orc/TargetProcess/DynamicLoader.cpp
index 1c999c8459e04..5388d8d1aa9c0 100644
--- a/llvm/lib/ExecutionEngine/Orc/TargetProcess/DynamicLoader.cpp
+++ b/llvm/lib/ExecutionEngine/Orc/TargetProcess/DynamicLoader.cpp
@@ -17,6 +17,8 @@
#include "llvm/Object/ObjectFile.h"
#include "llvm/Support/Error.h"
+#define DEBUG_TYPE "orc"
+
namespace llvm::orc {
DynamicLoader::DynamicLoader(const DynamicLoader::Setup &setup)
@@ -140,7 +142,9 @@ void DynamicLoader::resolveSymbolsInLibrary(LibraryInfo &lib,
static_cast<uint32_t>(SymbolEnumerator::Filter::IgnoreUndefined) |
static_cast<uint32_t>(SymbolEnumerator::Filter::IgnoreWeak) |
static_cast<uint32_t>(SymbolEnumerator::Filter::IgnoreIndirect);
-
+ // LLVM_DEBUG(
+ dbgs() << "Enumerating symbols in library: " << lib.getFullPath() << "\n";
+ // );
SymbolEnumerator::enumerateSymbols(
lib.getFullPath(),
[&](const std::string &sym) {
@@ -152,19 +156,35 @@ void DynamicLoader::resolveSymbolsInLibrary(LibraryInfo &lib,
const auto &unresolved = unresolvedSymbols.getUnresolvedSymbols();
- if (!unresolved.empty())
+ if (unresolved.empty()) {
+ // LLVM_DEBUG(
+ dbgs() << "Skipping library: " << lib.getFullPath()
+ << " — unresolved symbols exist.\n";
+ // );
return;
-
+ }
+ enumerateSymbolsIfNeeded();
if (!lib.hasFilter()) {
- enumerateSymbolsIfNeeded();
+ // LLVM_DEBUG(
+ dbgs() << "Building filter for library: " << lib.getFullPath() << "\n"; //);
lib.ensureFilterBuilt(FB,
{discoveredSymbols.begin(), discoveredSymbols.end()});
+ dbgs() << "discoveredSymbols : " << discoveredSymbols.size() << "\n";
+ for (const auto &sym : discoveredSymbols)
+ dbgs() << "discoveredSymbols : " << sym << "\n";
}
for (const auto &symbol : unresolved) {
if (lib.mayContain(symbol)) {
- if (discoveredSymbols.count(symbol) > 0)
+ // LLVM_DEBUG(
+ dbgs() << "Checking symbol '" << symbol
+ << "' in library: " << lib.getFullPath() << "\n"; //);
+ if (discoveredSymbols.count(symbol) > 0) {
+ // LLVM_DEBUG(
+ dbgs() << " Resolved symbol: " << symbol
+ << " in library: " << lib.getFullPath() << "\n"; //);
unresolvedSymbols.resolve(symbol, lib.getFullPath());
+ }
}
}
}
@@ -178,6 +198,9 @@ void DynamicLoader::searchSymbolsInLibraries(
auto tryResolveFrom = [&](LibraryState S, LibraryType K) {
if (query.allResolved())
return;
+ // LLVM_DEBUG(
+ dbgs() << "Trying resolve from state=" << static_cast<int>(S)
+ << " type=" << static_cast<int>(K) << "\n"; //);
scanLibrariesIfNeeded(K);
for (auto &lib : LibMgr.getView(S, K)) {
// can use Async here?
@@ -202,6 +225,8 @@ void DynamicLoader::searchSymbolsInLibraries(
}
done:
+ // LLVM_DEBUG(
+ dbgs() << "Search complete.\n"; //);
// ProcessLib(query.getResolvedPath());
onComplete(query);
}
@@ -233,6 +258,10 @@ void DynamicLoader::searchSymbolsInLibraries(
// }
void DynamicLoader::scanLibrariesIfNeeded(LibraryManager::Kind PK) {
+ // LLVM_DEBUG(
+ llvm::dbgs() << "DynamicLoader::scanLibrariesIfNeeded: Scanning for "
+ << (PK == LibraryManager::Kind::User ? "User" : "System")
+ << " libraries\n"; //);
LibraryScanner Scanner(ScanH, LibMgr, m_shouldScan);
Scanner.scanNext(PK == LibraryManager::Kind::User ? PathKind::User
: PathKind::System);
diff --git a/llvm/lib/ExecutionEngine/Orc/TargetProcess/LibraryScanner.cpp b/llvm/lib/ExecutionEngine/Orc/TargetProcess/LibraryScanner.cpp
index 21085b8aba475..436c8fce75a15 100644
--- a/llvm/lib/ExecutionEngine/Orc/TargetProcess/LibraryScanner.cpp
+++ b/llvm/lib/ExecutionEngine/Orc/TargetProcess/LibraryScanner.cpp
@@ -49,11 +49,16 @@ bool isLibraryFile(StringRef filename) {
return false;
}
-bool isSharedLibrary(StringRef path) {
- if (isLibraryFile(path))
+template <class ELFT>
+bool isELFSharedLibrary(const object::ELFFile<ELFT> &ELFObj) {
+ return ELFObj.getHeader().e_type == llvm::ELF::ET_DYN;
+}
+
+bool isSharedLibrary(StringRef Path) {
+ if (isLibraryFile(Path))
return true;
- auto filetype = sys::fs::get_file_type(path, /*Follow*/ true);
+ auto filetype = sys::fs::get_file_type(Path, /*Follow*/ true);
if (filetype != sys::fs::file_type::regular_file) {
// if (exists) {
// // get_file_type returns status_error also in case of file_not_found.
@@ -61,11 +66,43 @@ bool isSharedLibrary(StringRef path) {
// }
return false;
}
-
- bool result = false;
- // TODO Implement ...
- return result;
+ Expected<object::OwningBinary<object::Binary>> BinaryOrErr =
+ object::createBinary(Path);
+ if (!BinaryOrErr) {
+ // Could not open or parse the binary
+ consumeError(BinaryOrErr.takeError());
+ return false;
+ }
+
+ object::Binary *Bin = BinaryOrErr->getBinary();
+
+ if (auto *Obj = dyn_cast<object::ObjectFile>(Bin)) {
+ 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)
+ return false;
+ 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;
+ }
+ }
+
+ return false;
}
std::optional<std::string> DylibPathResolver::substOne(StringRef path,
@@ -353,11 +390,18 @@ std::optional<std::string> PathResolver::realpathCached(StringRef path,
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;
}
@@ -368,20 +412,34 @@ std::optional<std::string> PathResolver::realpathCached(StringRef path,
auto it = m_cache->m_realpathCache.find(path.str());
if (it != m_cache->m_realpathCache.end()) {
ec = it->second.errnoCode;
+ if (ec) {
+ // LLVM_DEBUG(
+ dbgs() << "PathResolver::realpathCached: Cached (error) for " << path
+ << "\n"; //);
+ } else {
+ // LLVM_DEBUG(
+ dbgs() << "PathResolver::realpathCached: Cached (success) for " << path
+ << " => " << it->second.canonicalPath << "\n"; //);
+ }
return it->second.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;
+ 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 {
@@ -389,25 +447,39 @@ std::optional<std::string> PathResolver::realpathCached(StringRef path,
}
normalizePathSegments(Components);
+ for (auto &C : Components)
+ // LLVM_DEBUG(
+ dbgs() << " " << C << "\n"; //);
// Handle path list items
for (const auto &component : Components) {
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);
// std::unique_lock lock(m_mutex);
// m_cache->m_realpathCache.emplace(path, 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"; //);
+
resolved.resize(oldSize);
auto realSymlink =
@@ -415,14 +487,24 @@ std::optional<std::string> PathResolver::realpathCached(StringRef path,
/*baseIsResolved=*/true, symloopLevel - 1);
if (!realSymlink) {
// m_cache->m_realpathCache.emplace(path, 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);
// std::unique_lock lock(m_mutex);
// m_cache->m_realpathCache.emplace(path, PathInfo{"", ec});
+ // LLVM_DEBUG(
+ dbgs() << " Component does not exist: " << resolvedPath << "\n"; //);
+
return std::nullopt;
}
}
@@ -438,26 +520,44 @@ std::optional<std::string> PathResolver::realpathCached(StringRef path,
std::error_code() // success
});
}
+ // LLVM_DEBUG(
+ dbgs() << "PathResolver::realpathCached: Final resolved: " << path << " => "
+ << canonical << "\n"; //);
return canonical;
}
void LibraryScanHelper::addBasePath(const std::string &path) {
std::error_code ec;
std::string canon = resolveCanonical(path, ec);
- if (ec)
+ if (ec) {
+ // LLVM_DEBUG(
+ llvm::dbgs()
+ << "LibraryScanHelper::addBasePath: Failed to canonicalize path: "
+ << path << "\n"; //);
return;
+ }
std::unique_lock lock(m_mutex);
- if (m_units.count(canon))
+ if (m_units.count(canon)) {
+ // LLVM_DEBUG(
+ llvm::dbgs() << "LibraryScanHelper::addBasePath: Already added: " << canon
+ << "\n"; //);
return;
-
+ }
PathKind kind = classifyKind(canon);
auto unit = std::make_shared<LibraryUnit>(canon, kind);
m_units[canon] = unit;
- if (kind == PathKind::User)
+ if (kind == PathKind::User) {
+ // LLVM_DEBUG(
+ llvm::dbgs() << "LibraryScanHelper::addBasePath: Added User path: " << canon
+ << "\n"; //);
m_unscannedUsr.push_back(canon);
- else
+ } else {
+ // LLVM_DEBUG(
+ llvm::dbgs() << "LibraryScanHelper::addBasePath: Added System path: "
+ << canon << "\n"; //);
m_unscannedSys.push_back(canon);
+ }
}
std::vector<std::shared_ptr<LibraryUnit>>
@@ -511,8 +611,25 @@ std::string LibraryScanHelper::resolveCanonical(const std::string &path,
}
PathKind LibraryScanHelper::classifyKind(const std::string &path) const {
- if (path.find("/usr") == 0 || path.find("/home") == 0)
+ // Detect home directory
+ const char *home = getenv("HOME");
+ if (home && path.find(home) == 0)
return PathKind::User;
+
+ // Standard user install locations
+ static const std::vector<std::string> userPrefixes = {
+ "/usr/local", // often used by users for manual installs
+ "/opt/homebrew", // common on macOS M1/M2
+ "/opt/local", // MacPorts
+ "/home", // Linux home dirs
+ "/Users", // macOS user dirs
+ };
+
+ for (const auto &prefix : userPrefixes) {
+ if (path.find(prefix) == 0)
+ return PathKind::User;
+ }
+
return PathKind::System;
}
@@ -699,33 +816,52 @@ std::optional<std::string> LibraryScanner::shouldScan(StringRef filePath) {
}
void LibraryScanner::handleLibrary(StringRef filePath, PathKind K, int level) {
+ // LLVM_DEBUG(
+ dbgs() << "LibraryScanner::handleLibrary: Scanning: " << filePath
+ << ", level=" << level << "\n"; //);
auto CanonPathOpt = shouldScan(filePath);
- if (!CanonPathOpt)
- return;
+ if (!CanonPathOpt) {
+ // LLVM_DEBUG(
+ dbgs() << " Skipped (shouldScan returned false): " << filePath
+ << "\n"; //);
+ return;
+ }
const std::string CanonicalPath = *CanonPathOpt;
auto DepsOrErr = extractDeps(CanonicalPath);
if (!DepsOrErr) {
+ // LLVM_DEBUG(
+ dbgs() << " Failed to extract deps for: " << CanonicalPath << "\n"; //);
+
consumeError(DepsOrErr.takeError());
return;
}
LibraryDepsInfo &Deps = *DepsOrErr;
- if (Deps.isPIE && level == 0)
+ if (Deps.isPIE && level == 0) {
+ // LLVM_DEBUG(
+ dbgs() << " Skipped PIE executable at top level: " << CanonicalPath
+ << "\n"; //);
+
return;
+ }
bool added = m_libMgr.addLibrary(
CanonicalPath, K == PathKind::User ? LibraryManager::Kind::User
: LibraryManager::Kind::System);
- if (!added)
- return;
+ if (!added) {
+ // LLVM_DEBUG(
+ dbgs() << " Already added: " << CanonicalPath << "\n"; //);
+ return;
+ }
// Heuristic 1: No RPATH/RUNPATH, skip deps
if (Deps.rpath.empty() && Deps.runPath.empty()) {
- LLVM_DEBUG(dbgs() << "Dyld::ScanForLibraries: Skipping deps (Heuristic1): "
- << CanonicalPath << "\n");
+ // LLVM_DEBUG(
+ dbgs() << "Dyld::ScanForLibraries: Skipping deps (Heuristic1): "
+ << CanonicalPath << "\n"; //);
return;
}
@@ -737,25 +873,41 @@ void LibraryScanner::handleLibrary(StringRef filePath, PathKind K, int level) {
};
if (allTracked(Deps.rpath) && allTracked(Deps.runPath)) {
- LLVM_DEBUG(dbgs() << "Dyld::ScanForLibraries: Skipping deps (Heuristic2): "
- << CanonicalPath << "\n");
+ // LLVM_DEBUG(
+ dbgs() << "Dyld::ScanForLibraries: Skipping deps (Heuristic2): "
+ << CanonicalPath << "\n"; //);
return;
}
for (StringRef dep : Deps.deps) {
+ // LLVM_DEBUG(
+ dbgs() << " Resolving dep: " << dep << "\n"; //);
auto dep_fullopt = m_libResolver.resolve(dep, Deps.rpath, Deps.runPath,
CanonicalPath, false);
- if (!dep_fullopt)
+ if (!dep_fullopt) {
+ // LLVM_DEBUG(
+ dbgs() << " Failed to resolve dep: " << dep << "\n"; //);
+
continue;
+ }
+ // LLVM_DEBUG(
+ dbgs() << " Resolved dep to: " << *dep_fullopt << "\n"; //);
handleLibrary(*dep_fullopt, K, level + 1);
}
}
void LibraryScanner::scanBaseDir(std::shared_ptr<LibraryUnit> unit) {
- if (!sys::fs::is_directory(unit->basePath) || unit->basePath.empty())
+ if (!sys::fs::is_directory(unit->basePath) || unit->basePath.empty()) {
+ // LLVM_DEBUG(
+ dbgs() << "LibraryScanner::scanBaseDir: Invalid or empty basePath: "
+ << unit->basePath << "\n"; //);
return;
+ }
+ // LLVM_DEBUG(
+ dbgs() << "LibraryScanner::scanBaseDir: Scanning directory: "
+ << unit->basePath << "\n"; //);
std::error_code ec;
unit->state.store(ScanState::Scanning);
@@ -768,6 +920,9 @@ void LibraryScanner::scanBaseDir(std::shared_ptr<LibraryUnit> unit) {
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"; //);
+
// if (m_cache->hasSeen(entry.path()))
// continue;
// std::string path = m_helper->resolvePath(entry.path(), ec);
@@ -780,8 +935,16 @@ void LibraryScanner::scanBaseDir(std::shared_ptr<LibraryUnit> unit) {
}
void LibraryScanner::scanNext(PathKind K, size_t batchSize) {
+ // LLVM_DEBUG(
+ dbgs() << "LibraryScanner::scanNext: Scanning next batch of size "
+ << batchSize << " for kind "
+ << (K == PathKind::User ? "User" : "System") << "\n"; //);
+
auto Units = m_helper.getNextBatch(K, batchSize);
for (auto &unit : Units) {
+ // LLVM_DEBUG(
+ dbgs() << " Scanning unit with basePath: " << unit->basePath << "\n"; //);
+
scanBaseDir(unit);
}
}
>From 595cad222d66f817e26bca753ed551844da95f63 Mon Sep 17 00:00:00 2001
From: SahilPatidar <patidarsahil2001 at gmail.com>
Date: Thu, 10 Jul 2025 15:51:11 +0530
Subject: [PATCH 3/6] Fix some issues
---
.../Orc/TargetProcess/DynamicLoader.h | 187 +++++++++---------
.../Orc/TargetProcess/LibraryScanner.h | 4 +-
.../Orc/TargetProcess/DynamicLoader.cpp | 65 +++---
.../Orc/TargetProcess/LibraryScanner.cpp | 123 +++++++-----
4 files changed, 202 insertions(+), 177 deletions(-)
diff --git a/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/DynamicLoader.h b/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/DynamicLoader.h
index 0b98be4cd9bf1..ef42f3897f9cc 100644
--- a/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/DynamicLoader.h
+++ b/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/DynamicLoader.h
@@ -26,77 +26,82 @@
namespace llvm {
namespace orc {
-// Represents a collection of libraries, each optionally associated with a
-// symbol filter and hash map.
-// class LibraryCollection {
+// template <typename Iterator, typename Predicate, typename Projection>
+// class FilterIterator {
// public:
-// class LibraryInfo {
-// public:
-// LibraryInfo(const LibraryInfo &) = delete;
-// LibraryInfo &operator=(const LibraryInfo &) = delete;
-
-// LibraryInfo(std::string basePath,
-// std::optional<BloomFilter> filter = std::nullopt
-// : basePath(std::move(basePath)), filter(std::move(filter)) {}
-
-// StringRef getBasePath() const { return basePath; }
-// StringRef getFileName() const { return fileName; }
-
-// std::string getFullPath() const {
-// llvm::SmallString<512> fullPath(basePath);
-// llvm::sys::path::append(fullPath, llvm::StringRef(fileName));
-// return std::string(fullPath.str());
-// }
-
-// bool setFilter(const BloomFilter &f);
-// bool buildFilter(const std::vector<std::string> &symbols);
-// bool ensureFilterBuilt(const std::vector<std::string> &symbols);
-// bool buildHashMap(const std::vector<std::string> &symbols);
-
-// bool mayContain(StringRef symbol) const;
-// bool hasFilter() const { return filter.has_value(); }
-// bool hasHashMap() const { return hashMap.has_value(); }
+// FilterIterator(Iterator current, Iterator end, Predicate pred,
+// Projection proj)
+// : current_(current), end_(end), pred_(pred), proj_(proj) {
+// advance_to_valid();
+// }
-// private:
-// std::string basePath;
-// std::string fileName;
-// std::optional<BloomFilter> filter;
-// std::mutex mutex;
-// };
+// decltype(auto) operator*() { return proj_(*current_); }
-// using LibraryVisitor = unique_function<bool(const LibraryInfo &)>;
+// FilterIterator &operator++() {
+// ++current_;
+// advance_to_valid();
+// return *this;
+// }
-// // Visit each library. Stops early if visitor returns false.
-// bool forEachLibrary(LibraryVisitor &&visitor) const;
+// bool operator!=(const FilterIterator &other) const {
+// return current_ != other.current_;
+// }
-// // Visit each library. Removes those for which the visitor returns true.
-// bool removeIf(LibraryVisitor &&shouldRemove);
+// private:
+// void advance_to_valid() {
+// while (current_ != end_ && !pred_(*current_)) {
+// ++current_;
+// }
+// }
-// // Adds a new library with optional filter and hash map.
-// bool addLibrary(std::string basePath,
-// std::optional<BloomFilter> filter = std::nullopt);
+// Iterator current_, end_;
+// Predicate pred_;
+// Projection proj_;
+// };
-// // Removes a library by base path.
-// bool removeLibrary(const std::string &basePath);
+// template <typename Iterator, typename Predicate, typename Projection>
+// class FilterView {
+// public:
+// FilterView(Iterator begin, Iterator end, Predicate pred, Projection proj)
+// : begin_(begin), end_(end), pred_(pred), proj_(proj) {}
+
+// auto begin() {
+// return FilterIterator<Iterator, Predicate, Projection>(begin_, end_,
+// pred_,
+// proj_);
+// }
+
+// auto end() {
+// return FilterIterator<Iterator, Predicate, Projection>(end_, end_, pred_,
+// proj_);
+// }
+
+// template <typename Iterator, typename Predicate, typename Projection>
+// static auto make_filter_view(Iterator begin, Iterator end, Predicate pred,
+// Projection proj) {
+// return FilterView<Iterator, Predicate, Projection>(begin, end, pred,
+// proj);
+// }
+
+// template <typename Iterator, typename Predicate>
+// static auto make_filter_view(Iterator begin, Iterator end, Predicate pred)
+// {
+// return FilterView<Iterator, Predicate,
+// decltype([](auto &x) -> decltype(auto) { return x; })>(
+// begin, end, pred, [](auto &x) -> decltype(auto) { return x; });
+// }
// private:
-// struct LibraryInfoHash {
-// size_t operator()(const LibraryInfo &lib) const {
-// return std::hash<size_t>()(lib.getBasePath().length()) ^
-// std::hash<std::string>()(std::string(lib.getFileName()));
-// }
-// };
-
-// std::unordered_set<LibraryInfo, LibraryInfoHash> libraries;
-// std::vector<const LibraryInfo *> Libs;
-// mutable std::mutex mutex;
+// Iterator begin_, end_;
+// Predicate pred_;
+// Projection proj_;
// };
/// Manages library metadata and state for symbol resolution.
///
-/// Tracks libraries by load state and kind (user/system), and stores associated
-/// Bloom filters and hash maps to speed up symbol lookups. Thread-safe for
-/// concurrent access.
+/// Tracks libraries by load state and kind (user/system), and stores
+/// associated Bloom filters and hash maps to speed up symbol lookups.
+/// Thread-safe for concurrent access.
class LibraryManager {
public:
enum class State : uint8_t { Unloaded = 0, Loaded = 1, Queried = 2 };
@@ -164,7 +169,7 @@ class LibraryManager {
class FilteredView {
public:
- using Map = std::unordered_map<std::string, std::shared_ptr<LibraryInfo>>;
+ using Map = StringMap<std::shared_ptr<LibraryInfo>>;
using Iterator = typename Map::const_iterator;
class FilterIterator {
public:
@@ -217,7 +222,7 @@ class LibraryManager {
};
private:
- std::unordered_map<std::string, std::shared_ptr<LibraryInfo>> libraries;
+ StringMap<std::shared_ptr<LibraryInfo>> libraries;
mutable std::shared_mutex mutex;
public:
@@ -236,20 +241,20 @@ class LibraryManager {
if (libraries.count(path) > 0)
return false;
- libraries.emplace(std::move(path),
+ libraries.insert({std::move(path),
std::make_shared<LibraryInfo>(path, State::Unloaded, kind,
- std::move(filter)));
+ std::move(filter))});
return true;
}
bool hasLibrary(StringRef path) {
std::shared_lock<std::shared_mutex> lock(mutex);
- if (libraries.count(path.str()) > 0)
+ if (libraries.count(path) > 0)
return true;
return false;
}
- bool removeLibrary(const std::string &path) {
+ bool removeLibrary(StringRef path) {
std::unique_lock<std::shared_mutex> lock(mutex);
// auto P = sys::path::native(path);
auto I = libraries.find(path);
@@ -259,21 +264,21 @@ class LibraryManager {
return true;
}
- void markLoaded(const std::string &path) {
+ void markLoaded(StringRef path) {
std::unique_lock<std::shared_mutex> lock(mutex);
if (auto it = libraries.find(path); it != libraries.end()) {
it->second->setState(State::Loaded);
}
}
- void markQueried(const std::string &path) {
+ void markQueried(StringRef path) {
std::unique_lock<std::shared_mutex> lock(mutex);
if (auto it = libraries.find(path); it != libraries.end()) {
it->second->setState(State::Queried);
}
}
- std::shared_ptr<LibraryInfo> getLibrary(const std::string &path) {
+ std::shared_ptr<LibraryInfo> getLibrary(StringRef path) {
std::shared_lock<std::shared_mutex> lock(mutex);
if (auto it = libraries.find(path); it != libraries.end())
return it->second;
@@ -326,7 +331,7 @@ class DynamicLoader {
public:
enum class Result { Continue, Stop, Error };
- using OnEachSymbolFn = std::function<Result(const std::string &)>;
+ using OnEachSymbolFn = std::function<Result(StringRef Sym)>;
enum class Filter : uint32_t {
None = 0,
@@ -341,7 +346,7 @@ class DynamicLoader {
uint32_t FilterFlags = static_cast<uint32_t>(Filter::Default);
};
- static bool enumerateSymbols(llvm::StringRef Path, OnEachSymbolFn OnEach,
+ static bool enumerateSymbols(StringRef Path, OnEachSymbolFn OnEach,
const Options &Opts);
};
@@ -354,17 +359,18 @@ class DynamicLoader {
private:
mutable std::shared_mutex mtx;
- std::unordered_map<std::string, Result> results;
+ StringMap<Result> results;
std::atomic<size_t> resolvedCount = 0;
public:
explicit SymbolQuery(const std::vector<std::string> &symbols) {
+ // results.reserve(symbols.size());
for (const auto &s : symbols)
- results.emplace(s, Result{s, ""});
+ results.insert({s, Result{s, ""}});
}
- std::vector<std::string> getUnresolvedSymbols() const {
- std::vector<std::string> unresolved;
+ std::vector<StringRef> getUnresolvedSymbols() const {
+ std::vector<StringRef> unresolved;
std::shared_lock lock(mtx);
for (const auto &[name, res] : results) {
if (res.ResolvedLibPath.empty())
@@ -373,12 +379,12 @@ class DynamicLoader {
return unresolved;
}
- void resolve(const std::string &symbol, const std::string &libPath) {
+ void resolve(StringRef symbol, const std::string &libPath) {
std::unique_lock lock(mtx);
auto it = results.find(symbol);
if (it != results.end() && it->second.ResolvedLibPath.empty()) {
it->second.ResolvedLibPath = libPath;
- ++resolvedCount;
+ resolvedCount.fetch_add(1, std::memory_order_relaxed);
}
}
@@ -390,26 +396,26 @@ class DynamicLoader {
return resolvedCount.load(std::memory_order_relaxed) < results.size();
}
- std::optional<std::string> getResolvedLib(const std::string &symbol) const {
+ std::optional<StringRef> getResolvedLib(StringRef symbol) const {
std::shared_lock lock(mtx);
auto it = results.find(symbol);
if (it != results.end() && !it->second.ResolvedLibPath.empty())
- return it->second.ResolvedLibPath;
+ return StringRef(it->second.ResolvedLibPath);
return std::nullopt;
}
- bool isResolved(const std::string &symbol) const {
+ bool isResolved(StringRef symbol) const {
std::shared_lock lock(mtx);
- auto it = results.find(symbol);
+ auto it = results.find(symbol.str());
return it != results.end() && !it->second.ResolvedLibPath.empty();
}
- std::vector<Result> getAllResults() const {
+ std::vector<const Result *> getAllResults() const {
std::shared_lock lock(mtx);
- std::vector<Result> out;
+ std::vector<const Result *> out;
out.reserve(results.size());
for (const auto &[_, res] : results)
- out.push_back(res);
+ out.push_back(&res);
return out;
}
};
@@ -426,12 +432,13 @@ class DynamicLoader {
BloomFilterBuilder filterBuilder = BloomFilterBuilder();
- static Setup create(
- std::vector<std::string> basePaths,
- std::shared_ptr<LibraryPathCache> existingCache = nullptr,
- std::shared_ptr<PathResolver> existingResolver = nullptr,
- // std::shared_ptr<DylibPathResolver> existingDylibResolver = nullptr,
- LibraryScanner::shouldScanFn customShouldScan = nullptr) {
+ static Setup
+ create(std::vector<std::string> basePaths,
+ std::shared_ptr<LibraryPathCache> existingCache = nullptr,
+ std::shared_ptr<PathResolver> existingResolver = nullptr,
+ // std::shared_ptr<DylibPathResolver> existingDylibResolver =
+ // nullptr,
+ LibraryScanner::shouldScanFn customShouldScan = nullptr) {
Setup setup;
setup.basePaths = std::move(basePaths);
@@ -464,10 +471,10 @@ class DynamicLoader {
void scanLibrariesIfNeeded(LibraryManager::Kind K);
void resolveSymbolsInLibrary(LibraryInfo &library, SymbolQuery &query);
bool
- symbolExistsInLibrary(const LibraryInfo &library, llvm::StringRef symbol,
+ symbolExistsInLibrary(const LibraryInfo &library, StringRef symbol,
std::vector<std::string> *matchedSymbols = nullptr);
- bool symbolExistsInLibrary(const LibraryInfo &lib, llvm::StringRef symbolName,
+ bool symbolExistsInLibrary(const LibraryInfo &lib, StringRef symbolName,
std::vector<std::string> *allSymbols,
const SymbolEnumerator::Options &opts);
diff --git a/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/LibraryScanner.h b/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/LibraryScanner.h
index ffe41c118c84e..d55f82ed105af 100644
--- a/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/LibraryScanner.h
+++ b/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/LibraryScanner.h
@@ -157,8 +157,8 @@ class LibraryScanHelper {
std::shared_ptr<PathResolver> m_resolver)
: m_cache(std::move(m_cache)), m_resolver(std::move(m_resolver)) {
// LLVM_DEBUG(
- llvm::dbgs() << "LibraryScanHelper::LibraryScanHelper: base paths : "
- << paths.size() << "\n"; //);
+ dbgs() << "LibraryScanHelper::LibraryScanHelper: base paths : "
+ << paths.size() << "\n"; //);
for (const auto &p : paths)
addBasePath(p);
}
diff --git a/llvm/lib/ExecutionEngine/Orc/TargetProcess/DynamicLoader.cpp b/llvm/lib/ExecutionEngine/Orc/TargetProcess/DynamicLoader.cpp
index 5388d8d1aa9c0..0abff22611bb8 100644
--- a/llvm/lib/ExecutionEngine/Orc/TargetProcess/DynamicLoader.cpp
+++ b/llvm/lib/ExecutionEngine/Orc/TargetProcess/DynamicLoader.cpp
@@ -9,6 +9,8 @@
#include "llvm/ExecutionEngine/Orc/TargetProcess/DynamicLoader.h"
#include "llvm/ExecutionEngine/Orc/TargetProcess/LibraryScanner.h"
+#include "llvm/ADT/StringSet.h"
+
#include "llvm/BinaryFormat/MachO.h"
#include "llvm/Object/COFF.h"
#include "llvm/Object/ELF.h"
@@ -17,6 +19,9 @@
#include "llvm/Object/ObjectFile.h"
#include "llvm/Support/Error.h"
+#include <mutex>
+#include <thread>
+
#define DEBUG_TYPE "orc"
namespace llvm::orc {
@@ -90,7 +95,7 @@ bool SymbolEnumerator::enumerateSymbols(StringRef Path, OnEachSymbolFn OnEach,
if (Name.empty())
continue;
- EnumerateResult Res = OnEach(Name.str());
+ EnumerateResult Res = OnEach(Name);
if (Res != EnumerateResult::Continue)
return Res;
}
@@ -117,7 +122,7 @@ bool SymbolEnumerator::enumerateSymbols(StringRef Path, OnEachSymbolFn OnEach,
if (Name.empty())
continue;
- if (OnEach(Name.str()) != EnumerateResult::Continue)
+ if (OnEach(Name) != EnumerateResult::Continue)
return false;
}
} else if (Obj->isMachO()) {
@@ -147,16 +152,14 @@ void DynamicLoader::resolveSymbolsInLibrary(LibraryInfo &lib,
// );
SymbolEnumerator::enumerateSymbols(
lib.getFullPath(),
- [&](const std::string &sym) {
- discoveredSymbols.insert(sym);
+ [&](StringRef sym) {
+ discoveredSymbols.insert(sym.str());
return SymbolEnumerator::Result::Continue;
},
opts);
};
- const auto &unresolved = unresolvedSymbols.getUnresolvedSymbols();
-
- if (unresolved.empty()) {
+ if (!unresolvedSymbols.hasUnresolved()) {
// LLVM_DEBUG(
dbgs() << "Skipping library: " << lib.getFullPath()
<< " — unresolved symbols exist.\n";
@@ -174,12 +177,13 @@ void DynamicLoader::resolveSymbolsInLibrary(LibraryInfo &lib,
dbgs() << "discoveredSymbols : " << sym << "\n";
}
+ const auto &unresolved = unresolvedSymbols.getUnresolvedSymbols();
for (const auto &symbol : unresolved) {
if (lib.mayContain(symbol)) {
// LLVM_DEBUG(
dbgs() << "Checking symbol '" << symbol
<< "' in library: " << lib.getFullPath() << "\n"; //);
- if (discoveredSymbols.count(symbol) > 0) {
+ if (discoveredSymbols.count(symbol.str()) > 0) {
// LLVM_DEBUG(
dbgs() << " Resolved symbol: " << symbol
<< " in library: " << lib.getFullPath() << "\n"; //);
@@ -225,43 +229,22 @@ void DynamicLoader::searchSymbolsInLibraries(
}
done:
- // LLVM_DEBUG(
- dbgs() << "Search complete.\n"; //);
+ // LLVM_DEBUG({
+ dbgs() << "Search complete.\n";
+ for (const auto &r : query.getAllResults())
+ dbgs() << "Resolved Symbol:" << r->Name << " -> " << r->ResolvedLibPath
+ << "\n";
+ //});
+
// ProcessLib(query.getResolvedPath());
onComplete(query);
}
-// void DynamicLoader::searchSymbolsInLibraries(
-// std::vector<std::string> &symbolList, OnSearchComplete onComplete) {
-// SymbolQuery query(symbolList);
-
-// auto tryResolveFrom = [&](const LibraryCollection &libraries,
-// bool isUserLib) {
-// scanLibrariesIfNeeded(libraries, isUserLib);
-// for (const auto &lib : libraries) {
-// // can use Async here?
-// tryToResolveSymbols(lib, query);
-// if (query.allResolved())
-// break;
-// }
-// };
-
-// tryResolveFrom(loadedLibs, /*isUserLib=*/false);
-
-// if (!query.allResolved())
-// tryResolveFrom(usrLibs, /*isUserLib=*/true);
-
-// if (!query.allResolved() && includesys)
-// tryResolveFrom(sysLibs, /*isUserLib=*/false);
-
-// onComplete(query);
-// }
-
void DynamicLoader::scanLibrariesIfNeeded(LibraryManager::Kind PK) {
// LLVM_DEBUG(
- llvm::dbgs() << "DynamicLoader::scanLibrariesIfNeeded: Scanning for "
- << (PK == LibraryManager::Kind::User ? "User" : "System")
- << " libraries\n"; //);
+ dbgs() << "DynamicLoader::scanLibrariesIfNeeded: Scanning for "
+ << (PK == LibraryManager::Kind::User ? "User" : "System")
+ << " libraries\n"; //);
LibraryScanner Scanner(ScanH, LibMgr, m_shouldScan);
Scanner.scanNext(PK == LibraryManager::Kind::User ? PathKind::User
: PathKind::System);
@@ -284,9 +267,9 @@ bool DynamicLoader::symbolExistsInLibrary(
SymbolEnumerator::enumerateSymbols(
lib.getFullPath(),
- [&](const std::string &sym) {
+ [&](StringRef sym) {
if (allSymbols)
- allSymbols->emplace_back(sym);
+ allSymbols->emplace_back(sym.str());
if (sym == symbolName) {
found = true;
diff --git a/llvm/lib/ExecutionEngine/Orc/TargetProcess/LibraryScanner.cpp b/llvm/lib/ExecutionEngine/Orc/TargetProcess/LibraryScanner.cpp
index 436c8fce75a15..75dcabb9a5d42 100644
--- a/llvm/lib/ExecutionEngine/Orc/TargetProcess/LibraryScanner.cpp
+++ b/llvm/lib/ExecutionEngine/Orc/TargetProcess/LibraryScanner.cpp
@@ -51,7 +51,7 @@ bool isLibraryFile(StringRef filename) {
template <class ELFT>
bool isELFSharedLibrary(const object::ELFFile<ELFT> &ELFObj) {
- return ELFObj.getHeader().e_type == llvm::ELF::ET_DYN;
+ return ELFObj.getHeader().e_type == ELF::ET_DYN;
}
bool isSharedLibrary(StringRef Path) {
@@ -111,7 +111,7 @@ std::optional<std::string> DylibPathResolver::substOne(StringRef path,
if (path.size() < pattern.size() || !path.starts_with_insensitive(pattern))
return std::nullopt;
- llvm::SmallString<256> result(replacement);
+ SmallString<256> result(replacement);
result.append(path.drop_front(pattern.size()));
return normalizeIfShared(result);
@@ -121,16 +121,16 @@ std::optional<std::string> DylibPathResolver::substAll(StringRef original,
StringRef loaderPath) {
#ifdef __APPLE__
- llvm::SmallString<256> mainExecutablePath(
- llvm::sys::fs::getMainExecutable(nullptr, nullptr));
- llvm::sys::path::remove_filename(mainExecutablePath);
+ SmallString<256> mainExecutablePath(
+ sys::fs::getMainExecutable(nullptr, nullptr));
+ sys::path::remove_filename(mainExecutablePath);
- llvm::SmallString<256> loaderDir;
+ SmallString<256> loaderDir;
if (loaderPath.empty())
loaderDir = mainExecutablePath;
else {
loaderDir = loaderPath;
- llvm::sys::path::remove_filename(loaderDir);
+ sys::path::remove_filename(loaderDir);
}
// Try @loader_path
@@ -142,13 +142,13 @@ std::optional<std::string> DylibPathResolver::substAll(StringRef original,
return path;
#else
- llvm::SmallString<256> loaderDir;
+ SmallString<256> loaderDir;
if (loaderPath.empty())
- loaderDir = llvm::sys::fs::getMainExecutable(nullptr, nullptr);
+ loaderDir = sys::fs::getMainExecutable(nullptr, nullptr);
else
loaderDir = loaderPath;
- llvm::sys::path::remove_filename(loaderDir);
+ sys::path::remove_filename(loaderDir);
// Try $origin
if (auto path = substOne(original, "$origin", loaderDir))
@@ -168,8 +168,8 @@ DylibPathResolver::tryWithBasePaths(ArrayRef<StringRef> basePaths,
if (!resolvedBaseOpt)
continue;
- llvm::SmallString<256> fullPath(*resolvedBaseOpt);
- llvm::sys::path::append(fullPath, stem);
+ SmallString<256> fullPath(*resolvedBaseOpt);
+ sys::path::append(fullPath, stem);
if (auto norm = normalizeIfShared(fullPath)) {
return norm;
@@ -214,7 +214,7 @@ std::optional<std::string> DylibPathResolver::tryWithExtensions(
#endif
// Optionally try "lib" prefix if not already there
- StringRef filename = llvm::sys::path::filename(baseName);
+ StringRef filename = sys::path::filename(baseName);
if (!filename.starts_with("lib")) {
SmallString<256> withPrefix("lib");
withPrefix += filename;
@@ -256,7 +256,7 @@ DylibPathResolver::resolve(StringRef libStem, SmallVector<StringRef, 2> RPath,
SmallVector<StringRef, 2> RunPath,
StringRef libLoader, bool variateLibStem) {
// If it is an absolute path, don't try iterate over the paths.
- if (llvm::sys::path::is_absolute(libStem)) {
+ if (sys::path::is_absolute(libStem)) {
return normalizeIfShared(libStem);
}
@@ -447,9 +447,11 @@ std::optional<std::string> PathResolver::realpathCached(StringRef path,
}
normalizePathSegments(Components);
+ // LLVM_DEBUG({
for (auto &C : Components)
- // LLVM_DEBUG(
- dbgs() << " " << C << "\n"; //);
+ dbgs() << " " << C << " ";
+
+ dbgs() << "\n"; //});
// Handle path list items
for (const auto &component : Components) {
@@ -468,8 +470,9 @@ std::optional<std::string> PathResolver::realpathCached(StringRef path,
auto symlinkOpt = readlinkCached(resolvedPath);
if (!symlinkOpt) {
ec = std::make_error_code(std::errc::no_such_file_or_directory);
- // std::unique_lock lock(m_mutex);
- // m_cache->m_realpathCache.emplace(path, PathInfo{"", ec});
+ std::unique_lock lock(m_mutex);
+ m_cache->m_realpathCache.emplace(path,
+ LibraryPathCache::PathInfo{"", ec});
// LLVM_DEBUG(
dbgs() << " Failed to read symlink: " << resolvedPath << "\n"; //);
@@ -486,7 +489,9 @@ std::optional<std::string> PathResolver::realpathCached(StringRef path,
realpathCached(symlink.str(), ec, resolved,
/*baseIsResolved=*/true, symloopLevel - 1);
if (!realSymlink) {
- // m_cache->m_realpathCache.emplace(path, PathInfo{"", ec});
+ std::unique_lock lock(m_mutex);
+ m_cache->m_realpathCache.emplace(path,
+ LibraryPathCache::PathInfo{"", ec});
// LLVM_DEBUG(
dbgs() << " Failed to resolve symlink target: " << symlink
<< "\n"; //);
@@ -500,8 +505,9 @@ std::optional<std::string> PathResolver::realpathCached(StringRef path,
} else if (st_mode == 0) {
ec = std::make_error_code(std::errc::no_such_file_or_directory);
- // std::unique_lock lock(m_mutex);
- // m_cache->m_realpathCache.emplace(path, PathInfo{"", ec});
+ std::unique_lock lock(m_mutex);
+ m_cache->m_realpathCache.emplace(path,
+ LibraryPathCache::PathInfo{"", ec});
// LLVM_DEBUG(
dbgs() << " Component does not exist: " << resolvedPath << "\n"; //);
@@ -531,16 +537,15 @@ void LibraryScanHelper::addBasePath(const std::string &path) {
std::string canon = resolveCanonical(path, ec);
if (ec) {
// LLVM_DEBUG(
- llvm::dbgs()
- << "LibraryScanHelper::addBasePath: Failed to canonicalize path: "
- << path << "\n"; //);
+ dbgs() << "LibraryScanHelper::addBasePath: Failed to canonicalize path: "
+ << path << "\n"; //);
return;
}
std::unique_lock lock(m_mutex);
if (m_units.count(canon)) {
// LLVM_DEBUG(
- llvm::dbgs() << "LibraryScanHelper::addBasePath: Already added: " << canon
- << "\n"; //);
+ dbgs() << "LibraryScanHelper::addBasePath: Already added: " << canon
+ << "\n"; //);
return;
}
PathKind kind = classifyKind(canon);
@@ -549,13 +554,13 @@ void LibraryScanHelper::addBasePath(const std::string &path) {
if (kind == PathKind::User) {
// LLVM_DEBUG(
- llvm::dbgs() << "LibraryScanHelper::addBasePath: Added User path: " << canon
- << "\n"; //);
+ dbgs() << "LibraryScanHelper::addBasePath: Added User path: " << canon
+ << "\n"; //);
m_unscannedUsr.push_back(canon);
} else {
// LLVM_DEBUG(
- llvm::dbgs() << "LibraryScanHelper::addBasePath: Added System path: "
- << canon << "\n"; //);
+ dbgs() << "LibraryScanHelper::addBasePath: Added System path: " << canon
+ << "\n"; //);
m_unscannedSys.push_back(canon);
}
}
@@ -744,7 +749,8 @@ Expected<LibraryDepsInfo> parseELF(const object::ELFFile<ELFT> &Elf) {
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))
@@ -754,25 +760,55 @@ Expected<LibraryDepsInfo> parseELFDeps(const object::ELFObjectFileBase &obj) {
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");
}
+void handleError(Error Err) {
+ consumeError(llvm::handleErrors(std::move(Err), [](const ErrorInfoBase &EIB) {
+ dbgs() << "LLVM Error: " << EIB.message() << "\n";
+ }));
+}
+
+template <typename T> T handleErrorAndReturn(Error Err, T ReturnValue) {
+ handleError(std::move(Err));
+ return ReturnValue;
+}
+
Expected<LibraryDepsInfo> LibraryScanner::extractDeps(StringRef filePath) {
+ // LLVM_DEBUG(
+ dbgs() << "extractDeps: Attempting to open file " << filePath << "\n"; //);
+
auto ObjOrErr = object::ObjectFile::createObjectFile(filePath);
- if (!ObjOrErr)
+ if (!ObjOrErr) {
+ // LLVM_DEBUG(
+ dbgs() << "extractDeps: Failed to open " << filePath << "\n"; //);
+ consumeError(ObjOrErr.takeError());
+ return handleErrorAndReturn(ObjOrErr.takeError(),
+ createStringError(std::errc::file_exists,
+ "Failed to open %s",
+ filePath.str().c_str()));
return createStringError(std::errc::file_exists, "Failed to open %s",
filePath.str().c_str());
-
+ }
object::ObjectFile *Obj = ObjOrErr.get().getBinary();
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);
}
-
+ // LLVM_DEBUG(
+ dbgs() << "extractDeps: Unsupported binary format for file " << filePath
+ << "\n"; //);
return createStringError(inconvertibleErrorCode(),
"Unsupported binary format: %s",
filePath.str().c_str());
@@ -796,16 +832,16 @@ std::optional<std::string> LibraryScanner::shouldScan(StringRef filePath) {
if (sys::fs::is_directory(CanonicalPath))
return std::nullopt;
- // [4] Skip if we've already seen this path (via cache)
- if (m_helper.hasSeen(CanonicalPath))
+ // [4] Skip if it's not a shared library.
+ if (!isSharedLibrary(CanonicalPath))
return std::nullopt;
- // [5] Already tracked in LibraryManager?
- if (m_libMgr.hasLibrary(CanonicalPath))
+ // [5] Skip if we've already seen this path (via cache)
+ if (m_helper.hasSeen(CanonicalPath))
return std::nullopt;
- // [6] Is this a shared library?
- if (!isSharedLibrary(CanonicalPath))
+ // [6] Already tracked in LibraryManager?
+ if (m_libMgr.hasLibrary(CanonicalPath))
return std::nullopt;
// [7] Run user-defined hook (default: always true)
@@ -833,8 +869,7 @@ void LibraryScanner::handleLibrary(StringRef filePath, PathKind K, int level) {
if (!DepsOrErr) {
// LLVM_DEBUG(
dbgs() << " Failed to extract deps for: " << CanonicalPath << "\n"; //);
-
- consumeError(DepsOrErr.takeError());
+ handleError(DepsOrErr.takeError());
return;
}
@@ -860,7 +895,7 @@ void LibraryScanner::handleLibrary(StringRef filePath, PathKind K, int level) {
// Heuristic 1: No RPATH/RUNPATH, skip deps
if (Deps.rpath.empty() && Deps.runPath.empty()) {
// LLVM_DEBUG(
- dbgs() << "Dyld::ScanForLibraries: Skipping deps (Heuristic1): "
+ dbgs() << "LibraryScanner::handleLibrary: Skipping deps (Heuristic1): "
<< CanonicalPath << "\n"; //);
return;
}
@@ -874,7 +909,7 @@ void LibraryScanner::handleLibrary(StringRef filePath, PathKind K, int level) {
if (allTracked(Deps.rpath) && allTracked(Deps.runPath)) {
// LLVM_DEBUG(
- dbgs() << "Dyld::ScanForLibraries: Skipping deps (Heuristic2): "
+ dbgs() << "LibraryScanner::handleLibrary: Skipping deps (Heuristic2): "
<< CanonicalPath << "\n"; //);
return;
}
>From cef69e40cbf1808fb5cd71343bd278666bc586f5 Mon Sep 17 00:00:00 2001
From: SahilPatidar <patidarsahil2001 at gmail.com>
Date: Sun, 13 Jul 2025 12:03:34 +0530
Subject: [PATCH 4/6] Fix some compilation issues
---
.../Orc/TargetProcess/DynamicLoader.h | 107 ++++--------------
.../Orc/TargetProcess/LibraryScanner.h | 21 +++-
.../Orc/TargetProcess/DynamicLoader.cpp | 13 ++-
.../Orc/TargetProcess/LibraryScanner.cpp | 26 ++---
4 files changed, 55 insertions(+), 112 deletions(-)
diff --git a/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/DynamicLoader.h b/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/DynamicLoader.h
index ef42f3897f9cc..6492823aa0d56 100644
--- a/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/DynamicLoader.h
+++ b/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/DynamicLoader.h
@@ -20,83 +20,13 @@
#include "llvm/ExecutionEngine/Orc/TargetProcess/LibraryScanner.h"
#include "llvm/Support/Path.h"
+#include <atomic>
#include <shared_mutex>
#include <unordered_map>
namespace llvm {
namespace orc {
-// template <typename Iterator, typename Predicate, typename Projection>
-// class FilterIterator {
-// public:
-// FilterIterator(Iterator current, Iterator end, Predicate pred,
-// Projection proj)
-// : current_(current), end_(end), pred_(pred), proj_(proj) {
-// advance_to_valid();
-// }
-
-// decltype(auto) operator*() { return proj_(*current_); }
-
-// FilterIterator &operator++() {
-// ++current_;
-// advance_to_valid();
-// return *this;
-// }
-
-// bool operator!=(const FilterIterator &other) const {
-// return current_ != other.current_;
-// }
-
-// private:
-// void advance_to_valid() {
-// while (current_ != end_ && !pred_(*current_)) {
-// ++current_;
-// }
-// }
-
-// Iterator current_, end_;
-// Predicate pred_;
-// Projection proj_;
-// };
-
-// template <typename Iterator, typename Predicate, typename Projection>
-// class FilterView {
-// public:
-// FilterView(Iterator begin, Iterator end, Predicate pred, Projection proj)
-// : begin_(begin), end_(end), pred_(pred), proj_(proj) {}
-
-// auto begin() {
-// return FilterIterator<Iterator, Predicate, Projection>(begin_, end_,
-// pred_,
-// proj_);
-// }
-
-// auto end() {
-// return FilterIterator<Iterator, Predicate, Projection>(end_, end_, pred_,
-// proj_);
-// }
-
-// template <typename Iterator, typename Predicate, typename Projection>
-// static auto make_filter_view(Iterator begin, Iterator end, Predicate pred,
-// Projection proj) {
-// return FilterView<Iterator, Predicate, Projection>(begin, end, pred,
-// proj);
-// }
-
-// template <typename Iterator, typename Predicate>
-// static auto make_filter_view(Iterator begin, Iterator end, Predicate pred)
-// {
-// return FilterView<Iterator, Predicate,
-// decltype([](auto &x) -> decltype(auto) { return x; })>(
-// begin, end, pred, [](auto &x) -> decltype(auto) { return x; });
-// }
-
-// private:
-// Iterator begin_, end_;
-// Predicate pred_;
-// Projection proj_;
-// };
-
/// Manages library metadata and state for symbol resolution.
///
/// Tracks libraries by load state and kind (user/system), and stores
@@ -123,7 +53,7 @@ class LibraryManager {
std::string getFullPath() const { return filePath; }
bool setFilter(BloomFilter F) {
- std::lock_guard lock(mutex);
+ std::lock_guard<std::shared_mutex> lock(mutex);
if (filter)
return false;
filter.emplace(std::move(F));
@@ -132,7 +62,7 @@ class LibraryManager {
bool ensureFilterBuilt(const BloomFilterBuilder &FB,
const std::vector<std::string> &symbols) {
- std::lock_guard lock(mutex);
+ std::lock_guard<std::shared_mutex> lock(mutex);
if (filter)
return false;
filter.emplace(FB.build(symbols));
@@ -141,12 +71,12 @@ class LibraryManager {
bool mayContain(StringRef symbol) const {
assert(hasFilter());
- std::shared_lock lock(mutex);
+ std::shared_lock<std::shared_mutex> lock(mutex);
return filter->mayContain(symbol);
}
bool hasFilter() const {
- std::shared_lock lock(mutex);
+ std::shared_lock<std::shared_mutex> lock(mutex);
return filter.has_value();
}
@@ -286,12 +216,12 @@ class LibraryManager {
}
FilteredView getView(State s, Kind k) const {
- std::shared_lock lock(mutex);
+ std::shared_lock<std::shared_mutex> lock(mutex);
return FilteredView(libraries.begin(), libraries.end(), s, k);
}
void forEachLibrary(const LibraryVisitor &visitor) const {
- std::unique_lock lock(mutex);
+ std::unique_lock<std::shared_mutex> lock(mutex);
for (const auto &[_, entry] : libraries) {
if (!visitor(*entry))
break;
@@ -299,21 +229,21 @@ class LibraryManager {
}
bool isLoaded(StringRef path) const {
- std::unique_lock lock(mutex);
+ std::unique_lock<std::shared_mutex> lock(mutex);
if (auto it = libraries.find(path.str()); it != libraries.end())
return it->second->getState() == State::Loaded;
return false;
}
bool isQueried(StringRef path) const {
- std::unique_lock lock(mutex);
+ std::unique_lock<std::shared_mutex> lock(mutex);
if (auto it = libraries.find(path.str()); it != libraries.end())
return it->second->getState() == State::Queried;
return false;
}
void clear() {
- std::unique_lock lock(mutex);
+ std::unique_lock<std::shared_mutex> lock(mutex);
libraries.clear();
}
};
@@ -326,6 +256,8 @@ using LibraryInfo = LibraryManager::LibraryInfo;
/// symbol resolution results through SymbolQuery. Thread-safe and uses
/// LibraryScanHelper for efficient path resolution and caching.
class DynamicLoader {
+ friend class LoaderControllerImpl;
+
public:
class SymbolEnumerator {
public:
@@ -371,7 +303,7 @@ class DynamicLoader {
std::vector<StringRef> getUnresolvedSymbols() const {
std::vector<StringRef> unresolved;
- std::shared_lock lock(mtx);
+ std::shared_lock<std::shared_mutex> lock(mtx);
for (const auto &[name, res] : results) {
if (res.ResolvedLibPath.empty())
unresolved.push_back(name);
@@ -380,7 +312,7 @@ class DynamicLoader {
}
void resolve(StringRef symbol, const std::string &libPath) {
- std::unique_lock lock(mtx);
+ std::unique_lock<std::shared_mutex> lock(mtx);
auto it = results.find(symbol);
if (it != results.end() && it->second.ResolvedLibPath.empty()) {
it->second.ResolvedLibPath = libPath;
@@ -397,7 +329,7 @@ class DynamicLoader {
}
std::optional<StringRef> getResolvedLib(StringRef symbol) const {
- std::shared_lock lock(mtx);
+ std::shared_lock<std::shared_mutex> lock(mtx);
auto it = results.find(symbol);
if (it != results.end() && !it->second.ResolvedLibPath.empty())
return StringRef(it->second.ResolvedLibPath);
@@ -405,13 +337,13 @@ class DynamicLoader {
}
bool isResolved(StringRef symbol) const {
- std::shared_lock lock(mtx);
+ std::shared_lock<std::shared_mutex> lock(mtx);
auto it = results.find(symbol.str());
return it != results.end() && !it->second.ResolvedLibPath.empty();
}
std::vector<const Result *> getAllResults() const {
- std::shared_lock lock(mtx);
+ std::shared_lock<std::shared_mutex> lock(mtx);
std::vector<const Result *> out;
out.reserve(results.size());
for (const auto &[_, res] : results)
@@ -480,15 +412,16 @@ class DynamicLoader {
std::shared_ptr<LibraryPathCache> m_cache;
std::shared_ptr<PathResolver> m_PathResolver;
- LibraryScanHelper ScanH;
+ LibraryScanHelper m_scanH;
BloomFilterBuilder FB;
- LibraryManager LibMgr;
+ LibraryManager m_libMgr;
LibraryScanner::shouldScanFn m_shouldScan;
// std::shared_ptr<DylibPathResolver> m_DylibPathResolver;
bool includeSys;
};
using SymbolEnumerator = DynamicLoader::SymbolEnumerator;
+using SymbolQuery = DynamicLoader::SymbolQuery;
using EnumerateResult = SymbolEnumerator::Result;
} // end namespace orc
diff --git a/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/LibraryScanner.h b/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/LibraryScanner.h
index d55f82ed105af..2420ff5c82b38 100644
--- a/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/LibraryScanner.h
+++ b/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/LibraryScanner.h
@@ -19,6 +19,7 @@
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Error.h"
+#include <atomic>
#include <mutex>
#include <queue>
#include <shared_mutex>
@@ -37,12 +38,18 @@ class LibraryPathCache {
public:
LibraryPathCache() = default;
- void clear();
+ void clear() {
+ std::unique_lock<std::shared_mutex> lock(m_mutex);
+ m_seen.clear();
+ m_readlinkCache.clear();
+ m_realpathCache.clear();
+ m_lstatCache.clear();
+ }
void markSeen(const std::string &canon_path) { m_seen.insert(canon_path); }
bool hasSeen(StringRef canon_path, bool cache = true) {
- std::shared_lock lock(m_mutex);
+ std::shared_lock<std::shared_mutex> lock(m_mutex);
std::string s = canon_path.str();
if (m_seen.count(s) > 0)
return true;
@@ -133,9 +140,9 @@ class DylibPathResolver {
std::optional<std::string> normalizeIfShared(StringRef path);
};
-enum class PathKind { User, System };
+enum class PathKind : uint8_t { User, System, Unknown };
-enum class ScanState { NotScanned, Scanning, Scanned };
+enum class ScanState : uint8_t { NotScanned, Scanning, Scanned };
struct LibraryUnit {
std::string basePath; // Canonical base directory path
@@ -163,8 +170,10 @@ class LibraryScanHelper {
addBasePath(p);
}
- void addBasePath(
- const std::string &path); // Add a canonical directory for scanning
+ void
+ addBasePath(const std::string &path,
+ PathKind Kind =
+ PathKind::Unknown); // Add a canonical directory for scanning
std::vector<std::shared_ptr<LibraryUnit>> getNextBatch(PathKind kind,
size_t batchSize);
diff --git a/llvm/lib/ExecutionEngine/Orc/TargetProcess/DynamicLoader.cpp b/llvm/lib/ExecutionEngine/Orc/TargetProcess/DynamicLoader.cpp
index 0abff22611bb8..9168af976d993 100644
--- a/llvm/lib/ExecutionEngine/Orc/TargetProcess/DynamicLoader.cpp
+++ b/llvm/lib/ExecutionEngine/Orc/TargetProcess/DynamicLoader.cpp
@@ -31,12 +31,13 @@ DynamicLoader::DynamicLoader(const DynamicLoader::Setup &setup)
m_PathResolver(setup.resolver ? setup.resolver
: std::make_shared<PathResolver>(m_cache)),
// m_DylibPathResolver(setup.dylibResolver),
- ScanH(setup.basePaths, m_cache, m_PathResolver), FB(setup.filterBuilder),
- LibMgr(), m_shouldScan(setup.shouldScan ? setup.shouldScan
- : [](StringRef) { return true; }),
+ m_scanH(setup.basePaths, m_cache, m_PathResolver),
+ FB(setup.filterBuilder), m_libMgr(),
+ m_shouldScan(setup.shouldScan ? setup.shouldScan
+ : [](StringRef) { return true; }),
includeSys(setup.includeSys) {
- if (ScanH.getAllUnits().empty()) {
+ if (m_scanH.getAllUnits().empty()) {
errs() << "Warning: No base paths provided for scanning.\n";
}
}
@@ -206,7 +207,7 @@ void DynamicLoader::searchSymbolsInLibraries(
dbgs() << "Trying resolve from state=" << static_cast<int>(S)
<< " type=" << static_cast<int>(K) << "\n"; //);
scanLibrariesIfNeeded(K);
- for (auto &lib : LibMgr.getView(S, K)) {
+ for (auto &lib : m_libMgr.getView(S, K)) {
// can use Async here?
resolveSymbolsInLibrary(*lib, query);
if (query.allResolved())
@@ -245,7 +246,7 @@ void DynamicLoader::scanLibrariesIfNeeded(LibraryManager::Kind PK) {
dbgs() << "DynamicLoader::scanLibrariesIfNeeded: Scanning for "
<< (PK == LibraryManager::Kind::User ? "User" : "System")
<< " libraries\n"; //);
- LibraryScanner Scanner(ScanH, LibMgr, m_shouldScan);
+ LibraryScanner Scanner(m_scanH, m_libMgr, m_shouldScan);
Scanner.scanNext(PK == LibraryManager::Kind::User ? PathKind::User
: PathKind::System);
}
diff --git a/llvm/lib/ExecutionEngine/Orc/TargetProcess/LibraryScanner.cpp b/llvm/lib/ExecutionEngine/Orc/TargetProcess/LibraryScanner.cpp
index 75dcabb9a5d42..9c443b8375f77 100644
--- a/llvm/lib/ExecutionEngine/Orc/TargetProcess/LibraryScanner.cpp
+++ b/llvm/lib/ExecutionEngine/Orc/TargetProcess/LibraryScanner.cpp
@@ -298,7 +298,7 @@ std::optional<std::string> readlinkCached(const std::string &path) {
mode_t PathResolver::lstatCached(const std::string &path) {
// If already cached - retun cached result
- std::unique_lock lock(m_mutex);
+ std::unique_lock<std::shared_mutex> lock(m_mutex);
auto &cache = m_cache->m_lstatCache;
@@ -317,7 +317,7 @@ mode_t PathResolver::lstatCached(const std::string &path) {
std::optional<std::string>
PathResolver::readlinkCached(const std::string &path) {
- std::unique_lock lock(m_mutex);
+ std::unique_lock<std::shared_mutex> lock(m_mutex);
auto &cache = m_cache->m_readlinkCache;
// If already cached - retun cached result
auto it = cache.find(path);
@@ -408,7 +408,7 @@ std::optional<std::string> PathResolver::realpathCached(StringRef path,
// If already cached - retun cached result
bool isRelative = sys::path::is_relative(path);
{
- std::shared_lock lock(m_mutex);
+ std::shared_lock<std::shared_mutex> lock(m_mutex);
auto it = m_cache->m_realpathCache.find(path.str());
if (it != m_cache->m_realpathCache.end()) {
ec = it->second.errnoCode;
@@ -470,7 +470,7 @@ std::optional<std::string> PathResolver::realpathCached(StringRef path,
auto symlinkOpt = readlinkCached(resolvedPath);
if (!symlinkOpt) {
ec = std::make_error_code(std::errc::no_such_file_or_directory);
- std::unique_lock lock(m_mutex);
+ std::unique_lock<std::shared_mutex> lock(m_mutex);
m_cache->m_realpathCache.emplace(path,
LibraryPathCache::PathInfo{"", ec});
// LLVM_DEBUG(
@@ -489,7 +489,7 @@ std::optional<std::string> PathResolver::realpathCached(StringRef path,
realpathCached(symlink.str(), ec, resolved,
/*baseIsResolved=*/true, symloopLevel - 1);
if (!realSymlink) {
- std::unique_lock lock(m_mutex);
+ std::unique_lock<std::shared_mutex> lock(m_mutex);
m_cache->m_realpathCache.emplace(path,
LibraryPathCache::PathInfo{"", ec});
// LLVM_DEBUG(
@@ -505,7 +505,7 @@ std::optional<std::string> PathResolver::realpathCached(StringRef path,
} else if (st_mode == 0) {
ec = std::make_error_code(std::errc::no_such_file_or_directory);
- std::unique_lock lock(m_mutex);
+ std::unique_lock<std::shared_mutex> lock(m_mutex);
m_cache->m_realpathCache.emplace(path,
LibraryPathCache::PathInfo{"", ec});
// LLVM_DEBUG(
@@ -520,7 +520,7 @@ std::optional<std::string> PathResolver::realpathCached(StringRef path,
std::string canonical = resolved.str().str();
{
- std::unique_lock lock(m_mutex);
+ std::unique_lock<std::shared_mutex> lock(m_mutex);
m_cache->m_realpathCache.emplace(path, LibraryPathCache::PathInfo{
canonical,
std::error_code() // success
@@ -532,7 +532,7 @@ std::optional<std::string> PathResolver::realpathCached(StringRef path,
return canonical;
}
-void LibraryScanHelper::addBasePath(const std::string &path) {
+void LibraryScanHelper::addBasePath(const std::string &path, PathKind kind) {
std::error_code ec;
std::string canon = resolveCanonical(path, ec);
if (ec) {
@@ -541,14 +541,14 @@ void LibraryScanHelper::addBasePath(const std::string &path) {
<< path << "\n"; //);
return;
}
- std::unique_lock lock(m_mutex);
+ std::unique_lock<std::shared_mutex> lock(m_mutex);
if (m_units.count(canon)) {
// LLVM_DEBUG(
dbgs() << "LibraryScanHelper::addBasePath: Already added: " << canon
<< "\n"; //);
return;
}
- PathKind kind = classifyKind(canon);
+ kind = kind == PathKind::Unknown ? classifyKind(canon) : kind;
auto unit = std::make_shared<LibraryUnit>(canon, kind);
m_units[canon] = unit;
@@ -570,7 +570,7 @@ LibraryScanHelper::getNextBatch(PathKind kind, size_t batchSize) {
std::vector<std::shared_ptr<LibraryUnit>> result;
auto &queue = (kind == PathKind::User) ? m_unscannedUsr : m_unscannedSys;
- std::unique_lock lock(m_mutex);
+ std::unique_lock<std::shared_mutex> lock(m_mutex);
while (!queue.empty() && result.size() < batchSize) {
const std::string &base = queue.front(); // no copy
@@ -594,13 +594,13 @@ bool LibraryScanHelper::isTrackedBasePath(const std::string &path) const {
if (ec) {
return false;
}
- std::shared_lock lock(m_mutex);
+ std::shared_lock<std::shared_mutex> lock(m_mutex);
return m_units.count(canon) > 0;
}
std::vector<std::shared_ptr<LibraryUnit>>
LibraryScanHelper::getAllUnits() const {
- std::shared_lock lock(m_mutex);
+ std::shared_lock<std::shared_mutex> lock(m_mutex);
std::vector<std::shared_ptr<LibraryUnit>> result;
result.reserve(m_units.size());
for (const auto &[_, unit] : m_units) {
>From e32a10a5be8ae8b6bfad5faf51b87e4450e9826a Mon Sep 17 00:00:00 2001
From: SahilPatidar <patidarsahil2001 at gmail.com>
Date: Sun, 13 Jul 2025 15:08:17 +0530
Subject: [PATCH 5/6] Fix window build issue
---
.../Orc/TargetProcess/LibraryScanner.h | 12 +-
.../Orc/TargetProcess/LibraryScanner.cpp | 104 +++++++++---------
2 files changed, 56 insertions(+), 60 deletions(-)
diff --git a/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/LibraryScanner.h b/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/LibraryScanner.h
index 2420ff5c82b38..09150c5779759 100644
--- a/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/LibraryScanner.h
+++ b/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/LibraryScanner.h
@@ -86,8 +86,10 @@ class PathResolver {
std::error_code &ec) {
return realpathCached(path, ec);
}
+#ifndef _WIN32
mode_t lstatCached(const std::string &path);
std::optional<std::string> readlinkCached(const std::string &path);
+#endif
std::optional<std::string> realpathCached(StringRef path, std::error_code &ec,
StringRef base = "",
bool baseIsResolved = false,
@@ -115,15 +117,14 @@ class DylibPathResolver {
private:
LibraryScanHelper &m_helper;
- std::optional<std::string> substOne(StringRef path, StringRef pattern,
+ std::string substOne(StringRef path, StringRef pattern,
StringRef replacement);
/// Apply all known loader substitutions to the path
- std::optional<std::string> substAll(StringRef path, StringRef loaderPath);
+ std::string substAll(StringRef path, StringRef loaderPath);
- std::optional<std::string> tryWithBasePaths(ArrayRef<StringRef> basePaths,
- StringRef stem,
- StringRef loaderPath);
+ std::optional<std::string> tryWithBasePath(StringRef basePath, StringRef stem,
+ StringRef loaderPath);
/// Try resolving the path using RPATH, searchPaths, and RUNPATH (in that
/// order)
@@ -138,6 +139,7 @@ class DylibPathResolver {
StringRef loaderPath);
std::optional<std::string> normalizeIfShared(StringRef path);
+ std::optional<std::string> normalize(StringRef path);
};
enum class PathKind : uint8_t { User, System, Unknown };
diff --git a/llvm/lib/ExecutionEngine/Orc/TargetProcess/LibraryScanner.cpp b/llvm/lib/ExecutionEngine/Orc/TargetProcess/LibraryScanner.cpp
index 9c443b8375f77..47251d3345eaa 100644
--- a/llvm/lib/ExecutionEngine/Orc/TargetProcess/LibraryScanner.cpp
+++ b/llvm/lib/ExecutionEngine/Orc/TargetProcess/LibraryScanner.cpp
@@ -105,20 +105,19 @@ bool isSharedLibrary(StringRef Path) {
return false;
}
-std::optional<std::string> DylibPathResolver::substOne(StringRef path,
- StringRef pattern,
- StringRef replacement) {
+std::string DylibPathResolver::substOne(StringRef path, StringRef pattern,
+ StringRef replacement) {
if (path.size() < pattern.size() || !path.starts_with_insensitive(pattern))
- return std::nullopt;
+ return path.str();
SmallString<256> result(replacement);
result.append(path.drop_front(pattern.size()));
- return normalizeIfShared(result);
+ return result.str().str();
}
-std::optional<std::string> DylibPathResolver::substAll(StringRef original,
- StringRef loaderPath) {
+std::string DylibPathResolver::substAll(StringRef original,
+ StringRef loaderPath) {
#ifdef __APPLE__
SmallString<256> mainExecutablePath(
@@ -134,12 +133,9 @@ std::optional<std::string> DylibPathResolver::substAll(StringRef original,
}
// Try @loader_path
- if (auto path = substOne(original, "@loader_path", loaderDir))
- return path;
-
+ std::string result_path = substOne(original, "@loader_path", loaderDir);
// Try @executable_path
- if (auto path = substOne(original, "@executable_path", mainExecutablePath))
- return path;
+ return substOne(result_path, "@executable_path", mainExecutablePath);
#else
SmallString<256> loaderDir;
@@ -151,31 +147,21 @@ std::optional<std::string> DylibPathResolver::substAll(StringRef original,
sys::path::remove_filename(loaderDir);
// Try $origin
- if (auto path = substOne(original, "$origin", loaderDir))
- return path;
+ return substOne(original, "$origin", loaderDir);
- // Optional: handle $lib or $platform later if needed
+ // Optional: handle $lib or $platform later if needed
#endif
-
- return std::nullopt;
}
std::optional<std::string>
-DylibPathResolver::tryWithBasePaths(ArrayRef<StringRef> basePaths,
- StringRef stem, StringRef loaderPath) {
- for (const auto &base : basePaths) {
- auto resolvedBaseOpt = substAll(base, loaderPath);
- if (!resolvedBaseOpt)
- continue;
+DylibPathResolver::tryWithBasePath(StringRef basePath, StringRef stem,
+ StringRef loaderPath) {
+ std::string resolvedBase = substAll(basePath, loaderPath);
- SmallString<256> fullPath(*resolvedBaseOpt);
- sys::path::append(fullPath, stem);
+ SmallString<256> fullPath(resolvedBase);
+ sys::path::append(fullPath, stem);
- if (auto norm = normalizeIfShared(fullPath)) {
- return norm;
- }
- }
- return std::nullopt;
+ return normalizeIfShared(fullPath);
}
std::optional<std::string>
@@ -183,18 +169,22 @@ DylibPathResolver::tryAllPaths(StringRef stem, ArrayRef<StringRef> RPath,
ArrayRef<StringRef> RunPath,
StringRef loaderPath) {
// Try RPATH
- if (auto found = tryWithBasePaths(RPath, stem, loaderPath))
- return found;
+ for (const auto &rpath : RPath) {
+ if (auto found = tryWithBasePath(rpath, stem, loaderPath))
+ return found;
+ }
// Try search paths (like LD_LIBRARY_PATH or configured)
- // for (const auto &entry : m_searchPaths) {
- // TODO.
- // }
+ for (const auto &entry : m_helper.getAllUnits()) {
+ if (auto found = tryWithBasePath(entry->basePath, stem, loaderPath))
+ return found;
+ }
// Try RUNPATH
- if (auto found = tryWithBasePaths(RunPath, stem, loaderPath))
- return found;
-
+ for (const auto &runpath : RunPath) {
+ if (auto found = tryWithBasePath(runpath, stem, loaderPath))
+ return found;
+ }
return std::nullopt;
}
@@ -215,8 +205,10 @@ std::optional<std::string> DylibPathResolver::tryWithExtensions(
// Optionally try "lib" prefix if not already there
StringRef filename = sys::path::filename(baseName);
+ StringRef base = sys::path::parent_path(baseName);
+ SmallString<256> withPrefix(base);
if (!filename.starts_with("lib")) {
- SmallString<256> withPrefix("lib");
+ withPrefix += "lib";
withPrefix += filename;
// Apply extension too
#if defined(__APPLE__)
@@ -226,7 +218,7 @@ std::optional<std::string> DylibPathResolver::tryWithExtensions(
#else
withPrefix += ".so";
#endif
- candidates.push_back(withPrefix);
+ candidates.push_back(withPrefix.str());
}
// Try all variants using tryAllPaths
@@ -238,17 +230,25 @@ std::optional<std::string> DylibPathResolver::tryWithExtensions(
return std::nullopt;
}
-std::optional<std::string>
-DylibPathResolver::normalizeIfShared(StringRef path) {
+std::optional<std::string> DylibPathResolver::normalize(StringRef path) {
std::error_code ec;
auto real = m_helper.getPathResolver().realpathCached(path, ec);
if (!real || ec)
return std::nullopt;
- if (!isSharedLibrary(*real))
+ return real;
+}
+
+std::optional<std::string>
+DylibPathResolver::normalizeIfShared(StringRef path) {
+ auto realOpt = normalize(path);
+ if (!realOpt)
return std::nullopt;
- return real;
+ if (!isSharedLibrary(*realOpt))
+ return std::nullopt;
+
+ return realOpt;
}
std::optional<std::string>
@@ -265,12 +265,12 @@ DylibPathResolver::resolve(StringRef libStem, SmallVector<StringRef, 2> RPath,
// On MacOS @rpath is preplaced by all paths in RPATH one by one.
if (libStem.starts_with_insensitive("@rpath")) {
for (auto &P : RPath) {
- if (auto norm = substOne(libStem, "@rpath", P))
+ if (auto norm = normalizeIfShared(substOne(libStem, "@rpath", P)))
return norm;
}
} else {
#endif
- if (auto norm = substAll(libStem, libLoader))
+ if (auto norm = normalizeIfShared(substAll(libStem, libLoader)))
return norm;
#ifdef __APPLE__
}
@@ -289,13 +289,7 @@ DylibPathResolver::resolve(StringRef libStem, SmallVector<StringRef, 2> RPath,
return std::nullopt;
}
-#ifdef _WIN32
-mode_t lstatCached(const std::string &path) { return 0; }
-std::optional<std::string> readlinkCached(const std::string &path) {
- return std::nullopt;
-}
-#else
-
+#ifndef _WIN32
mode_t PathResolver::lstatCached(const std::string &path) {
// If already cached - retun cached result
std::unique_lock<std::shared_mutex> lock(m_mutex);
@@ -335,7 +329,6 @@ PathResolver::readlinkCached(const std::string &path) {
}
return std::nullopt;
}
-#endif
void createComponent(StringRef Path, StringRef base_path, bool baseIsResolved,
SmallVector<StringRef, 16> &component) {
@@ -380,6 +373,7 @@ void normalizePathSegments(SmallVector<StringRef, 16> &pathParts) {
}
pathParts.swap(normalizedPath);
}
+#endif
std::optional<std::string> PathResolver::realpathCached(StringRef path,
std::error_code &ec,
@@ -430,9 +424,9 @@ std::optional<std::string> PathResolver::realpathCached(StringRef path,
// If result not in cache - call system function and cache result
+#ifndef _WIN32
StringRef Separator(sys::path::get_separator());
SmallString<256> resolved(Separator);
-#ifndef _WIN32
SmallVector<StringRef, 16> Components;
if (isRelative) {
>From c012916b63fb6878063f211b4e5e00c50ce87509 Mon Sep 17 00:00:00 2001
From: SahilPatidar <patidarsahil2001 at gmail.com>
Date: Sun, 13 Jul 2025 16:16:25 +0530
Subject: [PATCH 6/6] Minor fix related to windows
---
.../llvm/ExecutionEngine/Orc/TargetProcess/LibraryScanner.h | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/LibraryScanner.h b/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/LibraryScanner.h
index 09150c5779759..ee7efc9cc9f71 100644
--- a/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/LibraryScanner.h
+++ b/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/LibraryScanner.h
@@ -66,10 +66,12 @@ class LibraryPathCache {
std::error_code errnoCode;
};
- std::unordered_map<std::string, std::string> m_readlinkCache;
+ std::unordered_set<std::string> m_seen;
std::unordered_map<std::string, PathInfo> m_realpathCache;
+#ifndef _WIN32
+ std::unordered_map<std::string, std::string> m_readlinkCache;
std::unordered_map<std::string, mode_t> m_lstatCache;
- std::unordered_set<std::string> m_seen;
+#endif
};
/// Resolves file system paths with optional caching of results.
More information about the llvm-commits
mailing list