Search code examples
gccdllstatic-linkingmingw-w64

How can I statically link a .lib file using mingw-w64


I'm trying to build an application and link it againt the ffmpeg dll's. However I want to build a static exe file.

I'm using MSYS2 and mingw-w64 (x86_64) with gcc version 13.2.0, when building this is the command being used by the makefile

gcc -g -O2  -static  -o comskip.exe comskip-comskip.o comskip-mpeg2dec.o comskip-platform.o comskip-video_out_dx.o ccextratorwin/comskip-608.o ccextratorwin/comskip-ccextractor.o ccextratorwin/comskip-encoding.o ccextratorwin/comskip-general_loop.o ccextratorwin/comskip-myth.o -largtable2 -Lffmpeg-6.1.1-full_build-shared/lib -lavutil -lavformat -lavcodec -lswscale -lcomdlg32 -lgdi32 -lpthread -lm

However the exe file produced is not statically linked to the ffmpeg libraries (avcodec, avutil, avformat and swscale). When I try to run the exe it's still asking for the dll file separately.

The file I have in the lib folder are avcodec.lib, avcodec-60.def and avcodec.dll.a. The question is why is mingw-w64 still linking dynamically instead of linking statically to the .lib files? What do I need to have it link statically to the avcodec library? (I also have the avcodec.dll file but my understanding is that the .lib files are needed to link statically). These are 64 bit dll and lib files.


Solution

  • I'm trying to build an application and link it against the ffmpeg dll's. However I want to build a static exe file.

    That's not an option I'm afraid. You can't statically link DLLs into a program. A program can only load DLLs at runtime: that's the nature of Dynamic Link Libraries. The only things you can statically link into a program are object files, which may be explicitly named to the linker or selectively extracted by the linker from a static library. Object files are always statically linked; dynamic libraries are always dynamically linked. Those are the only options.

    But the fact that your program persists in trying to load its DLLs even though you succeed in linking it statically:

    gcc -g -O2  -static  -o comskip.exe ....
    

    presents a more murky problem.

    We would hope and expect it is impossible for a command like gcc ... -static -o prog ... to produce an executable that has dynamic dependencies. But on Windows there is a way, thanks to Microsoft's historical decision that name.lib might as well be the name of the DLL import library for name.dll, as well as the name for the static library version of name.dll.

    my understanding is that the .lib files are needed to link statically

    It sounds as if you're thinking that if you have e.g. "the" avcodec.lib file that matches avcodec.dll then you can use it to statically link avcodec.dll into a program.

    You can't. If there is an avcodec.lib file that matches avcodec.dll then avcodec.lib might be either:

    • a static library version of the avcodec library, for static linkage only, whereas name.dll is a dynamic library version, for dynamic linkage only. They're both made from the same object files, but in completely different ways. avcodec.dll and avcodec.lib are independent of each other. You don't need avcodec.lib to link a program with avcodec.dll. You don't need avcodec.dll to link with avcodec.lib. You'd never (intentionally) link a program with both.

    or avcodec.lib might be:

    • an import library, named according to the Microsoft naming convention, that's required by the Microsoft linker to accomplish linkage of avcodec.dll with a program. The MS linker is unable to link with DLLs directly: to do it indirectly, it needs an import library to be input to the linker instead. The import library avcodec.lib is a link-time proxy for avcodec.dll. It contains stubs representing the symbols exported by avcodec.dll, which the runtime linker will resolve to the real symbols once avcodec.dll is found and co-loaded with the program.

    As you see, the two different things that avcodec.lib might be, per the MS library naming convention, have entirely conflicting purposes :)

    Library naming conventions observed by the Msys2/mingw64 linker.

    To get Msys2/mingw64 linkages right, it helps to understand the names and types of file the linker can use as libraries of different sorts. We'll stick with the relevant avcodec example library.

    • libavcodec.dll: The canonical Msys2/mingw64 name for the avcodec dynamic library.
    • libavcodec.a: The canonical Msys2/mingw64 name for the avcodec static library. This file is an archive produced by the GNU archiver ar - canonical file extension .a - filled with object files for selective consumption in static linkage.
    • libavcodec.dll.a: The canonical Msys2/mingw64 name for the import library matching libavcodec.dll. This file is an ar archive containing very small object files that serve as link-time stubs for the symbols exported by libavcodec.dll
    • avcodec.dll.a: A non-canonical Msys2/mingw64 name for an import library, possibly made to match libavcodec.dll and possibly to match avcodec.dll.
    • avcodec.dll: The canonical Microsoft name for the avcodec dynamic library.
    • avcodec.lib: The canonical Microsoft name for the avcodec static library. This file is an archive produced by the Microsoft archiver LIB - canonical extension .lib - filled with object files for selective consumption in static linkage.
    • avcodec.lib: Also the canonical Microsoft name for the import library matching avcodec.dll. This file is a LIB archive containing very small object files that serve as link-time stubs for the symbols exported by name.dll. Since it is a LIB archive, and contains object files, it strictly is a static library - it's just no use for statically linking the avcodec library! If the "real" static library avcodec.lib exists at all, it's somewhere else.

    How the Msys2/mingw64 linker resolves -l name.

    Unlike the Microsoft linker The Msys2/mingw64 linker does not actually need an import library to link against a DLL, but it will use an import library if it comes across one first. Either way, you still have dynamic, not static linkage: the program is runtime dependent on the DLL.

    The linker gives preference to libraries that conform to its own naming conventions, but tries to accommodate the Microsoft naming convention as well.

    By default the linker will allow linkage with DLLs , and prefer them to static libraries. To resolve -l name, it traverses its list of explicit and implicit search directories seeking candidate files. In each directory it will accept the following files, in order of preference:

    libname.dll, libname.dll.a, name.dll.a, name.dll, libname.a, name.lib
    

    If the option -static has been passed to gcc then only the last two of those will be accepted because all the rest imply dynamic linkage. As soon as the linker finds any candidates in a directory it picks the preferred one, inputs it to the linkage and looks no further.

    What's gone wrong

    For a -static linkage, libname.a or name.lib ought to be the genuine static library that you want. But either of them might really be an import library and the linker would be none the wiser, as long the file is indeed an archive containing object files.

    With libname.a, it's very improbable that the file will really be an import library and not a static library, because the canonical import library will be libname.dll.a. But with the Microsoft-styled name.lib, there is no such distinction: an import library could readily infiltrate your linker search path somewhere where a real static library is supposed to be, or just be mistaken for a static library when you work out the -L dir options for library search.

    One of those things has happened to you. In your -static linkage, the linker accepts avcodec.lib as resolving -l avcodec. It is in a search directory also containing avcodec.def and avcodec.dll.a. avcodec.def is not any kind of library name (it is a gendef output file) and avcodec.dll.a will not be accepted to resolve -l avcodec in a -static linkage. So avcodec.lib is linked. It is in fact a MS-styled import library that proxies for avcodec.dll and your "statically linked" comskip.exe accordingly attempts to load avcodec.dll at runtime.

    In unix-like OSes the linker can't be fooled like this, because it doesn't use import libraries, even optionally. And the MS linker can't be fooled like this either, because it has no equivalent of the gcc -static option. The Msys2/mingw64 linker can be fooled like this because it's the GNU/Linux linker, ported to Windows with added support for import libraries.

    From your linkage option -Lffmpeg-6.1.1-full_build-shared/lib it appears that ffmpeg-6.1.1-full_build-shared/lib is the search directory where this library confusion abides. Probably the pattern:

    name.lib, name.def, name.dll.a
    

    is repeated for all the other ffmpeg libraries you are linking. If you didn't create this setup yourself then I'd guess that the name.lib files have been deposited there to be import libraries for building with the MS Visual C++ toolchain and that the name.dll.a files have originated with commands like:

    > gendef.exe [lib?]name.dll
    > dlltool.exe -d [lib?]name.def -D [lib?]name.dll -l name.dll.a
    

    and been deposited there to be import libraries for building with the Msys2/mingw64 GCC toolchain.

    What to do?

    To statically link your comskip.exe you need to link the real ffmpeg static libraries for mingw64 GCC. You can install all these in MSYS2 from the mingw-w64-x86_64-ffmpeg package. In the MSYS2 shell run:

    $ pacman -S mingw-w64-x86_64-ffmpeg
    

    Then link with -L C:\msys64\mingw64\lib instead of -Lffmpeg-6.1.1-full_build-shared/lib.

    Of the non-ffmpeg libraries you are linking,

    -largtable2 ... -lcomdlg32 -lgdi32 -lpthread -lm
    

    you should find that C:\msys64\mingw64\lib has static versions of all but libargtable2.a. There is no pacman package for that. Google tells me that source distributions of argtable2 are on Sourceforge and github. You likely got it from one of those sources and can build build the static library yourself with gcc and ar. Those sources however have seen no development in ~10 years and I also spotted a more active github fork argtabl3 which you might check out.