Linker cannot find symbols, but libraries are read and symbols exist
Asked Answered
H

1

8

I've been trying to compile my project and I'm getting undefined reference errors. eg.:

installertest.cpp:(.text+0x9d1): undefined reference to `XmlRpcValue::makeArray()'
...
installertest.cpp:(.text+0xede): undefined reference to `dbcancel'
installertest.cpp:(.text+0xefd): undefined reference to `dbfcmd'
installertest.cpp:(.text+0xf0f): undefined reference to `dbsqlexec'
installertest.cpp:(.text+0xf2d): undefined reference to `SHA1_Init'
...

My command-line is:

g++ -o installertest \
    -lsybdb \
    -lxmlrpc \
    -lxmlrpc_cpp \
    -lxmlrpc_xmlparse \
    -lxmlrpc_xmltok \
    -lxmlrpc_util \
    -lxmlrpc++ \
    -lxmlrpc_server_cgi \
    -lssl \
    -std=c++0x \
    ContractData.o installertest.o

objdump -T shows that the symbols are in the .so file. eg.:

libsybdb.so:
...
0000000000011c30 g    DF .text  0000000000000083  Base        dbcancel
...

/usr/lib/libxmlrpc_cpp.so:
...
0000000000002e78 g    DF .text  0000000000000092  Base        _ZN11XmlRpcValue9makeArrayEv
...

strace shows that the library files are being opened and read by the linker:

...
[pid  5019] stat("/usr/lib/gcc/x86_64-linux-gnu/4.6/../../../x86_64-linux-gnu/libsybdb.so", {st_mode=S_IFREG|0644, st_size=421608, ...}) = 0
[pid  5019] open("/usr/lib/gcc/x86_64-linux-gnu/4.6/../../../x86_64-linux-gnu/libsybdb.so", O_RDONLY) = 7
[pid  5019] fcntl(7, F_GETFD)           = 0
[pid  5019] fcntl(7, F_SETFD, FD_CLOEXEC) = 0
[pid  5019] fstat(7, {st_mode=S_IFREG|0644, st_size=421608, ...}) = 0
[pid  5019] mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x2b16c200c000
[pid  5019] lseek(7, 0, SEEK_SET)       = 0
[pid  5019] read(7, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0P\237\0\0\0\0\0\0"..., 4096) = 4096
...
[pid  5019] stat("/usr/lib/gcc/x86_64-linux-gnu/4.6/../../../../lib/libxmlrpc.so", {st_mode=S_IFREG|0644, st_size=80936, ...}) = 0
[pid  5019] open("/usr/lib/gcc/x86_64-linux-gnu/4.6/../../../../lib/libxmlrpc.so", O_RDONLY) = 8
[pid  5019] fcntl(8, F_GETFD)           = 0
[pid  5019] fcntl(8, F_SETFD, FD_CLOEXEC) = 0
[pid  5019] fstat(8, {st_mode=S_IFREG|0644, st_size=80936, ...}) = 0
[pid  5019] mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x2b16c200d000
[pid  5019] lseek(8, 0, SEEK_SET)       = 0
[pid  5019] read(8, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\300?\0\0\0\0\0\0"..., 4096) = 4096
...

All files involved are targeted to x86-64 and the header for the C libraries are extern "C". I have tried everything I can think of and it still won't link.

I've even tried removing all C++11 code and compiling without the command-line switch, still nothing.

My system is Ubuntu Precise (12.04) 64-bit using g++ (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3 if that helps. All packages were installed from the package manager and development packages are installed.

Edit (2017-05-30): Marked as duplicate of https://mcmap.net/q/64297/-why-does-the-order-in-which-libraries-are-linked-sometimes-cause-errors-in-gcc
Other question asks why order of arguments is important. Argument order was not known to be an issue when question was asked.
Also, previous question does not contain any helpful expansion, whereas this question shows the issue at hand.
Previous question may be seen as a helpful expansion on the answer to this one, but not a duplication.

Hydrocephalus answered 20/6, 2012 at 3:10 Comment(3)
Hi, Ben. Could you please show me how to use the strace to check the process. My main difficulty is about how to get the PID, as the compile process die very quickly. I only use the ps aux | grep to get the PID, but the process is already dead.Vassal
@XingWang, no problem. You simply need to prepend "strace -f -e trace=all" to your command line. Depending on your system, you may also need to add "sudo". So, from my example: "strace -f -e trace=all g++ -o installertest ContractData.o installertest.o "...Hydrocephalus
Possible duplicate of Why does the order in which libraries are linked sometimes cause errors in GCC?Luke
D
14

You have to put the libraries' linker flags after the object files. So, instead of

g++ -o installertest \
-lsybdb \
-lxmlrpc \
-lxmlrpc_cpp \
-lxmlrpc_xmlparse \
-lxmlrpc_xmltok \
-lxmlrpc_util \
-lxmlrpc++ \
-lxmlrpc_server_cgi \
-lssl \
-std=c++0x \
ContractData.o installertest.o

use

g++ -o installertest \
ContractData.o installertest.o \
-lsybdb \
-lxmlrpc \
-lxmlrpc_cpp \
-lxmlrpc_xmlparse \
-lxmlrpc_xmltok \
-lxmlrpc_util \
-lxmlrpc++ \
-lxmlrpc_server_cgi \
-lssl \
-std=c++0x
Donndonna answered 20/6, 2012 at 3:24 Comment(2)
Just tried properly, worked. Really!? Can't believe it was that simple.Hydrocephalus
For anyone else who comes upon this issue, here's the reasoning. It seems that gcc now send the linker flag --as-needed to ld. This has the effect of discarding any specified libraries that do not have symbols that are required for linking. In the first instance, all the libraries were being discarded be cause there were no unresolved symbols then in the linking phase, the symbols could not be found. In the second instance, ld had collected the list of unresolved symbols and then found them in the specified libraries and therefore kept them when it came to the actual linking phase.Hydrocephalus

© 2022 - 2024 — McMap. All rights reserved.