dream
dream

Reputation: 11

glibc versioned symbol and undefined reference to memcpy@GLIBC_2.14

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.

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.

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?

Upvotes: 1

Views: 2812

Answers (1)

juanrgar
juanrgar

Reputation: 11

As for Q3, I think it's because gcc is inlining its builtin version of memcpy. You have to pass -fno-builtin to gcc to force the use of libc's memcpy. Not sure if Q4 is also related to this.

I'm also looking for answers to the other questions. It's not relevant, but the reason libc has multiple memcpy versions is to guarantee backwards compatibility with programs that use memcpy with overlapping regions.

Upvotes: 1

Related Questions