Why does linking with `mingw32` solve this compilation problem with SDL2? - c

I'm learning development using SDL2 with C on Windows and Mingw.
I tried the following command to compile my program:
gcc -IC:C:/msys64_new/mingw64/include/SDL2 *.c -o game.exe -g -Wall -Wno-unused -LC:/msys64_new/mingw64/lib -lSDL2main -lSDL2
It gave the error: Undefined reference to WinMain
Then I read that I should add -lmingw32 to the command:
gcc -IC:C:/msys64_new/mingw64/include/SDL2 *.c -o game.exe -g -Wall -Wno-unused -LC:/msys64_new/mingw64/lib -lmingw32 -lSDL2main -lSDL2
And now it works! My questions are:
Why does this solve the problem? What does linking with libmingw32.a do that solves this?
How does gcc find libmingw32.a? I don't seem to have such a file in the folder directed by -LC:/msys64_new/mingw64/lib.

libmingw32 is a part of mingw implementation of C runtime library, including e.g. crt0 and thread-local storage, stuff happening before your main is called. If you ask gcc to inform you what it does under the hood via gcc -v the_rest_of_your_build_command you'll see that it have -lmingw32 anyway in its linking command; it is possible to drop CRT (and CRT0) but that's for another question (and you'll need special handling since ordinary C programs expect CRT to be present).
There are some predefined paths linker search for libraries in. Historically for unix-like systems there are /lib, /usr/lib and so on. As mingw is mostly just ported gcc toolchain it inherits the same paths, just with its installation directory prefix (e.g. c:\mingw-w64\x86_64-w64-mingw32\lib. Check outout of ld --verbose output, specifically SEARCH_DIR.
Now why you need to specify -lmingw32 even though it is there anyway - the only reason is how ld linker resolves dependencies - left to right rule, basically. If one static library depends on another, it needs to be specified first in libraries list (but can be specified multiple times, it will not produce a conflict). In your case, #include <SDL.h> redefines your main function to SDL_main; that way your program don't have defined entry point anymore, but libSDL2main.a have one - simple main or WinMain that does minimal preparation and calls your SDL_main. So, you need to link with -lSDL2main, but by default -lmingw32 is appended last (resulting in e.g. -lSDL2main -lSDL2 -lmingw32), and linker don't search for definition of WinMain in SDL2main, and there is no WinMain in your .os, hence linking error. If you add -lmingw32 -lSDL2main -lSDL2, you have correct dependency chain - CRT0 depends on WinMain which implemented in SDL2main which also depends on SDL2. Default parameters will still add -lmingw32 at the tail, so you'll implicitly have it twice, but as I've said before you're allowed to do so.

Related

libtool changes the order of my linker flags in autotools?

I am trying to statically build the web server lighttpd (version 1.4.49) using musl (for x86) as my compiler and lighttpd's autotools build system. My configuration script is as follows:
CC=/home/musl-1.1.23/install/bin/musl-gcc CFLAGS="-g --static" LDFLAGS="-L/home/lighttpd -lwrappers -static-libgcc -Wl,--wrap=socket -Wl,--wrap=bind -Wl,--wrap=listen -Wl,--wrap=accept4 -Wl,--wrap=send -Wl,--wrap=recv -Wl,--wrap=shutdown LIGHTTPD_STATIC=yes ./configure --prefix=/home/lighttpd/install --enable-static --disable-shared --without-zlib --disable-ipv6 --without-bzip2 --without-pcre
Where I have created my own static library "wrappers" that contains the wrapped definitions for the functions specified in my LDFLAGS argument for configure. The way everything is set up I need my wrapped library to be the first library linked (even before musl's standard c library), however libtool is changing the order of my LDFLAGS argument from above to the following:
libtool: link: /home/musl-1.1.23/install/bin/musl-gcc -g --static -Wall -W -Wshadow -pedantic -static-libgcc -Wl,--wrap=socket -Wl,--wrap=bind -Wl,--wrap=listen -Wl,--wrap=accept4 -Wl,--wrap=send -Wl,--wrap=recv -Wl,--wrap=shutdown -o proc_open proc_open-proc_open.o proc_open-buffer.o -L/home/lighttpd-1.4.49 -lwrappers
instead of:
libtool: link: /home/musl-1.1.23/install/bin/musl-gcc -g --static -Wall -W -Wshadow -pedantic -L/home/lighttpd-1.4.49 -lwrappers -static-libgcc -Wl,--wrap=socket -Wl,--wrap=bind -Wl,--wrap=listen -Wl,--wrap=accept4 -Wl,--wrap=send -Wl,--wrap=recv -Wl,--wrap=shutdown -o proc_open proc_open-proc_open.o proc_open-buffer.o
Which causes the following errors (I have only included the first few, but the rest are similar):
/usr/local/bin/ld: /home/musl-1.1.23/install/lib/libc.a(syslog.o): in function `__openlog':
/home/musl-1.1.23/src/misc/syslog.c:51: undefined reference to `__wrap_socket'
/usr/local/bin/ld: /home/musl-1.1.23/install/lib/libc.a(syslog.o): in function `_vsyslog':
/usr/local/bin/ld: /home/musl-1.1.23/src/misc/syslog.c:111: undefined reference to `__wrap_send'
/usr/local/bin/ld: /home/musl-1.1.23/src/misc/syslog.c:113: undefined reference to `__wrap_send'
Its weird to me why this is happening, as everywhere else the LDFLAGS appear in the right order:
/bin/bash ../libtool --tag=CC --mode=link /home/musl-1.1.23/install/bin/musl-gcc -g --static -Wall -W -Wshadow -pedantic -module -export-dynamic -avoid-version -L/home/lighttpd -lwrappers -static-libgcc -Wl,--wrap=socket -Wl,--wrap=bind -Wl,--wrap=listen -Wl,--wrap=accept4 -Wl,--wrap=send -Wl,--wrap=recv -Wl,--wrap=shutdown -o mod_setenv.la -rpath /home/lighttpd/install/lib mod_setenv.lo
libtool: link: ranlib .libs/mod_scgi.a
Any help would be greatly appreciated! Thank you in advance.
LDFLAGS is not for libraries to add to the link. libtool notwithstanding, LDFLAGS is expanded too early in the link command, and this is not specific to the autotools.
In your case, libtool appears to be recognizing that error, and in the case where that matters, moving the -L and -l options where they need to be, after the object(s) being linked. (Or at least the -l options need to be there, especially for a static link, and if libtool is going to move those, then there are practical reasons for moving the -L options, too.)
Unfortunately, there is no other user-oriented Autotools variable appropriate to your stated purpose, either. Injecting extra libraries into the link is not a use case the Autotools are designed to support, and injecting them at the beginning of the link is unlikely to be feasible without hacking the autotooling.
On the third hand, you appear to be misinterpreting the problem. You claim that
I need my wrapped library to be the first library linked (even before musl's standard c library)
But this ...
/usr/local/bin/ld: /home/musl-1.1.23/install/lib/libc.a(syslog.o): in function `_vsyslog':
/usr/local/bin/ld: /home/musl-1.1.23/src/misc/syslog.c:111: undefined reference to `__wrap_send'
... tells me exactly the opposite: even calls from inside standard library functions in libc are being wrapped, so the wrapper needs to appear last, even after the standard library (which ordinarily is last, not first).* And since libwrap probably calls standard library functions itself, you may need to link libc twice. Of course, that assumes that you really want to be messing with the standard library that way, which does not seem advisable to me.
If you were performing a dynamic link then this would not be an issue: it would not be possible to wrap the internal calls of the C library in the first place. This suggests to me that the linker's --wrap option probably was not designed with static linking of the standard library in mind.
I'm not inclined to set up a testbed to verify the following, but if you really do want to wrap libc's own internal function calls, then this may work: ... LDFLAGS="-static-libgcc -Wl,--wrap=socket -Wl,--wrap=bind -Wl,--wrap=listen -Wl,--wrap=accept4 -Wl,--wrap=send -Wl,--wrap=recv -Wl,--wrap=shutdown" LIBS="-L/home/lighttpd -lc -lwrappers -lc" ./configure ...
LIBS is not really intended to be a user variable, and that's the potential weak point of this plan. But it is among Autoconf's standard output variables, and its contents should appear at the end of the link line. configure may prepend other libraries to LIBS, but it should not append any. The risk is that configure may clear any initial contents.
The -lc appearing before -lwrappersis the secret sauce. Normally, you do not need to specify -lc explicitly, but in this case you do, because you need to resolve some symbols in it against libwrappers, which otherwise would be linked too early. You also (probably) need to link some symbols in libwrappers against libc. It may be redundant, but explicitly linking libc again, after libwrappers, may be necessary to overcome unwanted cleverness by the linker.
If you want to avoid wrapping the internal libc self-calls, then you could try ... LDFLAGS="-static-libgcc -Wl,--wrap=socket -Wl,--wrap=bind -Wl,--wrap=listen -Wl,--wrap=accept4 -Wl,--wrap=send -Wl,--wrap=recv -Wl,--wrap=shutdown" LIBS="-L/home/lighttpd -lwrappers -Wl,--no-wrap=socket -Wl,--no-wrap=bind -Wl,--no-wrap=listen -Wl,--no-wrap=accept4 -Wl,--no-wrap=send -Wl,--no-wrap=recv -Wl,--no-wrap=shutdown" ./configure .... The idea there is to turn off wrapping for libc (only), but it's pretty speculative, because although negating long options with no- is standard GNU form, the --no-wrap option in particular is not documented.
*So libtool's reordering is not causing the issue you present. In fact, if libtool did not perform that reordering then you would have even more link errors.

What are the correct flags for linking to gslcblas libraries when they are not in /usr/local/lib?

On my personal laptop I enter
gcc -std=gnu99 -lm -lgsl -lgslcblas equal_runtimes.c particle_filters.c solvers.c -o equal_runtimes
in the terminal to compile my C code and have no issues. As far as I know this is because the gsl headers and libraries are on the standard search paths /usr/local/include and /usr/local/lib respectively (https://www.gnu.org/software/gsl/doc/html/usage.html).
However, when I try to compile the same code with the above commands on a system in which the headers/libraries are located in /apps/gsl/2.1/include and /apps/gsl/2.1/lib respectively, I get the warning
warning: implicit declaration of function ‘gsl_blas_dgemv’ [-Wimplicit-function-declaration]
Therefore it seems the compiler is not linking the gsl libraries, as it's assuming gsl_blas_dgemv is my function. To try to fix this I've followed the guidance at the above gnu.org link, for which the best I can come up with is
gcc -L/apps/gsl/2.1/lib -std=gnu99 -lm -lgsl -lgslcblas equal_runtimes.c particle_filters.c solvers.c -o equal_runtimes
but still no success. Is there an issue with the above syntax I'm using or is there something else here that I'm missing?
Following on from #Eric Potpischil's comments, the problem was the wrong header <gsl/gsl_cblas.h> being used for gsl_blas_dgemv instead of the correct <gsl/gsl_blas.h>. This was not an issue on my own system as the blas library would automatically be found by virtue of its location, which was not the case when using the external system.

What could be causing linking errors when compiling in an Alpine Docker?

I am trying to compile a program within a docker container built from the Alpine 3.7 base image. The program uses argp.h, and includes it as #include <argp.h>. I have installed argp-standalone and verified that it is making it onto the image. The file argp.h is located in usr/include, however when I compile my program using the following commands:
gcc -W -Wall -Wextra -I/usr/include -c -o progname.o progname.c
gcc -largp -o progname progname.o
I get the following error:
progname.o: In function `parse_opt':
progname.c:(.text+0x4c9): undefined reference to `argp_failure'
progname.c:(.text+0x50f): undefined reference to `argp_failure'
progname.c:(.text+0x555): undefined reference to `argp_failure'
progname.c:(.text+0x59b): undefined reference to `argp_failure'
progname.c:(.text+0x5ce): undefined reference to `argp_error'
progname.c:(.text+0x5f4): undefined reference to `argp_error'
progname.o: In function `main':
progname.c:(.text+0x1397): undefined reference to `argp_parse'
collect2: error: ld returned 1 exit status
make: *** [Makefile:9: progname] Error 1
I have:
Ensured that the version of argp.h which is on the image does in fact include the argp_failure, argp_parse, and argp_error functions.
Tried moving argp.h into different locations on the machine (e.g. into the same directory where compilation is taking place, into /usr/lib)
Tried compiling with -l and -L.
The relevant packages also installed in the image are build-base, make, and gcc.
When compiling on an Ubuntu image these same commands work fine, even without the -largp and -I/usr/include flags. What could be happening differently within an Alpine image which would cause this not to work?
Edit
As per #Pablo's comment, I'm now compiling it as follows:
gcc -W -Wall -Wextra -I/usr/include -L/usr/lib -c -o progname.o progname.c
gcc -largp -o progname progname.o
After having verified that the static library, libargp.a, is located in /usr/lib. However, the same problem still persists.
Edit 2
Compiling as follows (and once again as per #Pablo's suggestion) has resolved the error I was having:
gcc -W -Wall -Wextra -I/usr/include -L/usr/lib -c -o progname.o progname.c
gcc -o progname progname.o /usr/lib/libargp.a
However, I am still curious why, using the exact same library and instructions, this would fail to compile in an Alpine image while compiling without issue in an Ubuntu image.
I am still curious why, using the exact same library and instructions, this would fail to compile in an Alpine image while compiling without issue in an Ubuntu image.
The reason for the linking error on Alpine may be kind of surprising, and is actually not specific to Alpine.
While this fails to link:
gcc -largp -o progname progname.o
This works:
gcc -o progname progname.o -largp
The reason is the order of parameters passed to the linker, and it related to the linking algorithm. Typically, in the linking command line objects are specified first (and possibly user's static libraries in any), then libraries using -l. The standard linker algorithm is explained perfectly in Eli Bendersky's article, Library order in static linking:
Object files and libraries are provided in a certain order on the command-line, from left to right. This is the linking order. Here's what the linker does:
The linker maintains a symbol table. This symbol table does a bunch of things, but among them is keeping two lists:
A list of symbols exported by all the objects and libraries encountered so far.
A list of undefined symbols that the encountered objects and libraries requested to import and were not found yet.
When the linker encounters a new object file, it looks at:
The symbols it exports: these are added to the list of exported symbols mentioned above. If any symbol is in the undefined list, it's removed from there because it has now been found. If any symbol has already been in the exported list, we get a "multiple definition" error: two different objects export the same symbol and the linker is confused.
The symbols it imports: these are added to the list of undefined symbols, unless they can be found in the list of exported symbols.
When the linker encounters a new library, things are a bit more interesting. The linker goes over all the objects in the library. For each one, it first looks at the symbols it exports.
If any of the symbols it exports are on the undefined list, the object is added to the link and the next step is executed. Otherwise, the next step is skipped.
If the object has been added to the link, it's treated as described above - its undefined and exported symbols get added to the symbol table.
Finally, if any of the objects in the library has been included in the link, the library is rescanned again - it's possible that symbols imported by the included object can be found in other objects within the same library.
When -largp appears first, the linker does not include any of its objects in the linking procedure, since it doesn't have any undefined symbols yet. If the static library is provided by path, and not with -l, then all of its objects are added to the linking procedure.

Compiling a C program and linking

Do I understand it correctly on how to use cmake for linking
cmake_minimum_required(VERSION 3.2)
project(Sdltest)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Werror -lmingw32 -lSDL2main -lSDL2")
set(SOURCE_FILES main.c)
add_executable(Sdltest ${SOURCE_FILES})
I'm trying to link the sdl2 to my project but I get a lot of errors.
like /src/mingw-org-wsl/4.0-dev/src/libcrt/crt/main.c:91: undefined reference to `WinMain#16'
When I use gcc on cmd, it compiles perfectly
gcc main.c -o test -Wall -Werror -lmingw32 -lSDL2main -lSDL2
By the way I use clion 1.0.5
The problem is the flags, you mix compiler and linker flags. First of all this is problematic because the compiler flags aren't used when linking, and secondly because the GNU linker needs the libraries to come after the main source (or object) file on the command line, just like when you build manually.
You set the libraries by using the target_link_libraries command in your CMakeLists.txt file:
target_link_libraries(Sdltest mingw32 SDL2main SDL2)
Also, you're not building a C++ project, you're building a C project, so the flags variable should be CMAKE_C_FLAGS.
Finally, if you're wondering about the command and arguments that CMake uses when building, try making with VERBOSE set to non-zero, e.g.
make VERBOSE=1
I'm sure it's possible to add that argument to CLion.
PS.
If you want to enable more warnings, there are even more flags you could add, like -Wextra and -pedantic. Yes it might sometime give false positives, especially when including header files from external libraries, but I think it's better to have to many warnings when developing than to few.

MinGW GCC - undefined reference to `atexit'

I am trying to link a large project with GCC 4.8.1 from MinGW for a x86 target.
I am calling the linker like this
D:\MyGCCPath\gcc -L [LIBPATHS] -nostdlib -Wl,-Map,D:\PathToMapFile.map,--emit-relocs [OBJECTFILES AND LIBS] -lmsvcrt -lgcc -o D:\PathToMyOutputFile
With this call I get this linker rror:
libgcc.a(__main.o):(.text+0x5a): undefined reference to `atexit'
I tried different msvcr versions (100 and 90), but this was more a desperate attempt, since I am not very familiar with this problem. I am using the correct libraries provided by MinGW.
Is there any way I can fix this error?
You are linking with -nostdlib, and atexit() is a function from stdlib.h.
According to GCC Link Options:
-nostdlib
Do not use the standard system startup files or libraries when linking. No startup files and only the libraries you specify are passed to the linker, and options specifying linkage of the system libraries, such as -static-libgcc or -shared-libgcc, are ignored.
Libraries are checked in the order used on the command line so use -lgcc -lmsvcrt.

Resources