Search code examples
ccygwinmingw

How to let MinGW compiled program link to dll built by cygwin?


My program uses some libraries which contains POSIX function calls and cannot be compiled with MinGW, and I use Qt6 for MinGW (no Qt6 for cygwin or msys2). So I need to make my program link to the dll built by cygwin. It linked successfully, but it cannot run.

For example, here are two files a.c and b.c:

/* a.c */
int a() {
  return 1;
}
/* b.c */
extern int a();

int main() {
  return a();
}

I compiled a.c to a.dll using cygwin:

(In cygwin shell)
$ gcc -c a.c -o a.o
$ gcc -shared a.o -o a.dll
$ ldd a.dll
        ntdll.dll => /cygdrive/c/Windows/SYSTEM32/ntdll.dll (0x7ff9d5450000)
        KERNEL32.DLL => /cygdrive/c/Windows/System32/KERNEL32.DLL (0x7ff9d50f0000)
        KERNELBASE.dll => /cygdrive/c/Windows/System32/KERNELBASE.dll (0x7ff9d2e90000)
        msvcrt.dll => /cygdrive/c/Windows/System32/msvcrt.dll (0x7ff9d5370000)
        cygwin1.dll => /cygdrive/c/Users/notify/Documents/cygwin1.dll (0x180040000)
        advapi32.dll => /cygdrive/c/Windows/System32/advapi32.dll (0x7ff9d3b70000)
        sechost.dll => /cygdrive/c/Windows/System32/sechost.dll (0x7ff9d3c20000)
        RPCRT4.dll => /cygdrive/c/Windows/System32/RPCRT4.dll (0x7ff9d51b0000)
        CRYPTBASE.DLL => /cygdrive/c/Windows/SYSTEM32/CRYPTBASE.DLL (0x7ff9d2440000)
        bcryptPrimitives.dll => /cygdrive/c/Windows/System32/bcryptPrimitives.dll (0x7ff9d2b50000)

Then I compiled b.c using MinGW and link it to a.dll:

(In MSYS2 MINGW64 shell)
$ gcc -c b.c -o b.o
$ gcc b.o -L. a.dll -o b.exe
$ ldd b.exe
        ntdll.dll => /c/Windows/SYSTEM32/ntdll.dll (0x7ff9d5450000)
        KERNEL32.DLL => /c/Windows/System32/KERNEL32.DLL (0x7ff9d50f0000)
        KERNELBASE.dll => /c/Windows/System32/KERNELBASE.dll (0x7ff9d2e90000)
        msvcrt.dll => /c/Windows/System32/msvcrt.dll (0x7ff9d5370000)
        a.dll => /c/Users/notify/Documents/a.dll (0x5e4da0000)
        cygwin1.dll => /c/Users/notify/Documents/cygwin1.dll (0x180040000)
$ ./b.exe
      0 [main] b (1828) C:\Users\notify\Documents\b.exe: *** fatal error - cygheap base mismatch det
ected - 0x180350408/0x18034C408.
This problem is probably due to using incompatible versions of the cygwin DLL.
Search for cygwin1.dll using the Windows Start->Find/Search facility
and delete all but the most recent version.  The most recent version *should*
reside in x:\cygwin\bin, where 'x' is the drive on which you have
installed the cygwin distribution.  Rebooting is also suggested if you
are unable to find another cygwin DLL.

How can I solve this problem?


Solution

  • Answer

    Don't mix MinGW and Cygwin binaries!

    In fact you should treat them as different patforms:

    • MinGW / MinGW-w64 targets native Windows
    • Cygwin doesn't target native Windows, but instead uses a POSIX compatibility layer.

    Their standard libraries are different and incompatible with each other.

    This also goes for MSVC, so also don't mix MinGW / MinGW-w64 or Cygwin with MSVC.

    Solution

    Compile all components and dependencies of your project with the same compiler.

    Some tips

    Prefer MinGW-w64

    It's best to use MinGW / MinGW-w64 where possible as this targets native Windows.

    Arguments:

    • Cygwin build will require redistribution of Cygwin DLL(s) along with your application, when you want to make it available for other systems or people.
    • The Cygwin compatibility layer may cause overhead so your application may be less performant than when build with MinGW / MinGW-w64

    Finally: MinGW-w64 is more up to date than MinGW so you should MinGW-w64 instead of MinGW. MinGW-w64 supports both 32-bit and 64-bit Windows. You can install MinGW-w64 via MSYS2's package manager pacman, or you can get a standalone build from https://winlibs.com/

    POSIX functions missing in MinGW / MinGW-w64

    A downside of using MinGW / MinGW-w64 is that some POSIX functions are missing (e.g. fork()).

    So you need to replace those with their Windows alternatives.

    In case of fork() where it is used to spawn a daemon process you may need to redesign that peace of code a bit more than finding an alternative for fork() since Windows doesn't use deamons but services. So you need to write some Windows specific code for Windows to allow your application to run as a Windows service. You can use #ifdef _WIN32 to still keep your POSIX code in your application so it will still compile on POSIX platforms.

    For some POSIX functions there are already some libraries out there providing Windows alternatives with a compatible C header, for example: