11
2- #include " PlatformLoader.hpp"
2+ #include " DynamicLibrary.hpp"
3+
4+ #include " Logging.hpp"
5+
6+ #include < cstdio>
37
48#ifndef __EMSCRIPTEN__
59
3135#endif
3236
3337// -------------------------------------------------------------------------
34- // Optional legacy DLL search fallback
38+ // Legacy DLL search fallback
3539// -------------------------------------------------------------------------
3640//
3741// If the OS loader does not support LOAD_LIBRARY_SEARCH_* flags (ERROR_INVALID_PARAMETER),
4246// (which can consult legacy search paths such as the process current working directory).
4347// This is disabled by default for security hardening.
4448//
45- // Define PLATFORM_ALLOW_UNSAFE_DLL_SEARCH =1 to re-enable the legacy fallback.
46- #ifndef PLATFORM_ALLOW_UNSAFE_DLL_SEARCH
49+ // Define GLRESOLVER_ALLOW_UNSAFE_DLL_SEARCH =1 to re-enable the legacy fallback.
50+ #ifndef GLRESOLVER_ALLOW_UNSAFE_DLL_SEARCH
4751
48- #define PLATFORM_ALLOW_UNSAFE_DLL_SEARCH 0
52+ #define GLRESOLVER_ALLOW_UNSAFE_DLL_SEARCH 0
4953
5054#endif
5155
@@ -89,9 +93,49 @@ auto TrimTrailingWhitespace(std::string& str) -> void
8993
9094} // namespace
9195
96+ auto EnvFlagEnabled (const char * name, bool defaultValue) -> bool
97+ {
98+ if (name == nullptr )
99+ {
100+ return defaultValue;
101+ }
102+ const char * v = std::getenv (name);
103+ if (v == nullptr || v[0 ] == ' \0 ' )
104+ {
105+ return defaultValue;
106+ }
107+
108+ // Lowercase first char is enough for common values.
109+ const char c0 = (v[0 ] >= ' A' && v[0 ] <= ' Z' ) ? static_cast <char >(v[0 ] - ' A' + ' a' ) : v[0 ];
110+ if (c0 == ' 1' || c0 == ' y' || c0 == ' t' )
111+ {
112+ return true ;
113+ }
114+ if (c0 == ' 0' || c0 == ' n' || c0 == ' f' )
115+ {
116+ return false ;
117+ }
118+
119+ // Accept "on"/"off".
120+ if ((c0 == ' o' ) && (v[1 ] != ' \0 ' ))
121+ {
122+ const char c1 = (v[1 ] >= ' A' && v[1 ] <= ' Z' ) ? static_cast <char >(v[1 ] - ' A' + ' a' ) : v[1 ];
123+ if (c1 == ' n' )
124+ {
125+ return true ;
126+ }
127+ if (c1 == ' f' )
128+ {
129+ return false ;
130+ }
131+ }
132+
133+ return defaultValue;
134+ }
135+
92136#ifndef __EMSCRIPTEN__
93137
94- DynamicLibrary::~DynamicLibrary ()
138+ DynamicLibrary::~DynamicLibrary ()
95139 {
96140 if (m_closeOnDestruct)
97141 {
@@ -142,14 +186,16 @@ auto TrimTrailingWhitespace(std::string& str) -> void
142186 continue ;
143187 }
144188
189+ LOG_DEBUG (std::string (" [DynamicLibrary] open attempt: " ) + name);
190+
145191#ifdef _WIN32
146192
147193 // DLL loading policy:
148194 // - Prefer LoadLibraryExA with LOAD_LIBRARY_SEARCH_* flags to avoid CWD/PATH hijacking.
149195 // - If the OS loader doesn't support the flags (ERROR_INVALID_PARAMETER), fall back to:
150196 // * application directory (explicit full path based on the running module)
151197 // * System32 (for known system DLLs like opengl32.dll)
152- // * optionally, legacy LoadLibraryA(name) (disabled by default; see PLATFORM_ALLOW_UNSAFE_DLL_SEARCH ).
198+ // * optionally, legacy LoadLibraryA(name) (disabled by default; see GLRESOLVER_ALLOW_UNSAFE_DLL_SEARCH ).
153199 //
154200 // NOTE: We intentionally do not call SetDefaultDllDirectories() here. It changes
155201 // the process-wide DLL search behavior and can surprise a host application.
@@ -202,7 +248,7 @@ auto TrimTrailingWhitespace(std::string& str) -> void
202248 return ::LoadLibraryA (dllName);
203249 };
204250
205- // Best-effort legacy fallback when LOAD_LIBRARY_SEARCH_* flags are unavailable.
251+ // legacy fallback when LOAD_LIBRARY_SEARCH_* flags are unavailable.
206252 // Prefer LOAD_WITH_ALTERED_SEARCH_PATH for absolute paths so dependent DLLs are
207253 // resolved relative to the loaded module directory.
208254 auto tryLoadExplicitPathFallback = [&](const char * dllPath) -> HMODULE {
@@ -285,7 +331,7 @@ auto TrimTrailingWhitespace(std::string& str) -> void
285331
286332 if (handle == nullptr && ::GetLastError () == ERROR_INVALID_PARAMETER )
287333 {
288- // Flags unsupported: best-effort manual safe search.
334+ // Flags unsupported: manual safe search.
289335 // 1) Application directory
290336 if (handle == nullptr && !appFull.empty ())
291337 {
@@ -307,7 +353,7 @@ auto TrimTrailingWhitespace(std::string& str) -> void
307353 // NOTE: LoadLibrary(name) without LOAD_LIBRARY_SEARCH_* flags can consult legacy
308354 // search paths (including the current working directory) depending on process
309355 // configuration. See Microsoft guidance on DLL search order hardening.
310- if (handle == nullptr && PLATFORM_ALLOW_UNSAFE_DLL_SEARCH != 0 )
356+ if (handle == nullptr && GLRESOLVER_ALLOW_UNSAFE_DLL_SEARCH != 0 )
311357 {
312358 handle = tryLoad (name);
313359 }
@@ -326,11 +372,43 @@ auto TrimTrailingWhitespace(std::string& str) -> void
326372 ::dlerror (); // clear any prior error
327373 m_handle = ::dlopen (name, RTLD_NOW | RTLD_LOCAL );
328374
375+ #if defined(__APPLE__)
376+ // Allow callers to specify an explicit search directory for bundled dylibs
377+ // (e.g., ANGLE's libEGL.dylib / libGLESv2.dylib inside a macOS app bundle).
378+ // This is an optional override for deployments where @rpath-based discovery is not sufficient.
379+ // It is intentionally scoped to macOS to avoid changing loader semantics elsewhere.
380+ if (m_handle == nullptr )
381+ {
382+ const char * const extraDir = std::getenv (" GLRESOLVER_DYLIB_DIR" );
383+ if (extraDir != nullptr && extraDir[0 ] != ' \00 ' )
384+ {
385+ std::string full (extraDir);
386+ if (!full.empty () && full.back () != ' /' )
387+ {
388+ full.push_back (' /' );
389+ }
390+ full += name;
391+ ::dlerror ();
392+
393+ m_handle = ::dlopen (full.c_str (), RTLD_NOW | RTLD_LOCAL );
394+
395+ if (m_handle != nullptr )
396+ {
397+ m_loadedName = full;
398+ LOG_DEBUG (std::string (" [DynamicLibrary] opened: " ) + m_loadedName);
399+ return true ;
400+ }
401+ }
402+ }
403+
404+ #endif
405+
329406#endif
330407
331408 if (m_handle != nullptr )
332409 {
333410 m_loadedName = name;
411+ LOG_DEBUG (std::string (" [DynamicLibrary] opened: " ) + m_loadedName);
334412 return true ;
335413 }
336414
@@ -382,6 +460,10 @@ auto TrimTrailingWhitespace(std::string& str) -> void
382460
383461 } // for loop
384462
463+ if (!reason.empty ())
464+ {
465+ LOG_DEBUG (std::string (" [DynamicLibrary] open failed: " ) + reason);
466+ }
385467 return false ;
386468 }
387469
@@ -467,7 +549,7 @@ auto TrimTrailingWhitespace(std::string& str) -> void
467549 }
468550
469551 // If the host has already loaded EGL/GLES provider DLLs (e.g., ANGLE), probe those modules as well.
470- // This is a best-effort enhancement for applications embedding this library where we may not have
552+ // This is a enhancement for applications embedding this library where we may not have
471553 // opened the provider libraries ourselves.
472554 {
473555 static constexpr std::array<const char *, 6 > moduleNames =
0 commit comments