Use modern, secure, Windows API for dlopen

Part of the reason I started to investigate this mechanism is because we are currently reliant on setting environment variables to communicate with GR:

GR uses its own plugin system and loads dynamic libraries itself:

Here we communicate the location of the GR binaries via the GRDIR.

My thought is that perhaps we could use this new API to communicate multiple directories to GR.

In trying out this API, I found something unpleasant. If someone calls SetDefaultDllDirectories with
LOAD_LIBRARY_SEARCH_DEFAULT_DIRS, then it causes LoadLibraryExW to fail with LOAD_WITH_ALTERED_SEARCH_PATH.

julia> begin
           wchar = append!(transcode(UInt16, raw"libdSFMT.dll"), 0x0000)
           LOAD_LIBRARY_SEARCH_DEFAULT_DIRS = 0x1000
           LOAD_WITH_ALTERED_SEARCH_PATH = 0x8
           status = @ccall "kernel32".SetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_DEFAULT_DIRS::UInt32)::Bool # GR.jl v0.70.1
           handle = @ccall "kernel32".LoadLibraryExW(wchar::Ptr{UInt16}, C_NULL::Ptr{Nothing}, LOAD_WITH_ALTERED_SEARCH_PATH::UInt32)::Ptr{Nothing} # Julia
           if handle == C_NULL
               println(Libc.FormatMessage())
           end
           handle
       end
The parameter is incorrect. 
Ptr{Nothing} @0x0000000000000000

Everything loads fine if we use LOAD_LIBRARY_SEARCH_DEFAULT_DIRS.

julia> begin
           wchar = append!(transcode(UInt16, raw"libdSFMT.dll"), 0x0000)
           LOAD_LIBRARY_SEARCH_DEFAULT_DIRS = 0x1000
           LOAD_WITH_ALTERED_SEARCH_PATH = 0x8
           status = @ccall "kernel32".SetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_DEFAULT_DIRS::UInt32)::Bool
           handle = @ccall "kernel32".LoadLibraryExW(wchar::Ptr{UInt16}, C_NULL::Ptr{Nothing}, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS::UInt32)::Ptr{Nothing}        
           if handle == C_NULL
               println(Libc.FormatMessage())
           end
           handle
       end
Ptr{Nothing} @0x0000000000e20000

The main lesson from this is that we should not use SetDefaultDllDirectories since this causes Julia’s current use of LoadLibraryExW to fail when used with LOAD_WITH_ALTERED_SEARCH_PATH. The other thought is that using LOAD_WITH_ALTERED_SEARCH_PATH is fragile. Loading some plugin that invokes SetDefaultDllDirectories will cause Julia to be unable to dynamically load other dynamic libraries.

To make dynamic library loading more robust and and to take advantage of AddDllDirectory perhaps we could try to use LOAD_LIBRARY_SEARCH_DEFAULT_DIRS with LoadLibraryExW if loading with LOAD_WITH_ALTERED_SEARCH_PATH fails. That might look as follows.

    HANDLE lib = LoadLibraryExW(wfilename, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
    if(lib == NULL)
    {
        lib = LoadLibraryExW(wfilename, NULL, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS);
    }

Essentially we try to load using LOAD_WITH_ALTERED_SEARCH_PATH. If that fails, then we try LOAD_LIBRARY_SEARCH_DEFAULT_DIRS.

LOAD_LIBRARY_SEARCH_DEFAULT_DIRS is a combination of the following:

  1. LOAD_LIBRARY_SEARCH_APPLICATION_DIR
  2. LOAD_LIBRARY_SEARCH_USER_DIRS (e.g. directories set with AddDllDirectory)
  3. LOAD_LIBRARY_SEARCH_SYSTEM32

Alternatively, the contingency loading could be restricted to LOAD_LIBRARY_SEARCH_USER_DIRS.

2 Likes