Is it possible to statically link libstdc++ and wrap memcpy?
Asked Answered
N

1

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.

Nicol answered 6/4, 2016 at 20:21 Comment(6)
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.Doughty
Thanks for the reply. Unfortunately, we rely on third party libraries that have a dependency on the new memcpy (ex. boost). Also #include <iostream> adds a dependency to memcpy as well.Nicol
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?)Doughty
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++.Nicol
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.Doughty
This question and answer have saved me days of work. ThanksDrawbar
T
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
Thievish answered 11/4, 2016 at 15:28 Comment(2)
This question and answer have saved me days of work. ThanksDrawbar
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.Quilt

© 2022 - 2024 — McMap. All rights reserved.