Today, when I use conda zlib to compile a binary, I encountered the error Undefined reference to memcpy@GLIBC_2.14.
.../x86_64-conda-linux-gne/bin/ld: ...envs/myenv/lib/libz.so: undefined reference to memcpy@GLIBC_2.14
Although somebody asked similar questions, like this, they cannot solve my problem since I am using third party library.
I then try to understand what is happening. I did the following experiments:
~ $ ldd $CONDA_PREFIX/lib/libz.so
linux-vdso.so.1 (0x00007ffcc4a90000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fe449c1a000)
/lib64/ld-linux-x86-64.so.2 (0x00007fe449e70000)
~ $ nm $CONDA_PREFIX/lib/libz.so | grep memcpy
U memcpy@@GLIBC_2.14
~ $ nm -gD /lib/x86_64-linux-gnu/libc.so.6 | grep ' memcpy'
00000000000c7a30 T memcpy@GLIBC_2.2.5
00000000000ad1e0 i memcpy@@GLIBC_2.14
Q1: @@ vs @ in the versioned symbols
Why does nm libz.so above show memcpy@@GLIBC_2.14 instead of memcpy@GLIBC_2.14.
From all-about-symbol-versioning, I learnt that @@ is the default to be used if a symbol without version is requested. But If the libz.so explicitly asks for memcpy@@GLIBC_2.14, shouldn't the error be undefined reference to memcpy@@GLIBC_2.14?
Q2: what does i mean in the output. man nm says
For ELF format files this indicates that the symbol is an indirect function. This is a GNU extension to the standard set of ELF symbol types. It indicates a symbol which if referenced by a relocation does not evaluate to its address, but instead must be invoked at runtime. The runtime execution will then return the value to be used in the relocation.
But I cannot understand what it means.
- Does the
libc.soI have providememcpy@@GLIBC_2.14for others to link against?
Q3: why my following code does not depend on symbol memcpy
I then coded a simple foo.c file
#include <stdio.h>
#include <string.h>
int main()
{
char from[10] = "hello";
char to[10] = "";
printf("from <%s> to <%s>\n", from, to);
memcpy(to, from, 10);
printf("from <%s> to <%s>\n", from, to);
}
Compiling it gcc -c foo.c and then nm foo.o, I see the following:
U _GLOBAL_OFFSET_TABLE_
0000000000000000 T main
U printf
It has U printf, but not U memcpy. Why? Is this related to the i type in Q?
Q4: why adding __asm__ has no effect
I added a line __asm__(".symver memcpy,memcpy@GLIBC_2.14"); to foo.c, the result is the same. I then changed it to one of the following. Strangely, all of them will be compiled successfully, even some of them contains various typos.
__asm__(".symver memcpy,memcpy@GLIBC_2.14");__asm__(".symver memcpy,memcpy@GLIBC_2.18");2.18 does not exist__asm__(".symver memcpy,xxxxxx@GLIBC_2.18");
It seems it has no effect for memcpy. I tested it for other functions like foo,foo@v1 and it works only if foo@v1 exists as an exported symbol. So I think the syntax and mechanism for versioned symbols is correct when foo is used; but memcpy is special.
How to explain this?