[llvm] [ORC] Add automatic shared library resolver for unresolved symbols. (PR #148410)

Vassil Vassilev via llvm-commits llvm-commits at lists.llvm.org
Wed Aug 13 23:34:16 PDT 2025


================
@@ -0,0 +1,476 @@
+//===- LibraryResolver.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 <atomic>
+#include <shared_mutex>
+#include <unordered_map>
+
+namespace llvm {
+namespace orc {
+
+/// 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 };
+
+  class LibraryInfo {
+  public:
+    LibraryInfo(const LibraryInfo &) = delete;
+    LibraryInfo &operator=(const LibraryInfo &) = delete;
+
+    LibraryInfo(std::string filePath, State s, PathType 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<std::shared_mutex> lock(mutex);
+      if (filter)
+        return false;
+      filter.emplace(std::move(F));
+      return true;
+    }
+
+    bool ensureFilterBuilt(const BloomFilterBuilder &FB,
+                           ArrayRef<StringRef> Symbols) {
+      std::lock_guard<std::shared_mutex> lock(mutex);
+      if (filter)
+        return false;
+      filter.emplace(FB.build(Symbols));
+      return true;
+    }
+
+    bool mayContain(StringRef symbol) const {
+      assert(hasFilter());
+      std::shared_lock<std::shared_mutex> lock(mutex);
+      return filter->mayContain(symbol);
+    }
+
+    bool hasFilter() const {
+      std::shared_lock<std::shared_mutex> lock(mutex);
+      return filter.has_value();
+    }
+
+    State getState() const { return state.load(); }
+    PathType 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;
+    PathType kind;
+    std::optional<BloomFilter> filter;
+    mutable std::shared_mutex mutex;
+  };
+
+  class FilteredView {
+  public:
+    using Map = StringMap<std::shared_ptr<LibraryInfo>>;
+    using Iterator = typename Map::const_iterator;
+    class FilterIterator {
+    public:
+      FilterIterator(Iterator _it, Iterator _end, State s, PathType 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() {
+        for (; it != end; ++it)
+          if (it->second->getState() == state && it->second->getKind() == kind)
+            break;
+      }
+      Iterator it, end;
+      State state;
+      PathType kind;
+    };
+    FilteredView(Iterator begin, Iterator end, State s, PathType 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_;
+    PathType kind_;
+  };
+
+private:
+  StringMap<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, PathType kind,
+                  std::optional<BloomFilter> filter = std::nullopt) {
+    std::unique_lock<std::shared_mutex> lock(mutex);
+    if (libraries.count(path) > 0)
+      return false;
+    libraries.insert({std::move(path),
+                      std::make_shared<LibraryInfo>(path, State::Unloaded, kind,
+                                                    std::move(filter))});
+    return true;
+  }
+
+  bool hasLibrary(StringRef path) const {
+    std::shared_lock<std::shared_mutex> lock(mutex);
+    if (libraries.count(path) > 0)
+      return true;
+    return false;
+  }
+
+  bool removeLibrary(StringRef path) {
+    std::unique_lock<std::shared_mutex> lock(mutex);
+    auto I = libraries.find(path);
+    if (I == libraries.end())
+      return false;
+    libraries.erase(I);
+    return true;
+  }
+
+  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(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(StringRef 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, PathType k) const {
+    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<std::shared_mutex> lock(mutex);
+    for (const auto &[_, entry] : libraries) {
+      if (!visitor(*entry))
+        break;
+    }
+  }
+
+  bool isLoaded(StringRef path) const {
+    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<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<std::shared_mutex> lock(mutex);
+    libraries.clear();
+  }
+};
+
+using LibraryInfo = LibraryManager::LibraryInfo;
+
+struct SearchPlanEntry {
+  LibraryManager::State state; // Loaded, Queried, Unloaded
+  PathType type;               // User, System
+};
+
+struct SearchPolicy {
+  std::vector<SearchPlanEntry> plan;
+
+  static SearchPolicy defaultPlan() {
+    return {{{LibraryManager::State::Loaded, PathType::User},
+             {LibraryManager::State::Queried, PathType::User},
+             {LibraryManager::State::Unloaded, PathType::User},
+             {LibraryManager::State::Loaded, PathType::System},
+             {LibraryManager::State::Queried, PathType::System},
+             {LibraryManager::State::Unloaded, PathType::System}}};
+  }
+};
+
+/// 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 LibraryResolver {
+  friend class LibraryResolutionDriver;
+
+public:
+  class SymbolEnumerator {
+  public:
+    enum class EnumerateResult { Continue, Stop, Error };
+
+    using OnEachSymbolFn = std::function<EnumerateResult(StringRef Sym)>;
+
+    enum Filter : uint32_t {
+      None = 0,
+      IgnoreUndefined = 1 << 0,
+      IgnoreWeak = 1 << 1,
+      IgnoreIndirect = 1 << 2,
+      IgnoreHidden = 1 << 3,
+      IgnoreNonGlobal = 1 << 4
+    };
+
+    struct Options {
+      static uint32_t defaultFlag() {
+        return SymbolEnumerator::Filter::IgnoreUndefined |
+               SymbolEnumerator::Filter::IgnoreWeak |
+               SymbolEnumerator::Filter::IgnoreIndirect;
+      }
+      uint32_t FilterFlags = Filter::None;
+    };
+
+    static bool enumerateSymbols(StringRef Path, OnEachSymbolFn OnEach,
+                                 const Options &Opts);
+  };
+
+  class SymbolQuery {
----------------
vgvassilev wrote:

We will need some documentation here, too.

https://github.com/llvm/llvm-project/pull/148410


More information about the llvm-commits mailing list