From f584ec97d09789853f8c7df3dccfa7768223ae24 Mon Sep 17 00:00:00 2001
From: Alexey Matveichev <>
Date: Wed, 14 Jun 2023 10:45:45 +0200
Subject: [PATCH] ENH: reimplement Foam::dlOpen() for macOS (#2801)

- The Apple SIP (System Integrity Protection) clears environment
  variables, which affects the behaviour of dynamic library loading
  (the DYLD_LIBRARY_PATH env variable).

  OpenFOAM shadows this variable as FOAM_LD_LIBRARY_PATH, which has
  been used to restore DYLD_LIBRARY_PATH (eg, in RunFunctions script).

  However, this solution is not quite complete, as it
    (a) requires sourcing of RunFunctions file,
    (b) additional errors appear depending on a user workflow.

  This changeset alleviates the problem by also iterating through
  paths stored in the shadow variable when loading dynamic libraries
  (if the DYLD_LIBRARY_PATH is empty).
---
 bin/foamJob                  |  7 ----
 bin/tools/RunFunctions       |  7 ----
 src/OSspecific/POSIX/POSIX.C | 67 +++++++++++++++++++++++++++++-------
 3 files changed, 54 insertions(+), 27 deletions(-)

diff --git a/bin/foamJob b/bin/foamJob
index 75a963e93cd..6221eb67dff 100755
--- a/bin/foamJob
+++ b/bin/foamJob
@@ -23,13 +23,6 @@
 # If dispatching via foamExec
 foamExec="$WM_PROJECT_DIR/bin/tools/foamExec"
 
-# Darwin workaround - SIP clearing DYLD_LIBRARY_PATH variable
-if [ -n "$FOAM_LD_LIBRARY_PATH" ] && [ -z "$DYLD_LIBRARY_PATH" ]
-then
-    export DYLD_LIBRARY_PATH="$FOAM_LD_LIBRARY_PATH"
-fi
-
-
 #------------------------------------------------------------------------------
 
 usage() {
diff --git a/bin/tools/RunFunctions b/bin/tools/RunFunctions
index f8482f63f29..68364b7738b 100644
--- a/bin/tools/RunFunctions
+++ b/bin/tools/RunFunctions
@@ -25,13 +25,6 @@
 # Basic sanity checks
 [ -d "$FOAM_TUTORIALS" ] || echo "No OpenFOAM tutorials? : $FOAM_TUTORIALS" 1>&2
 
-# Darwin workaround - SIP clearing DYLD_LIBRARY_PATH variable
-if [ -n "$FOAM_LD_LIBRARY_PATH" ] && [ -z "$DYLD_LIBRARY_PATH" ]
-then
-    export DYLD_LIBRARY_PATH="$FOAM_LD_LIBRARY_PATH"
-fi
-
-
 #------------------------------------------------------------------------------
 
 #
diff --git a/src/OSspecific/POSIX/POSIX.C b/src/OSspecific/POSIX/POSIX.C
index 826ac470215..dffd5e0a540 100644
--- a/src/OSspecific/POSIX/POSIX.C
+++ b/src/OSspecific/POSIX/POSIX.C
@@ -41,6 +41,7 @@ Description
 #include "timer.H"
 #include "DynamicList.H"
 #include "CStringList.H"
+#include "stringOps.H"
 #include "IOstreams.H"
 #include "Pstream.H"
 
@@ -108,6 +109,52 @@ static inline void redirects(const bool bg)
 }
 
 
+// Library loading is normally simply via dlopen(),
+// but SIP (System Integrity Protection) on Apple will generally
+// clear out the DYLD_LIBRARY_PATH set from shell scripts.
+// We thus have FOAM_LD_LIBRARY_PATH as a shadow parameter and use
+// that to attempt loading ourselves
+static inline void* loadLibrary(const Foam::fileName& libName)
+{
+    constexpr int ldflags = (RTLD_LAZY|RTLD_GLOBAL);
+
+#ifdef __APPLE__
+    const char* normal = nullptr;
+    const char* shadow = nullptr;
+
+    if
+    (
+        !libName.isAbsolute()
+     && ((normal = ::getenv("DYLD_LIBRARY_PATH"))    == nullptr || !*normal)
+     && ((shadow = ::getenv("FOAM_LD_LIBRARY_PATH")) != nullptr && *shadow)
+    )
+    {
+        // SIP appears to have cleared DYLD_LIBRARY_PATH but the
+        // shadow parameter is available
+
+        const Foam::string ldPaths(shadow);
+        const auto paths = Foam::stringOps::split<Foam::string>(ldPaths, ':');
+
+        for (const auto& p : paths)
+        {
+            if (p.length())  // Split removes empty, but be paranoid
+            {
+                const Foam::fileName fullPath(p.str()/libName);
+                void* handle = ::dlopen(fullPath.c_str(), ldflags);
+                if (handle)
+                {
+                    return handle;
+                }
+            }
+        }
+    }
+#endif
+
+    // Regular loading
+    return ::dlopen(libName.c_str(), ldflags);
+}
+
+
 // * * * * * * * * * * * * * * * * Local Classes * * * * * * * * * * * * * * //
 
 namespace Foam
@@ -1752,16 +1799,13 @@ int Foam::system(const Foam::UList<Foam::string>& command, const bool bg)
 
 void* Foam::dlOpen(const fileName& libName, const bool check)
 {
-    constexpr int ldflags = (RTLD_LAZY|RTLD_GLOBAL);
-
     if (POSIX::debug)
     {
         std::cout
-            << "dlOpen(const fileName&)"
-            << " : dlopen of " << libName << std::endl;
+            << "dlopen() of " << libName << std::endl;
     }
 
-    void* handle = ::dlopen(libName.c_str(), ldflags);
+    void* handle = loadLibrary(libName);
 
     if (!handle)
     {
@@ -1771,13 +1815,12 @@ void* Foam::dlOpen(const fileName& libName, const bool check)
         {
             // Try with 'lib' prefix
             libso = "lib" + libName;
-            handle = ::dlopen(libso.c_str(), ldflags);
+            handle = loadLibrary(libso);
 
             if (POSIX::debug)
             {
                 std::cout
-                    << "dlOpen(const fileName&)"
-                    << " : dlopen of " << libso << std::endl;
+                    << "   dlopen() as " << libso << std::endl;
             }
         }
         else
@@ -1790,13 +1833,12 @@ void* Foam::dlOpen(const fileName& libName, const bool check)
         if (!handle && !libso.has_ext(EXT_SO))
         {
             libso.replace_ext(EXT_SO);
-            handle = ::dlopen(libso.c_str(), ldflags);
+            handle = loadLibrary(libso);
 
             if (POSIX::debug)
             {
                 std::cout
-                    << "dlOpen(const fileName&)"
-                    << " : dlopen of " << libso << std::endl;
+                    << "   dlopen() as " << libso << std::endl;
             }
         }
     }
@@ -1810,8 +1852,7 @@ void* Foam::dlOpen(const fileName& libName, const bool check)
     if (POSIX::debug)
     {
         std::cout
-            << "dlOpen(const fileName&)"
-            << " : dlopen of " << libName
+            << "dlopen() of " << libName
             << " handle " << handle << std::endl;
     }
 
-- 
GitLab