[llvm] [Offload] Try loading dynamic libraries at global scope first (PR #227020)

Joseph Huber via llvm-commits llvm-commits at lists.llvm.org
Mon Sep 28 08:55:50 PDT 2026


https://github.com/jhuber6 updated https://github.com/llvm/llvm-project/pull/227020

>From cde5fc6b964d016e5ce6bdc502d0ad4896b94123 Mon Sep 17 00:00:00 2001
From: Joseph Huber <huberjn at outlook.com>
Date: Mon, 28 Sep 2026 09:57:35 -0500
Subject: [PATCH] [Offload] Try loading dynamic libraries at global scope first

Summary:
The intention of the dynamic path is to provide basic linking in cases
where the SDK is not avaialble at the user's build time. Previously this
did a distinct `dlopen` call on the library.

However, in  cases where an existing HSA implementation exists at global
scope this can cause multiple instances to be open at the same time.
Specifically this is problematic if mixing with another implementation
(Like HIP or CUDA) or trying to intercept functions (like compiler-rt).
The compiler-rt interceptors currently carry interceptors into `dlsym`
when we should instead just load these at global scope, this makes them
behave more similar to a direct link in these scenarios, which was the
intention.

The fallback remains as the current behavior, should only trigger in
cases where there is an existing HSA in the process.
---
 .../amdgpu/dynamic_hsa/hsa.cpp                | 40 ++++++++++--------
 .../cuda/dynamic_cuda/cuda.cpp                | 41 +++++++++++--------
 2 files changed, 48 insertions(+), 33 deletions(-)

diff --git a/offload/plugins-nextgen/amdgpu/dynamic_hsa/hsa.cpp b/offload/plugins-nextgen/amdgpu/dynamic_hsa/hsa.cpp
index 6cb81f06dd9c4..13bd2fd064002 100644
--- a/offload/plugins-nextgen/amdgpu/dynamic_hsa/hsa.cpp
+++ b/offload/plugins-nextgen/amdgpu/dynamic_hsa/hsa.cpp
@@ -96,9 +96,32 @@ DLWRAP_FINALIZE()
 #define DEBUG_PREFIX "Target " GETNAME(TARGET_NAME) " RTL"
 #endif
 
+static bool resolveSymbols(llvm::sys::DynamicLibrary &Lib, const char *Name) {
+  for (size_t I = 0; I < dlwrap::size(); I++) {
+    const char *Sym = dlwrap::symbol(I);
+
+    void *P = Lib.getAddressOfSymbol(Sym);
+    if (P == nullptr) {
+      ODBG(OLDT_Init) << "Unable to find '" << Sym << "' in '" << Name << "'!";
+      return false;
+    }
+    ODBG(OLDT_Init) << "Implementing " << Sym << " with dlsym(" << Sym
+                    << ") -> " << P;
+
+    *dlwrap::pointer(I) = P;
+  }
+  return true;
+}
+
 static bool checkForHSA() {
   // return true if dlopen succeeded and all functions found
 
+  // Resolve through the process rather than the library handle so that
+  // definitions already in the global scope take precedence like a normal link.
+  auto Process = llvm::sys::DynamicLibrary::getPermanentLibrary(nullptr);
+  if (resolveSymbols(Process, "<process>"))
+    return true;
+
   const char *HsaLib = DYNAMIC_HSA_PATH ".1";
   std::string ErrMsg;
   auto DynlibHandle = std::make_unique<llvm::sys::DynamicLibrary>(
@@ -113,22 +136,7 @@ static bool checkForHSA() {
     return false;
   }
 
-  for (size_t I = 0; I < dlwrap::size(); I++) {
-    const char *Sym = dlwrap::symbol(I);
-
-    void *P = DynlibHandle->getAddressOfSymbol(Sym);
-    if (P == nullptr) {
-      ODBG(OLDT_Init) << "Unable to find '" << Sym << "' in '" << HsaLib
-                      << "'!";
-      return false;
-    }
-    ODBG(OLDT_Init) << "Implementing " << Sym << " with dlsym(" << Sym
-                    << ") -> " << P;
-
-    *dlwrap::pointer(I) = P;
-  }
-
-  return true;
+  return resolveSymbols(Process, HsaLib);
 }
 
 hsa_status_t hsa_init() {
diff --git a/offload/plugins-nextgen/cuda/dynamic_cuda/cuda.cpp b/offload/plugins-nextgen/cuda/dynamic_cuda/cuda.cpp
index 4c7c22da2f046..f8c46099b6f49 100644
--- a/offload/plugins-nextgen/cuda/dynamic_cuda/cuda.cpp
+++ b/offload/plugins-nextgen/cuda/dynamic_cuda/cuda.cpp
@@ -128,9 +128,7 @@ DLWRAP_FINALIZE()
 #define DEBUG_PREFIX "Target " GETNAME(TARGET_NAME) " RTL"
 #endif
 
-static bool checkForCUDA() {
-  // return true if dlopen succeeded and all functions found
-
+static bool resolveSymbols(llvm::sys::DynamicLibrary &Lib, const char *Name) {
   // Prefer _v2 versions of functions if found in the library
   std::unordered_map<std::string, const char *> TryFirst = {
       {"cuMemAlloc", "cuMemAlloc_v2"},
@@ -147,23 +145,13 @@ static bool checkForCUDA() {
       {"cuDevicePrimaryCtxSetFlags", "cuDevicePrimaryCtxSetFlags_v2"},
   };
 
-  const char *CudaLib = DYNAMIC_CUDA_PATH;
-  std::string ErrMsg;
-  auto DynlibHandle = std::make_unique<llvm::sys::DynamicLibrary>(
-      llvm::sys::DynamicLibrary::getPermanentLibrary(CudaLib, &ErrMsg));
-  if (!DynlibHandle->isValid()) {
-    ODBG(OLDT_Init) << "Unable to load library ' " << CudaLib << "': " << ErrMsg
-                    << "!";
-    return false;
-  }
-
   for (size_t I = 0; I < dlwrap::size(); I++) {
     const char *Sym = dlwrap::symbol(I);
 
     auto It = TryFirst.find(Sym);
     if (It != TryFirst.end()) {
       const char *First = It->second;
-      void *P = DynlibHandle->getAddressOfSymbol(First);
+      void *P = Lib.getAddressOfSymbol(First);
       if (P) {
         ODBG(OLDT_Init) << "Implementing " << Sym << " with dlsym(" << First
                         << ") -> " << P;
@@ -172,10 +160,9 @@ static bool checkForCUDA() {
       }
     }
 
-    void *P = DynlibHandle->getAddressOfSymbol(Sym);
+    void *P = Lib.getAddressOfSymbol(Sym);
     if (P == nullptr) {
-      ODBG(OLDT_Init) << "Unable to find '" << Sym << "' in '" << CudaLib
-                      << "'!";
+      ODBG(OLDT_Init) << "Unable to find '" << Sym << "' in '" << Name << "'!";
       return false;
     }
     ODBG(OLDT_Init) << "Implementing " << Sym << " with dlsym(" << Sym
@@ -187,6 +174,26 @@ static bool checkForCUDA() {
   return true;
 }
 
+static bool checkForCUDA() {
+  // Resolve through the process rather than the library handle so that
+  // definitions already in the global scope take precedence like a normal link.
+  auto Process = llvm::sys::DynamicLibrary::getPermanentLibrary(nullptr);
+  if (resolveSymbols(Process, "<process>"))
+    return true;
+
+  const char *CudaLib = DYNAMIC_CUDA_PATH;
+  std::string ErrMsg;
+  auto DynlibHandle = std::make_unique<llvm::sys::DynamicLibrary>(
+      llvm::sys::DynamicLibrary::getPermanentLibrary(CudaLib, &ErrMsg));
+  if (!DynlibHandle->isValid()) {
+    ODBG(OLDT_Init) << "Unable to load library ' " << CudaLib << "': " << ErrMsg
+                    << "!";
+    return false;
+  }
+
+  return resolveSymbols(Process, CudaLib);
+}
+
 CUresult cuInit(unsigned X) {
   // Note: Called exactly once from cuda rtl.cpp in a global constructor so
   // does not need to handle being called repeatedly or concurrently



More information about the llvm-commits mailing list