[compiler-rt] [compiler-rt] Expose shared DSO helpers for compiler-rt runtimes (PR #191098)

Yaxun Liu via llvm-commits llvm-commits at lists.llvm.org
Thu Apr 9 07:05:48 PDT 2026


https://github.com/yxsamliu updated https://github.com/llvm/llvm-project/pull/191098

>From e1d2be980504e7189aae1d03babd0c35b21b2d1e Mon Sep 17 00:00:00 2001
From: "Yaxun (Sam) Liu" <yaxun.liu at amd.com>
Date: Wed, 8 Apr 2026 10:47:23 -0400
Subject: [PATCH] [compiler-rt] Expose shared DSO helpers for compiler-rt
 runtimes

The motivation of this PR is to refactor and expose DSO helper functions so
they can be used by all compiler-rt libraries, including the profile library,
without duplicating dlopen/dlsym (non-Windows) or LoadLibrary/GetProcAddress
(Windows) logic in each runtime.

Implement the helpers in namespace __interception in interception_linux.cpp for
non-Windows targets and interception_win.cpp for Windows, and use them from the
existing Linux interception path for RTLD_NEXT/RTLD_DEFAULT/dlvsym lookups.

This is NFC for existing libraries that already use interception's public APIs;
sanitizer and interception lit behavior is unchanged.
---
 compiler-rt/lib/interception/interception.h   | 10 ++++
 .../lib/interception/interception_linux.cpp   | 59 +++++++++++++++++--
 .../lib/interception/interception_win.cpp     | 24 ++++++++
 .../tests/interception_linux_test.cpp         | 11 ++++
 4 files changed, 99 insertions(+), 5 deletions(-)

diff --git a/compiler-rt/lib/interception/interception.h b/compiler-rt/lib/interception/interception.h
index 9fe7d3db308bf..71c59ed36b4ad 100644
--- a/compiler-rt/lib/interception/interception.h
+++ b/compiler-rt/lib/interception/interception.h
@@ -362,6 +362,16 @@ const interpose_substitution substitution_##func_name[]             \
 // so we use casts via uintptr_t (the local __sanitizer::uptr equivalent).
 namespace __interception {
 
+// Dynamic library loading helpers (dlopen/LoadLibrary, dlsym/GetProcAddress).
+// Implemented in interception_linux.cpp on non-Windows targets and
+// interception_win.cpp on Windows.
+bool DynamicLoaderAvailable();
+void* OpenLibrary(const char* name);
+void* LookupSymbol(void* handle, const char* symbol);
+void* LookupSymbolDefault(const char* symbol);
+void* LookupSymbolNext(const char* symbol);
+void* LookupSymbolNextVersioned(const char* symbol, const char* version);
+
 #if defined(__ELF__) && !SANITIZER_FUCHSIA
 // The use of interceptors makes many sanitizers unusable for static linking.
 // Define a function, if called, will cause a linker error (undefined _DYNAMIC).
diff --git a/compiler-rt/lib/interception/interception_linux.cpp b/compiler-rt/lib/interception/interception_linux.cpp
index f900ae6a593bd..9b9796f7cac47 100644
--- a/compiler-rt/lib/interception/interception_linux.cpp
+++ b/compiler-rt/lib/interception/interception_linux.cpp
@@ -9,15 +9,64 @@
 // This file is a part of AddressSanitizer, an address sanity checker.
 //
 // Linux-specific interception methods.
+//
+// POSIX dynamic-library helpers (dlopen / dlsym) live here for every
+// non-Windows interception target (Linux, *BSD, Darwin, AIX, Fuchsia, …).
+// macOS/AIX/Fuchsia compile this TU for RTInterception but do not use the
+// Linux-specific InterceptFunction helpers below.
 //===----------------------------------------------------------------------===//
 
 #include "interception.h"
 
+#if !SANITIZER_WINDOWS
+
+#  include <dlfcn.h>
+
+#  pragma weak dlopen
+#  pragma weak dlsym
+#  pragma weak dlvsym
+
+namespace __interception {
+
+bool DynamicLoaderAvailable() { return dlopen != nullptr && dlsym != nullptr; }
+
+void* OpenLibrary(const char* name) {
+  if (!DynamicLoaderAvailable())
+    return nullptr;
+  return dlopen(name, RTLD_LAZY | RTLD_LOCAL);
+}
+
+void* LookupSymbol(void* handle, const char* symbol) {
+  if (!DynamicLoaderAvailable())
+    return nullptr;
+  return dlsym(handle, symbol);
+}
+
+void* LookupSymbolDefault(const char* symbol) {
+  if (!DynamicLoaderAvailable())
+    return nullptr;
+  return dlsym(RTLD_DEFAULT, symbol);
+}
+
+void* LookupSymbolNext(const char* symbol) {
+  if (!DynamicLoaderAvailable())
+    return nullptr;
+  return dlsym(RTLD_NEXT, symbol);
+}
+
+void* LookupSymbolNextVersioned(const char* symbol, const char* version) {
+  if (!DynamicLoaderAvailable() || dlvsym == nullptr)
+    return nullptr;
+  return dlvsym(RTLD_NEXT, symbol, version);
+}
+
+}  // namespace __interception
+
+#endif  // !SANITIZER_WINDOWS
+
 #if SANITIZER_LINUX || SANITIZER_FREEBSD || SANITIZER_NETBSD || \
     SANITIZER_SOLARIS || SANITIZER_HAIKU
 
-#include <dlfcn.h>   // for dlsym() and dlvsym()
-
 namespace __interception {
 
 #if SANITIZER_NETBSD
@@ -39,14 +88,14 @@ static void *GetFuncAddr(const char *name, uptr trampoline) {
   if (StrCmp(name, "sigaction"))
     name = "__sigaction14";
 #endif
-  void *addr = dlsym(RTLD_NEXT, name);
+  void* addr = LookupSymbolNext(name);
   if (!addr) {
     // If the lookup using RTLD_NEXT failed, the sanitizer runtime library is
     // later in the library search order than the DSO that we are trying to
     // intercept, which means that we cannot intercept this function. We still
     // want the address of the real definition, though, so look it up using
     // RTLD_DEFAULT.
-    addr = dlsym(RTLD_DEFAULT, name);
+    addr = LookupSymbolDefault(name);
 
     // In case `name' is not loaded, dlsym ends up finding the actual wrapper.
     // We don't want to intercept the wrapper and have it point to itself.
@@ -66,7 +115,7 @@ bool InterceptFunction(const char *name, uptr *ptr_to_real, uptr func,
 // dlvsym is a GNU extension supported by some other platforms.
 #if SANITIZER_GLIBC || SANITIZER_FREEBSD || SANITIZER_NETBSD
 static void *GetFuncAddr(const char *name, const char *ver) {
-  return dlvsym(RTLD_NEXT, name, ver);
+  return LookupSymbolNextVersioned(name, ver);
 }
 
 bool InterceptFunction(const char *name, const char *ver, uptr *ptr_to_real,
diff --git a/compiler-rt/lib/interception/interception_win.cpp b/compiler-rt/lib/interception/interception_win.cpp
index 856872425117a..8b95c5b90d12a 100644
--- a/compiler-rt/lib/interception/interception_win.cpp
+++ b/compiler-rt/lib/interception/interception_win.cpp
@@ -134,6 +134,30 @@
 
 namespace __interception {
 
+bool DynamicLoaderAvailable() { return true; }
+
+void* OpenLibrary(const char* name) {
+  if (!name)
+    return reinterpret_cast<void*>(GetModuleHandleA(nullptr));
+  return reinterpret_cast<void*>(LoadLibraryA(name));
+}
+
+void* LookupSymbol(void* handle, const char* symbol) {
+  if (!handle)
+    return nullptr;
+  return reinterpret_cast<void*>(reinterpret_cast<__sanitizer::uptr>(
+      GetProcAddress(reinterpret_cast<HMODULE>(handle), symbol)));
+}
+
+void* LookupSymbolDefault(const char* symbol) {
+  return LookupSymbol(reinterpret_cast<void*>(GetModuleHandleA(nullptr)),
+                      symbol);
+}
+
+void* LookupSymbolNext(const char*) { return nullptr; }
+
+void* LookupSymbolNextVersioned(const char*, const char*) { return nullptr; }
+
 static const int kAddressLength = FIRST_32_SECOND_64(4, 8);
 static const int kJumpInstructionLength = 5;
 static const int kShortJumpInstructionLength = 2;
diff --git a/compiler-rt/lib/interception/tests/interception_linux_test.cpp b/compiler-rt/lib/interception/tests/interception_linux_test.cpp
index c204274fb3297..0b61f499ba255 100644
--- a/compiler-rt/lib/interception/tests/interception_linux_test.cpp
+++ b/compiler-rt/lib/interception/tests/interception_linux_test.cpp
@@ -93,6 +93,17 @@ TEST(Interception, Basic) {
   EXPECT_EQ(0, isdigit_called);
 }
 
+TEST(Interception, DsoHelpers) {
+  EXPECT_TRUE(DynamicLoaderAvailable());
+
+  void* self = OpenLibrary(nullptr);
+  EXPECT_NE(nullptr, self);
+  EXPECT_NE(nullptr, LookupSymbol(self, "malloc"));
+  EXPECT_NE(nullptr, LookupSymbolDefault("malloc"));
+  EXPECT_NE(nullptr, LookupSymbolNext("malloc"));
+  EXPECT_EQ(nullptr, LookupSymbol(self, "symbol_that_does_not_exist__"));
+}
+
 TEST(Interception, ForeignOverrideDirect) {
   // Actual interceptor is overridden.
   EXPECT_FALSE(INTERCEPT_FUNCTION(isalpha));



More information about the llvm-commits mailing list