10

My issue originated with a shared library I was given without the option to recompile the library. The error stated undefined reference to memcpy@GLIBC_2.14.

The version of GLIBC on my machine was 2.12. I have seen fixes people have done online using the line

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

The fix I made was using a hex editor to change the reference of 2.14 to GLIBC_2.2.5. When executing the command readelf -V lib_name.so, the outputs changed from:

0x0060  Name: GLIBC_2.14 Flags: none Version 6
......
0x0080  Name: GLIBC_2.2.5 Flags: none Version 4

to:

0x0060  Name: GLIBC_2.2.5 Flags: none Version 6
......
0x0080  Name: GLIBC_2.2.5 Flags: none Version 4

This fixed my error. What I want to know is what effects this will have. I have attempted to research memcpy vs. memmove and the change to memcpy starting in GLIBC_2.14, but I do not quite understand what is going on and what the original problem with memcpy was. I am worried about this "fix", although it allows my program to run, in case whatever memcpy is doing is not behaving correctly. Why do all the fixes I have seen online specifically link to the version 2.2.5?

I would appreciate if anyone could give me some insight on this topic or provide some links with relevant info.

Sven Schoenung
  • 30,224
  • 8
  • 65
  • 70
PickleWeasel
  • 101
  • 1
  • 4

1 Answers1

9

What I want to know is what effects this will have.

The most likely effect is that the first time your 3rd party library calls memcpy, it will crash.

There is a reason a new version of memcpy@GLIBC_2.14 was introduced: it's not ABI-compatible with the old memcpy (what happened here is that as of GLIBC-2.14, memcpy is a GNU_IFUNC, which means it returns the address of actual memcpy; the code in the 3rd party library will then call the returned routine. But the return value of memcpy@GLIBC_2.2.5 is the destination parameter and not a function address, so you are expected to immediately crash).

if anyone could give me some insight

The library you were given requires GLIBC-2.14. By running it on a GLIBC-2.12 machine, you've voided all warranties. Your best bet is to either:

  • work with 3rd party vendor to get a version of the library compatible with your execution environment, or
  • make your execution environment compatible with the library you are given (i.e. update your OS). You should probably do that anyway so your system can't be powned by recent vulnerabilities, such as CVE-2015-7547.

Update:

I'm not using the returned value from memcpy

You didn't understand how GNU_IFUNCs work. Here is a description. The problem is that while you aren't using the return value, the dynamic linker does.

The code inside dynamic linker does something like:

if (symbol type == STT_GNU_IFUNC) {
  // call the IFUNC to get an address of the actual implementation
  void (*pfun)() = memcpy();
  // call the actual (non-IFUNC) implementation of memcpy.
  return (*pfun)(to, from, size);  // You will crash here!
}

By substituting non-ifunc version for an infunc-version via asm hack, you guaranteed that pfun == to, and so your to is getting called as if it were a function. That should normally immediately SIGSEGV.

Employed Russian
  • 199,314
  • 34
  • 295
  • 362
  • _to get a version of the library compatible with your execution environment_ Is there any way to cross compile a library or executable so the it will require older version of glibc than the one used in the GCC toolchain? – scrutari Mar 16 '16 at 21:27
  • 3
    @starfury Yes, there is, but it's not trivial: http://stackoverflow.com/a/8658468/50617 – Employed Russian Mar 17 '16 at 01:12
  • Thank you very much, what if it is only my code that is using `memcpy` and my compiler makes is `GLIBC_2.14` and I have to put this `__asm__` hack to make it run on `GLIBC-2.12` machine , and I'm not using the *returned* value from `memcpy` which has been changed between the 2 versions, I just use it like this `memcpy(to, from 100);` **am I still in danger of crashes because of this `__asm__` hack ?** – Accountant م Apr 19 '19 at 22:56
  • that means it **must** crash because the execution will jump to an unpredictable address , so why it didn't crash with me ? – Accountant م Apr 20 '19 at 00:35
  • 1
    @Accountantم Oh, if it doesn't crash, then my explanation probably isn't correct. Your hack may work by chance, or I am missing some other reason it works. – Employed Russian Apr 20 '19 at 01:22
  • @EmployedRussian I will implement my own memcpy function as a quick fix to my little C program `for(i=0; i – Accountant م Apr 20 '19 at 01:52
  • @EmployedRussian almost every question here on SO regarding linking/libraries/debugging and these stuff, I see great answers from you (you explain hard things in a very easy way) -*you should consider writing a book*- thank you very much , you made me like the mammoth :D – Accountant م Apr 20 '19 at 01:52
  • 2
    Ummmm... I guess it didn't crash because this hack is a backward compatible, **not forward**. It will crash if we run an old `memcpy` on a new linker which expects the address to be returned, but here we are doing the opposite, we run the new `memcpy` on an old linker, which does not expect the address to be returned so no crashes happened, is that correct ? – Accountant م Apr 20 '19 at 02:30
  • @Accountantم yes, with this hack, you are just telling your application to request the older version of `memcpy` which is available for all glibc since 2.2.5 as glibc is backward compatible. This does not change behavior of the linker which is an other independant program. We would have a lot of problems if running a simple program compiled years ago would crash the linker on the system. – Nicolas Dusart Oct 03 '19 at 09:34
  • @EmployedRussian I think the reason the hack worked was that both `memcpy@GLIBC_2.2.5` and `memcpy@@GLIBC_2.14` are IFUNC. Their difference is in optimization and overlapping behaviour. – Yongwei Wu Apr 11 '23 at 02:41
  • @YongweiWu Support for `STT_GNU_IFUNC` was added in GLIBC-2.10. There is _no way_ `memcpy@GLIBC_2.2.5` could be an `IFUNC`. Running `readelf -Ws libc.so.6 | grep memcpy@GLIBC_2.2.5` confirms that it's not. – Employed Russian Apr 11 '23 at 05:55
  • @EmployedRussian Interesting ... it seems different distros may have different behaviour. On Ubuntu, `readelf -Ws /lib/x86_64-linux-gnu/libc.so.6 | grep memcpy` shows that only `memcpy@@GLIBC_2.14` is IFUNC, but on SuSE `readelf -Ws /lib64/libc.so.6 | grep memcpy` shows that both `memcpy@GLIBC_2.2.5` and `memcpy@@GLIBC_2.14` are IFUNC! – Yongwei Wu Apr 11 '23 at 06:32
  • @YongweiWu That is likely a bug in SuSE build. If you try to run an old binary on it (an executable built with GLIBC version before 2.14) that binary should crash. – Employed Russian Apr 11 '23 at 14:36
  • @EmployedRussian I copied a program that was compiled on Ubuntu to SuSE (forcing the use of `memcpy@GLIBC_2.2.5`), and it still ran OK. I think the key is likely that the IFUNC attribute it not recorded in the exectuable. The loader will get the real function address on loading; and, according to the documentation, "the resolver function is called by the dynamic loader during early startup to resolve which of the implementations will be used by the application". Resolving does not happen at call time.—So my comment about why the hack worked was wrong too.... – Yongwei Wu Apr 12 '23 at 01:31