I have a Fortran library that I have been developing on Linux that I want to share with colleagues who normally use Windows. I do not expect them to help develop it, so all I want to do is produce a static executable using a cross-compiler so they can run it.
I can compile simple Hello world programs using the cross-compiler toolchain on Linux (openSUSE), which work on Windows without an issue, but when I try to link the executable up to another library (Lapack), Windows complains that it can not find the .dll
file that it is dynamically linked too. In my question I showed how I compile the source using
Minimum working example
I use openSUSE, which provides a repository of cross-compiling components. To get started I installed the mingw64-cross-toolchain as well as the associated lapack and blas development files:
mingw64-cross-gcc
mingw64-cross-g++
mingw64-cross-gfortran
mingw64-lapack-devel
mingw64-blas-devel
That also brought in a number of other required packages
My minimum working tree looks like this:
├── linalg_mod.f90
└── main.f90
linalg_mod.f90 is essentially a thin wrapper around some lapack routines, and main.f90 is my main program. By themselves they aren't very interesting, main contains a simple 3x3 matrix that needs to be solved and then outputs the solution.
> cat main.f90
program main
use iso_fortran_env, only: wp=>real64
use linalg, only: linsolve_quick
implicit none
integer :: ii
real(wp) :: A(3,3), b(3), x(3)
A = reshape([1, 2, -3, -2, 1, 2, 3, 1, -2], shape=([3,3]))
b = [7, 4, -10]
call linsolve_quick(3, A, 3, b, x)
do ii = 1,3
print'(a1,i1,a3,f8.5)', 'x', ii, ' = ', x(ii)
enddo
end program main
The linalg module is slightly large, so I copied it into a Github gist - you can read that here if you're interested
Compiling natively on Linux:
> gfortran -c linalg_mod.f90 main.f90
> gfortran linalg_mod.o main.o -o main -llapack -lblas
> ./main
x1 = 2.00000000
x2 = -1.00000000
x3 = 1.00000000
As far as I understand, cross-compiling on Linux for Windows is slightly trickier because you need to bundle the library as a static executable. The Linux native code I compiled was dynamic, as can be seen here:
> ldd main
linux-vdso.so.1 (0x00007fffb65be000)
liblapack.so.3 => /usr/lib64/liblapack.so.3 (0x00007f9346bdc000)
libblas.so.3 => /usr/lib64/libblas.so.3 (0x00007f9346982000)
libgfortran.so.4 => /home/user/Software/gcc/install/lib64/gcc/x86_64-pc-linux-gnu/7.0.1/libgfortran.so.4 (0x00007f93465ad000)
libm.so.6 => /lib64/libm.so.6 (0x00007f93462b0000)
libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007f9346098000)
libquadmath.so.0 => /home/user/Software/gcc/install/lib64/gcc/x86_64-pc-linux-gnu/7.0.1/libquadmath.so.0 (0x00007f9345e59000)
libc.so.6 => /lib64/libc.so.6 (0x00007f9345ab6000)
libgfortran.so.3 => /usr/lib64/libgfortran.so.3 (0x00007f934578c000)
/lib64/ld-linux-x86-64.so.2 (0x0000558391f9e000)
Add the -static
attribute created a static executable:
> gfortran -c mod.f90 linalg_mod.f90 main.f90
> gfortran linalg_mod.o mod.o main.o -static -o main -llapack -lblas
> ldd main
not a dynamic executable
Even though main is not a dynamic executable, I'm still not totally convinced it is totally cut off from the lapack and blas dynamic libs. Is there a better way to check?
Cross-compiling from Linux to Windows
I am trying to use these same steps to cross-compile the above code into a Windows executable. On linux I am doing as follows. NOTE: The lapack and blas libraries are available as static and dynamic libraries through the mingw64 repository (specificly, packages mingw64-liblapack3
and mingw64-lapack-devel
):
/usr/x86_64-w64-mingw32/sys-root/mingw/bin/liblapack.dll
/usr/x86_64-w64-mingw32/sys-root/mingw/lib/liblapack.dll.a
/usr/x86_64-w64-mingw32/sys-root/mingw/bin/libblas.dll
/usr/x86_64-w64-mingw32/sys-root/mingw/lib/libblas.dll.a
I'm assuming that the static libraries are the files ending in .dll.a
, and the dynamic libraries are the ones ending in just .dll
. Similar to the compilation step on native Linux, here's how I attempted to create a static main.exe file
> x86_64-w64-mingw32-gfortran -c mod.f90 linalg_mod.f90 main.f90
> x86_64-w64-mingw32-gfortran linalg_mod.o mod.o main.o -static -o main.exe -llapack -lblas
> ldd main.exe
not a dynamic executable
Everything looks good, except when I move the executable to my Windows machine, I get the following error trying to execute the file in Git bash:
> ./main.exe
K:/temp-dump/main.exe: error while loading shared libraries: liblapack.dll: cannot open shared object file: No such file or directory
Looking at it's dynamic libraries is also interesting:
> ./ldd.exe ./main.exe
ntdll.dll => /c/windows/SYSTEM32/ntdll.dll (0x7ffe37c20000)
KERNEL32.DLL => /c/windows/system32/KERNEL32.DLL (0x7ffe35ab0000)
KERNELBASE.dll => /c/windows/system32/KERNELBASE.dll (0x7ffe34da0000)
msvcrt.dll => /c/windows/system32/msvcrt.dll (0x7ffe37430000)
USER32.dll => /c/windows/system32/USER32.dll (0x7ffe37760000)
GDI32.dll => /c/windows/system32/GDI32.dll (0x7ffe35920000)
For some reason the static linking of lapack isn't being maintained between Linux/Windows. Is there a way to force a static linking of a cross-compiled executable from Linux to Windows?