2
votes

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?

1

1 Answers

2
votes

Well this took forever, and the solution was embarrassingly easy...

Apparently all I needed to do was put the .dll files alongside the executable in the same directory. I just copied over the following .dlls to my windows machine and now it works fine. These are the files I needed to copy over:

/path/to/windows/drive/
├── libblas.dll
├── libgcc_s_seh-1.dll
├── libgfortran-4.dll
├── liblapack.dll
├── libquadmath-0.dll
├── libwinpthread-1.dll
└── main.exe