boost test - 'undefined reference' errors
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
Mihai Rotaru
Updated on July 23, 2022Comments
-
Mihai Rotaru almost 2 years
I have two simple files:
runner.cpp:
#define BOOST_TEST_DYN_LINK #define BOOST_TEST_MODULE Main #include <boost/test/unit_test.hpp>
and test1.cpp:
#define BOOST_TEST_DYN_LINK #ifdef STAND_ALONE # define BOOST_TEST_MODULE Main #endif #include <boost/test/unit_test.hpp> BOOST_AUTO_TEST_SUITE( Foo) BOOST_AUTO_TEST_CASE( TestSomething ) { BOOST_CHECK( true ); } BOOST_AUTO_TEST_SUITE_END()
To compile, I'm using:
$ g++ -I/e/code/boost_1_52_0 -o runner -lboost_unit_test_framework runner.cpp test1.cpp
I get the following error:
C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\ccU0cDSz.o:runner.cpp:(.text+0x8c): multiple definition of `main' c:/pdev/mingw/bin/../lib/gcc/i686-pc-mingw32/4.7.2/../../../libboost_unit_test_framework.a(unit_test_main.o):unit_test_main.cpp:(.text.startup+0x0): first defined here c:/pdev/mingw/bin/../lib/gcc/i686-pc-mingw32/4.7.2/../../../libboost_unit_test_framework.a(unit_test_main.o):unit_test_main.cpp:(.text.startup+0x14): undefined reference to `init_unit_test_suite(int, char**)' C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\ccU0cDSz.o:runner.cpp:(.text+0x52): undefined reference to `_imp___ZN5boost9unit_test9framework17master_test_suiteEv' C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\ccU0cDSz.o:runner.cpp:(.text+0xb0): undefined reference to `_imp___ZN5boost9unit_test14unit_test_mainEPFbvEiPPc' C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\ccU0cDSz.o:runner.cpp:(.text$_ZN5boost9unit_test13test_observerD2Ev[__ZN5boost9unit_test13test_observerD2Ev]+0xe): undefined reference to `_imp___ZTVN5boost9unit_test13test_observerE' C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\ccU0cDSz.o:runner.cpp:(.text$_ZN5boost9unit_test13test_observerC2Ev[__ZN5boost9unit_test13test_observerC2Ev]+0xe): undefined reference to `_imp___ZTVN5boost9unit_test13test_observerE' C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\ccU0cDSz.o:runner.cpp:(.text$_ZN5boost9unit_test15unit_test_log_tC1Ev[__ZN5boost9unit_test15unit_test_log_tC1Ev]+0x22): undefined reference to `_imp___ZTVN5boost9unit_test15unit_test_log_tE' C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\cciSdkmB.o:test1.cpp:(.text+0x88): undefined reference to `_imp___ZN5boost9unit_test15unit_test_log_t14set_checkpointENS0_13basic_cstringIKcEEjS4_' C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\cciSdkmB.o:test1.cpp:(.text+0x136): undefined reference to `_imp___ZN5boost10test_tools9tt_detail10check_implERKNS0_16predicate_resultERKNS_9unit_test12lazy_ostreamENS5_13basic_cstringIKcEEjNS1_10tool_levelENS1_10check_typeEjz' C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\cciSdkmB.o:test1.cpp:(.text+0x21d): undefined reference to `_imp___ZN5boost9unit_test9ut_detail24auto_test_unit_registrarC1ENS0_13basic_cstringIKcEE' C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\cciSdkmB.o:test1.cpp:(.text+0x284): undefined reference to `_imp___ZN5boost9unit_test9ut_detail24auto_test_unit_registrarC1EPNS0_9test_caseEm' C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\cciSdkmB.o:test1.cpp:(.text+0x2a4): undefined reference to `_imp___ZN5boost9unit_test9ut_detail24auto_test_unit_registrarC1Ei' C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\cciSdkmB.o:test1.cpp:(.text$_ZN5boost9unit_test14make_test_caseERKNS0_9callback0INS0_9ut_detail6unusedEEENS0_13basic_cstringIKcEE[__ZN5boost9unit_test14make_test_caseERKNS0_9callback0INS0_9ut_detail6unusedEEENS0_13basic_cstringIKcEE]+0x1d): undefined reference to `_imp___ZN5boost9unit_test9ut_detail24normalize_test_case_nameENS0_13basic_cstringIKcEE' C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\cciSdkmB.o:test1.cpp:(.text$_ZN5boost9unit_test14make_test_caseERKNS0_9callback0INS0_9ut_detail6unusedEEENS0_13basic_cstringIKcEE[__ZN5boost9unit_test14make_test_caseERKNS0_9callback0INS0_9ut_detail6unusedEEENS0_13basic_cstringIKcEE]+0x5b): undefined reference to `_imp___ZN5boost9unit_test9test_caseC1ENS0_13basic_cstringIKcEERKNS0_9callback0INS0_9ut_detail6unusedEEE' collect2.exe: error: ld returned 1 exit status
I'm using g++ 4.7.2 on MinGW, with boost 1.52.0.
I get the same errors when only trying to compile
test1.cpp
- except the "multiple main definition" one.I perused the official documentation for quite a while, but its scarce on details regarding linking options. When I compiled the boost libs, besides
unit_test_framework
, I also gotprg_exec_monitor
andtest_exec_monitor
; perhaps I should link these somehow ? I tried many combinations, but all resulted in some kind of undefined reference linker error.Complete list of boost generated libraries - I have them all in the project root:
libboost_prg_exec_monitor-mgw47-mt-1_52.a libboost_prg_exec_monitor-mgw47-mt-1_52.dll libboost_prg_exec_monitor-mgw47-mt-1_52.dll.a libboost_prg_exec_monitor-mgw47-mt-d-1_52.a libboost_prg_exec_monitor-mgw47-mt-d-1_52.dll libboost_prg_exec_monitor-mgw47-mt-d-1_52.dll.a libboost_test_exec_monitor-mgw47-mt-1_52.a libboost_test_exec_monitor-mgw47-mt-d-1_52.a libboost_unit_test_framework-mgw47-mt-1_52.a libboost_unit_test_framework-mgw47-mt-1_52.dll libboost_unit_test_framework-mgw47-mt-1_52.dll.a libboost_unit_test_framework-mgw47-mt-d-1_52.a libboost_unit_test_framework-mgw47-mt-d-1_52.dll libboost_unit_test_framework-mgw47-mt-d-1_52.dll.a
-
Admin over 11 yearsExcellent answer. Was the problem of the multiple definition of main also solved with this? I plan to use something similar in the near future and I'm sure this will be of great help.
-
Mihai Rotaru over 11 years@llonesmiz, yes it was. I'm still not sure why it occurred; couldn't reproduce with the newer libs.
-
Brick about 7 yearsYour directory shows two version of each library. One with a
-d-
in the middle of the name and one without. For example,libboost_unit_test_framework-mgw47-mt-1_52.a
andlibboost_unit_test_framework-mgw47-mt-d-1_52.a
. How did you know which of those to use? -
Mihai Rotaru about 7 years@Brick if I remember correctly, the difference is that the ones with
-d-
include debugging symbols - but I'm not sure to be honest.