Bottom line: change compiler flags to link to libgcc
and libstdc++
statically.
Or place them (with any transitive MinGW-specific dependencies) alongside the .pyd
and distribute with your module.
I’ve diagnosed the problem with Process Monitor using the following code.
This is the most reliable way because you get to see what the system is actually searching for, and where, rather than look at metadata and make assumptions. Since Windows’ DLL search process is compilcated and very settings-dependent, the latter proved unreliable.
# FTP_* are secret variables for an FTP server that I run on my machine when needed
- curl -f -O ftp://$FTP_USER:$FTP_PASSWD@$FTP_SERVER/procmon.exe
- ./procmon //AcceptEula //Quiet //Minimized //BackingFile error.pml & (until test -f *.pml; do sleep 1; done)
- <command under test>
- ./procmon //Terminate
- gzip -v *.pml
- curl -T "$(perl -e 'print "{".join(",",@ARGV)."}"' *.gz)" ftp://$FTP_USER:$FTP_PASSWD@$FTP_SERVER/
The relevant part of the resulting .pml
I got onto my machine for examination is:
"Time of Day","Process Name","PID","TID","Operation","Path","Result","Detail"
"22:46:05,0651278","python.exe","4852","3816","Load Image","C:\Users\travis\build\native-api\travis-windows-pybind\build\CPPMATH.cp38-win_amd64.pyd","SUCCESS","Image Base: 0x68cc0000, Image Size: 0x3d000"
"22:46:05,0652375","python.exe","4852","3816","CloseFile","C:\Users\travis\build\native-api\travis-windows-pybind\build\CPPMATH.cp38-win_amd64.pyd","SUCCESS",""
"22:46:05,0653272","python.exe","4852","3816","QueryOpen","C:\Users\travis\build\native-api\travis-windows-pybind\build\libgcc_s_seh-1.dll","NAME NOT FOUND",""
"22:46:05,0654100","python.exe","4852","3816","QueryOpen","C:\Python38\libgcc_s_seh-1.dll","NAME NOT FOUND",""
"22:46:05,0655351","python.exe","4852","3816","QueryOpen","C:\Windows\System32\libgcc_s_seh-1.dll","NAME NOT FOUND",""
So, the module it fails to find is libgcc_s_seh-1.dll
, and for some reason, it only searches for it in a few locations rather than everything on PATH
– which does include C:\ProgramData\chocolatey\lib\mingw\tools\install\mingw64\bin
where this DLL is located.
Looking at the stacktrace of the events in Process Monitor shows that the .pyd
is being loaded from python38.dll
with LoadLibraryExW
. Searching Python codebase (tag 3.8.2
as this is the version you are installing) for LoadLibraryEx
and then looking for anything related to module loading in the results finds this peculiar code:
/* bpo-36085: We use LoadLibraryEx with restricted search paths
to avoid DLL preloading attacks and enable use of the
AddDllDirectory function. We add SEARCH_DLL_LOAD_DIR to
ensure DLLs adjacent to the PYD are preferred. */
Py_BEGIN_ALLOW_THREADS
hDLL = LoadLibraryExW(wpathname, NULL,
LOAD_LIBRARY_SEARCH_DEFAULT_DIRS |
LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR);
which explains the restricted search path.
Since those libraries are not stocked with Windows or CPython but are rather specific to the MinGW toolchain, you need to link to them statically (or place, together with their dependencies, alongside the .pyd
, a location which is searched).
The restricted search path is actually proving very useful in that you can detect such dependencies early!