With help from @llonesmiz, a number of issues were identified.
1. Libraries need to be specified after objects and sources which use them.
As described here:
The traditional behavior of linkers is to search for external functions from left to right in the libraries specified on the command line. This means that a library containing the definition of a function should appear after any source files or object files which use it. This includes libraries specified with the short-cut -l option, as shown in the following command:
$ gcc -Wall calc.c -lm -o calc (correct order)
With some linkers the opposite ordering (placing the -lm option before the file which uses it) would result in an error,
$ cc -Wall -lm calc.c -o calc (incorrect order)
main.o: In function 'main':
main.o(.text+0xf): undefined reference to 'sqrt'
because there is no library or object file containing sqrt after ‘calc.c’. The option -lm should appear after the file ‘calc.c’
2. Library paths should be explicitly specified.
If no lib paths are specified, the linker might look for the libs in a series
of default folders, thus loading a different library then intended. This is what
happened in my case - I wanted to link boost_unit_test_framework
, but did not
specify a path because I assumed the linker would look in the current folder.
That's what happens at runtime, after all - if the dll
is in the same folder
with the exe
, it will find it.
I found it a little bit strange the linker would find the lib, since it was
named ibboost_unit_test_framework-mgw47-mt-1_52.dll
. When I tried to link to
a non-existing lib, the linker complained though, so I assumed this isn't an
issue, and MinGW
's linker ignores those suffixes.
After some more research, I found this article about MinGW library paths.
The folders MinGW searches for libs can be found in the output of gcc -print-search-dirs
.
The article also contains some bash
magic to make sense of that output:
gcc -print-search-dirs | sed '/^lib/b 1;d;:1;s,/[^/.][^/]*/\.\./,/,;t 1;s,:[^=]*=,:;,;s,;,; ,g' | tr \; \\012 | grep -v '^ */'
This will print a nice list of those folders. gcc
will not, by default,
look in the current directory for libs. I looked in each of them, and found the
lib that was being loaded - libboost_unit_test_framework.a
, a static lib.
This brings into light another issue worth mentioning:
3. Static versus dynamic linking
I did not specify whether I want boost_unit_test_framework
linked statically or dynamically.
In this case, gcc
prefers dynamic linking:
Because of these advantages gcc compiles programs to use shared libraries by default on most systems, if they are available. Whenever a static library ‘libNAME.a’ would be used for linking with the option -lNAME the compiler first checks for an alternative shared library with the same name and a ‘.so’ extension.
(so
is the extension for dynamic libraries on Unix - on Windows, the equivalent is dll
.)
So, what happened is that gcc
looked for libboost_unit_test_framework.dll
in all it's default folders, but couldn't find it. Then it looked for
libboost_unit_test_framework.a
, and statically linked that. This resulted in
linking errors because the sources have #define BOOST_TEST_DYN_LINK
, and
therefore expect to have the lib dynamically linked.
To enforce static or dynamic linking, the -Wl,-Bstatic
and -Wl,-Bdynamic
linker options come into play, described here.
If I tell the linker that I want dynamic linking:
$ g++ -I/e/code/boost_1_52_0 runner.cpp test1.cpp -o runner -Wl,Bdynamic -lboost_unit_test_framework
This will fail, because the linker will not be able to find the dll
.
4.Summary
The issues were:
- libraries where specified before the sources which used them
- the lib path wasn't specified
- the type of linking wasn't specified
- the name of the library was not correct
Final, working command:
$ g++ -I/e/code/boost_1_52_0 -o runner runner.cpp test1.cpp -L. -Wl,-Bdynamic -lboost_unit_test_framework-mgw47-mt-1_52