7

I am trying to build an executable on Linux that meets the following criteria:

  1. statically linked to libstdc++ and libgcc
  2. built with a recent version of gcc (version >= 4.8.2) and glibc (version > 2.14)
  3. backwards compatible with old versions of glibc (version < 2.5)

My current dev environment is gcc4.8.5, glibc 2.17 on CentOS 7. The binary built does not work on systems with glibc < 2.14 due to a dependency on memcpy.

objdump -T main | fgrep GLIBC_2.14
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.14  memcpy

There was a breaking change to memcpy introduced in glibc 2.14 so I want to force the use of an older version. I came across this stackoverflow post Linking against older symbol version in a .so file but it did not work for me due to linker issues related to libstdc++. Here's my attempt at a solution below.

main.cpp

#include <iostream>
#include <string.h>

int main(int argc, char** argv)
{
    char source[] = "once upon a midnight dreary...", dest[4];
    memcpy(dest, source, sizeof dest);
    std::cout << dest << std::endl;
}

wrap_memcpy.cpp

#include <string.h>

__asm__(".symver memcpy, memcpy@GLIBC_2.2.5");

void *__wrap_memcpy(void *dest, const void *src, size_t n)
{
    return memcpy(dest, src, n);
}

compiler options and errors:

g++ -static-libgcc -static-libstdc++ wrap_memcpy.cpp main.cpp  -o main -Wl,--wrap=memcpy

/usr/lib/gcc/x86_64-redhat-linux/4.8.5/libstdc++.a(locale-inst.o): In function `std::ctype<char>::widen(char const*, char const*, char*) const':
(.text._ZNKSt5ctypeIcE5widenEPKcS2_Pc[_ZNKSt5ctypeIcE5widenEPKcS2_Pc]+0x5f): undefined reference to `__wrap_memcpy'
/usr/lib/gcc/x86_64-redhat-linux/4.8.5/libstdc++.a(locale-inst.o): In function `std::__timepunct<char>::__timepunct(__locale_struct*, char const*, unsigned long)':
(.text._ZNSt11__timepunctIcEC2EP15__locale_structPKcm[_ZNSt11__timepunctIcEC5EP15__locale_structPKcm]+0x96): undefined reference to `__wrap_memcpy'
/usr/lib/gcc/x86_64-redhat-linux/4.8.5/libstdc++.a(locale-inst.o): In function `std::messages<char>::messages(__locale_struct*, char const*, unsigned long)':
(.text._ZNSt8messagesIcEC2EP15__locale_structPKcm[_ZNSt8messagesIcEC5EP15__locale_structPKcm]+0x8e): undefined reference to `__wrap_memcpy'
/usr/lib/gcc/x86_64-redhat-linux/4.8.5/libstdc++.a(locale-inst.o): In function `std::messages_byname<char>::messages_byname(char const*, unsigned long)':
(.text._ZNSt15messages_bynameIcEC2EPKcm[_ZNSt15messages_bynameIcEC5EPKcm]+0xd6): undefined reference to `__wrap_memcpy'
/usr/lib/gcc/x86_64-redhat-linux/4.8.5/libstdc++.a(locale-inst.o): In function `std::__numpunct_cache<char>::_M_cache(std::locale const&)':
(.text._ZNSt16__numpunct_cacheIcE8_M_cacheERKSt6locale[_ZNSt16__numpunct_cacheIcE8_M_cacheERKSt6locale]+0x2ad): undefined reference to `__wrap_memcpy'
/usr/lib/gcc/x86_64-redhat-linux/4.8.5/libstdc++.a(locale-inst.o):(.text._ZNSt16__numpunct_cacheIcE8_M_cacheERKSt6locale[_ZNSt16__numpunct_cacheIcE8_M_cacheERKSt6locale]+0x2cd): more undefined references to `__wrap_memcpy' follow
collect2: error: ld returned 1 exit status

What am I doing wrong here? I've also attempted other solutions in the stack overflow post and I get the same error. I've also tried building this on glibc 5.2.1 on Ubuntu 15.0.4 and got the same result. Note that statically linking memcpy (which is under the GPL license) into the binary is not an option because of licensing issues.

Community
  • 1
  • 1
yoshimonster
  • 147
  • 2
  • 7
  • The path of least resistance is probably to globally search and replace all uses of `memcpy` in your code with `memmove`, and then you won't *need* the old `memcpy`. Alternatively, fix the code that depends on non-guaranteed `memcpy` behavior, but if that were easy you'd've already done it. – zwol Apr 06 '16 at 20:25
  • Thanks for the reply. Unfortunately, we rely on third party libraries that have a dependency on the new memcpy (ex. boost). Also #include adds a dependency to memcpy as well. – yoshimonster Apr 06 '16 at 20:34
  • Maybe I don't understand your problem. If your code does not need the non-guaranteed behavior of the old `memcpy`, then why are you trying to use it? (Put another way, why is it a problem for your binary to refer to the new `memcpy`?) – zwol Apr 06 '16 at 23:34
  • It's a problem for my binary to use the new memcpy because it will add a symbol "memcpy@GLIBC_2.14". On older linux distributions, that symbol will not resolve. Therefore, I want to force the use of an old memcpy. I copied a solution I saw on stackoverflow by wrapping memcpy but it causes problems when I statically link libstdc++. – yoshimonster Apr 07 '16 at 00:51
  • 1
    Unfortunately, if that's your requirement, then your only choice is to do your building on an older linux distribution with an older glibc and possibly also an older libstdc++ (depending on what your requirements for that are). None of the various hacks to attempt to avoid this actually work. – zwol Apr 07 '16 at 11:40
  • This question and answer have saved me days of work. Thanks – Neil Wightman Apr 27 '16 at 07:01

1 Answers1

7

You need to wrap __wrap_memcpy in extern "C" {}, so that this function is is exported as a C function; otherwise its name will be decorated as a C++ function. In addition I strongly suggest some additional #ifdef, because this problem only occurs with later compiler versions and only for x64 (the conditions are not totally perfect, so may need to adjust them):

#if defined( __GNUC__ )  &&  defined( __LP64__ )  &&  __LP64__ >= 1  && \
(_GNUC__ >= 5  ||  (__GNUC__ == 4  &&  __GNUC_MINOR__ >= 7))  &&  \
(defined( __x86_64__ )  ||  defined( __i386__ )  ||\
defined( __i486__ )  ||  defined( __i586__ )  ||  defined( __i686__ ))

#include <string.h>

__asm__(".symver memcpy, memcpy@GLIBC_2.2.5");

extern "C"
{
void *__wrap_memcpy(void *dest, const void *src, size_t n)
{
    return memcpy(dest, src, n);
}
}

#endif
Klingon
  • 186
  • 5
  • This question and answer have saved me days of work. Thanks – Neil Wightman Apr 27 '16 at 07:01
  • 1
    In case you have a 32-Bit platform be aware that the correct previous version for `memcpy` is then `memcpy@GLIBC_2.0` and not `memcpy@GLIBC_2.2.5`. You could handle both cases using a macro in your code. – flyingdutchman Nov 14 '18 at 14:31