[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