[llvm] 513f89d - Add functionality to load dynamic libraries temporarily
Stella Stamenova via llvm-commits
llvm-commits at lists.llvm.org
Mon Oct 3 16:20:59 PDT 2022
Author: Michael Holman
Date: 2022-10-03T16:20:22-07:00
New Revision: 513f89dc8d894df7d72460943012c3488f2aeec7
URL: https://github.com/llvm/llvm-project/commit/513f89dc8d894df7d72460943012c3488f2aeec7
DIFF: https://github.com/llvm/llvm-project/commit/513f89dc8d894df7d72460943012c3488f2aeec7.diff
LOG: Add functionality to load dynamic libraries temporarily
Previously, it was possible to load dynamic libraries which would be unloaded on llvm_shutdown(), but recently ManagedStatic removal changed this so that loaded libraries really can't ever be unloaded. This functionality was very useful, and so to add it back in a more explicit way, I've added new getLibrary() and closeLibrary() methods to allow callers to use the very convenient platform independent abstraction that LLVM has for dynamic libraries.
As a specific use case, the onnx-mlir project was using this functionality with an API that allows instancing LLVM so you can compile a shared library, and then load that library, and eventually close the instance (and library) and compile something else. This change to llvm_shutdown causes libraries to leak and also locks the libraries for the entire duration of the program which prevents reusing library names.
Reviewed By: lhames
Differential Revision: https://reviews.llvm.org/D134763
Added:
Modified:
llvm/include/llvm/Support/DynamicLibrary.h
llvm/lib/Support/DynamicLibrary.cpp
Removed:
################################################################################
diff --git a/llvm/include/llvm/Support/DynamicLibrary.h b/llvm/include/llvm/Support/DynamicLibrary.h
index 95d5ba281e22..0771606a75f5 100644
--- a/llvm/include/llvm/Support/DynamicLibrary.h
+++ b/llvm/include/llvm/Support/DynamicLibrary.h
@@ -21,110 +21,129 @@ class StringRef;
namespace sys {
- /// This class provides a portable interface to dynamic libraries which also
- /// might be known as shared libraries, shared objects, dynamic shared
- /// objects, or dynamic link libraries. Regardless of the terminology or the
- /// operating system interface, this class provides a portable interface that
- /// allows dynamic libraries to be loaded and searched for externally
- /// defined symbols. This is typically used to provide "plug-in" support.
- /// It also allows for symbols to be defined which don't live in any library,
- /// but rather the main program itself, useful on Windows where the main
- /// executable cannot be searched.
+/// This class provides a portable interface to dynamic libraries which also
+/// might be known as shared libraries, shared objects, dynamic shared
+/// objects, or dynamic link libraries. Regardless of the terminology or the
+/// operating system interface, this class provides a portable interface that
+/// allows dynamic libraries to be loaded and searched for externally
+/// defined symbols. This is typically used to provide "plug-in" support.
+/// It also allows for symbols to be defined which don't live in any library,
+/// but rather the main program itself, useful on Windows where the main
+/// executable cannot be searched.
+class DynamicLibrary {
+ // Placeholder whose address represents an invalid library.
+ // We use this instead of NULL or a pointer-int pair because the OS library
+ // might define 0 or 1 to be "special" handles, such as "search all".
+ static char Invalid;
+
+ // Opaque data used to interface with OS-specific dynamic library handling.
+ void *Data;
+
+public:
+ explicit DynamicLibrary(void *data = &Invalid) : Data(data) {}
+
+ /// Returns true if the object refers to a valid library.
+ bool isValid() const { return Data != &Invalid; }
+
+ /// Searches through the library for the symbol \p symbolName. If it is
+ /// found, the address of that symbol is returned. If not, NULL is returned.
+ /// Note that NULL will also be returned if the library failed to load.
+ /// Use isValid() to distinguish these cases if it is important.
+ /// Note that this will \e not search symbols explicitly registered by
+ /// AddSymbol().
+ void *getAddressOfSymbol(const char *symbolName);
+
+ /// This function permanently loads the dynamic library at the given path
+ /// using the library load operation from the host operating system. The
+ /// library instance will only be closed when global destructors run, and
+ /// there is no guarantee when the library will be unloaded.
///
- /// Note: there is currently no interface for temporarily loading a library,
- /// or for unloading libraries when the LLVM library is unloaded.
- class DynamicLibrary {
- // Placeholder whose address represents an invalid library.
- // We use this instead of NULL or a pointer-int pair because the OS library
- // might define 0 or 1 to be "special" handles, such as "search all".
- static char Invalid;
-
- // Opaque data used to interface with OS-specific dynamic library handling.
- void *Data;
-
- public:
- explicit DynamicLibrary(void *data = &Invalid) : Data(data) {}
-
- /// Returns true if the object refers to a valid library.
- bool isValid() const { return Data != &Invalid; }
-
- /// Searches through the library for the symbol \p symbolName. If it is
- /// found, the address of that symbol is returned. If not, NULL is returned.
- /// Note that NULL will also be returned if the library failed to load.
- /// Use isValid() to distinguish these cases if it is important.
- /// Note that this will \e not search symbols explicitly registered by
- /// AddSymbol().
- void *getAddressOfSymbol(const char *symbolName);
-
- /// This function permanently loads the dynamic library at the given path.
- /// The library will only be unloaded when llvm_shutdown() is called.
- /// This returns a valid DynamicLibrary instance on success and an invalid
- /// instance on failure (see isValid()). \p *errMsg will only be modified
- /// if the library fails to load.
- ///
- /// It is safe to call this function multiple times for the same library.
- /// Open a dynamic library permanently.
- static DynamicLibrary getPermanentLibrary(const char *filename,
- std::string *errMsg = nullptr);
-
- /// Registers an externally loaded library. The library will be unloaded
- /// when the program terminates.
- ///
- /// It is safe to call this function multiple times for the same library,
- /// though ownership is only taken if there was no error.
- ///
- /// \returns An empty \p DynamicLibrary if the library was already loaded.
- static DynamicLibrary addPermanentLibrary(void *handle,
- std::string *errMsg = nullptr);
-
- /// This function permanently loads the dynamic library at the given path.
- /// Use this instead of getPermanentLibrary() when you won't need to get
- /// symbols from the library itself.
- ///
- /// It is safe to call this function multiple times for the same library.
- static bool LoadLibraryPermanently(const char *Filename,
- std::string *ErrMsg = nullptr) {
- return !getPermanentLibrary(Filename, ErrMsg).isValid();
- }
-
- enum SearchOrdering {
- /// SO_Linker - Search as a call to dlsym(dlopen(NULL)) would when
- /// DynamicLibrary::getPermanentLibrary(NULL) has been called or
- /// search the list of explcitly loaded symbols if not.
- SO_Linker,
- /// SO_LoadedFirst - Search all loaded libraries, then as SO_Linker would.
- SO_LoadedFirst,
- /// SO_LoadedLast - Search as SO_Linker would, then loaded libraries.
- /// Only useful to search if libraries with RTLD_LOCAL have been added.
- SO_LoadedLast,
- /// SO_LoadOrder - Or this in to search libraries in the ordered loaded.
- /// The default bahaviour is to search loaded libraries in reverse.
- SO_LoadOrder = 4
- };
- static SearchOrdering SearchOrder; // = SO_Linker
-
- /// This function will search through all previously loaded dynamic
- /// libraries for the symbol \p symbolName. If it is found, the address of
- /// that symbol is returned. If not, null is returned. Note that this will
- /// search permanently loaded libraries (getPermanentLibrary()) as well
- /// as explicitly registered symbols (AddSymbol()).
- /// @throws std::string on error.
- /// Search through libraries for address of a symbol
- static void *SearchForAddressOfSymbol(const char *symbolName);
-
- /// Convenience function for C++ophiles.
- static void *SearchForAddressOfSymbol(const std::string &symbolName) {
- return SearchForAddressOfSymbol(symbolName.c_str());
- }
-
- /// This functions permanently adds the symbol \p symbolName with the
- /// value \p symbolValue. These symbols are searched before any
- /// libraries.
- /// Add searchable symbol/value pair.
- static void AddSymbol(StringRef symbolName, void *symbolValue);
-
- class HandleSet;
+ /// This returns a valid DynamicLibrary instance on success and an invalid
+ /// instance on failure (see isValid()). \p *errMsg will only be modified if
+ /// the library fails to load.
+ ///
+ /// It is safe to call this function multiple times for the same library.
+ /// Open a dynamic library permanently.
+ static DynamicLibrary getPermanentLibrary(const char *filename,
+ std::string *errMsg = nullptr);
+
+ /// Registers an externally loaded library. The library will be unloaded
+ /// when the program terminates.
+ ///
+ /// It is safe to call this function multiple times for the same library,
+ /// though ownership is only taken if there was no error.
+ static DynamicLibrary addPermanentLibrary(void *handle,
+ std::string *errMsg = nullptr);
+
+ /// This function permanently loads the dynamic library at the given path.
+ /// Use this instead of getPermanentLibrary() when you won't need to get
+ /// symbols from the library itself.
+ ///
+ /// It is safe to call this function multiple times for the same library.
+ static bool LoadLibraryPermanently(const char *Filename,
+ std::string *ErrMsg = nullptr) {
+ return !getPermanentLibrary(Filename, ErrMsg).isValid();
+ }
+
+ /// This function loads the dynamic library at the given path, using the
+ /// library load operation from the host operating system. The library
+ /// instance will be closed when closeLibrary is called or global destructors
+ /// are run, but there is no guarantee when the library will be unloaded.
+ ///
+ /// This returns a valid DynamicLibrary instance on success and an invalid
+ /// instance on failure (see isValid()). \p *Err will only be modified if the
+ /// library fails to load.
+ ///
+ /// It is safe to call this function multiple times for the same library.
+ static DynamicLibrary getLibrary(const char *FileName,
+ std::string *Err = nullptr);
+
+ /// This function closes the dynamic library at the given path, using the
+ /// library close operation of the host operating system, and there is no
+ /// guarantee if or when this will cause the the library to be unloaded.
+ ///
+ /// This function should be called only if the library was loaded using the
+ /// getLibrary() function.
+ static void closeLibrary(DynamicLibrary &Lib);
+
+ enum SearchOrdering {
+ /// SO_Linker - Search as a call to dlsym(dlopen(NULL)) would when
+ /// DynamicLibrary::getPermanentLibrary(NULL) has been called or
+ /// search the list of explcitly loaded symbols if not.
+ SO_Linker,
+ /// SO_LoadedFirst - Search all loaded libraries, then as SO_Linker would.
+ SO_LoadedFirst,
+ /// SO_LoadedLast - Search as SO_Linker would, then loaded libraries.
+ /// Only useful to search if libraries with RTLD_LOCAL have been added.
+ SO_LoadedLast,
+ /// SO_LoadOrder - Or this in to search libraries in the ordered loaded.
+ /// The default bahaviour is to search loaded libraries in reverse.
+ SO_LoadOrder = 4
};
+ static SearchOrdering SearchOrder; // = SO_Linker
+
+ /// This function will search through all previously loaded dynamic
+ /// libraries for the symbol \p symbolName. If it is found, the address of
+ /// that symbol is returned. If not, null is returned. Note that this will
+ /// search permanently loaded libraries (getPermanentLibrary()) as well
+ /// as explicitly registered symbols (AddSymbol()).
+ /// @throws std::string on error.
+ /// Search through libraries for address of a symbol
+ static void *SearchForAddressOfSymbol(const char *symbolName);
+
+ /// Convenience function for C++ophiles.
+ static void *SearchForAddressOfSymbol(const std::string &symbolName) {
+ return SearchForAddressOfSymbol(symbolName.c_str());
+ }
+
+ /// This functions permanently adds the symbol \p symbolName with the
+ /// value \p symbolValue. These symbols are searched before any
+ /// libraries.
+ /// Add searchable symbol/value pair.
+ static void AddSymbol(StringRef symbolName, void *symbolValue);
+
+ class HandleSet;
+};
} // End sys namespace
} // End llvm namespace
diff --git a/llvm/lib/Support/DynamicLibrary.cpp b/llvm/lib/Support/DynamicLibrary.cpp
index 319fbc59d43b..531c035ab926 100644
--- a/llvm/lib/Support/DynamicLibrary.cpp
+++ b/llvm/lib/Support/DynamicLibrary.cpp
@@ -41,13 +41,16 @@ class DynamicLibrary::HandleSet {
return Handle == Process || Find(Handle) != Handles.end();
}
- bool AddLibrary(void *Handle, bool IsProcess = false, bool CanClose = true) {
+ bool AddLibrary(void *Handle, bool IsProcess = false, bool CanClose = true,
+ bool AllowDuplicates = false) {
#ifdef _WIN32
assert((Handle == this ? IsProcess : !IsProcess) && "Bad Handle.");
#endif
+ assert((!AllowDuplicates || !CanClose) &&
+ "CanClose must be false if AllowDuplicates is true.");
if (LLVM_LIKELY(!IsProcess)) {
- if (Find(Handle) != Handles.end()) {
+ if (!AllowDuplicates && Find(Handle) != Handles.end()) {
if (CanClose)
DLClose(Handle);
return false;
@@ -67,6 +70,14 @@ class DynamicLibrary::HandleSet {
return true;
}
+ void CloseLibrary(void *Handle) {
+ DLClose(Handle);
+ HandleList::iterator it = Find(Handle);
+ if (it != Handles.end()) {
+ Handles.erase(it);
+ }
+ }
+
void *LibLookup(const char *Symbol, DynamicLibrary::SearchOrdering Order) {
if (Order & SO_LoadOrder) {
for (void *Handle : Handles) {
@@ -111,9 +122,10 @@ struct Globals {
// Collection of symbol name/value pairs to be searched prior to any
// libraries.
llvm::StringMap<void *> ExplicitSymbols;
- // Collection of known library handles.
+ // Collections of known library handles.
DynamicLibrary::HandleSet OpenedHandles;
- // Lock for ExplicitSymbols and OpenedHandles.
+ DynamicLibrary::HandleSet OpenedTemporaryHandles;
+ // Lock for ExplicitSymbols, OpenedHandles, and OpenedTemporaryHandles.
llvm::sys::SmartMutex<true> SymbolsMutex;
};
@@ -174,6 +186,29 @@ DynamicLibrary DynamicLibrary::addPermanentLibrary(void *Handle,
return DynamicLibrary(Handle);
}
+DynamicLibrary DynamicLibrary::getLibrary(const char *FileName,
+ std::string *Err) {
+ assert(FileName && "Use getPermanentLibrary() for opening process handle");
+ void *Handle = HandleSet::DLOpen(FileName, Err);
+ if (Handle != &Invalid) {
+ auto &G = getGlobals();
+ SmartScopedLock<true> Lock(G.SymbolsMutex);
+ G.OpenedTemporaryHandles.AddLibrary(Handle, /*IsProcess*/ false,
+ /*CanClose*/ false,
+ /*AllowDuplicates*/ true);
+ }
+ return DynamicLibrary(Handle);
+}
+
+void DynamicLibrary::closeLibrary(DynamicLibrary &Lib) {
+ auto &G = getGlobals();
+ SmartScopedLock<true> Lock(G.SymbolsMutex);
+ if (Lib.isValid()) {
+ G.OpenedTemporaryHandles.CloseLibrary(Lib.Data);
+ Lib.Data = &Invalid;
+ }
+}
+
void *DynamicLibrary::getAddressOfSymbol(const char *SymbolName) {
if (!isValid())
return nullptr;
@@ -194,6 +229,8 @@ void *DynamicLibrary::SearchForAddressOfSymbol(const char *SymbolName) {
// Now search the libraries.
if (void *Ptr = G.OpenedHandles.Lookup(SymbolName, SearchOrder))
return Ptr;
+ if (void *Ptr = G.OpenedTemporaryHandles.Lookup(SymbolName, SearchOrder))
+ return Ptr;
}
return llvm::SearchForAddressOfSpecialSymbol(SymbolName);
More information about the llvm-commits
mailing list