Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

glibc symbol versioning – default symbol versions default to most recent version #11833

Closed
datenwolf opened this issue May 20, 2019 · 6 comments

Comments

@datenwolf
Copy link

datenwolf commented May 20, 2019

NOTE: This is kind of a meta issue, regarding symbol versioning, and maybe even should be escalated to the glibc maintainers. However I became aware of it due to default symbol versioning mismatch comparing void-linux against another distribution, and like to first discuss the issue with the people who have to deal with that on a regular base in the context of maintaining this distribution.

TL;DR: The symbol versioning of some standard library functions that have been around for almost forever do not make a lot of sense. This might be either a glibc issue, or it might be a build configuration problem. Either way, it's semantically not correct and may cause problems for running stuff that's distributed in binary form (only), like games.


A customer whom I've sent a binary build of a library (confidential) I wrote (they just want to use it, not compile themself) reported an error that I immediately recognized as a symbol versioning issue. For the time being I just installed the Linux distribution they were using (Ubuntu 18.04.2 LTS) in a VM and built there. But the whole thing got me sleuthing because I took great care not to use anything "fancy" that'd make my code require a particularly "recent" version of libc.

I've built the library on void-linux, and a quick objdump -T immediately showed what was up:

.../build % objdump -T REDACTED_A.so | egrep 'GLIBC_2\.[0-9][0-9]'
0000000000000000      DF *UND*	0000000000000000  GLIBC_2.14  memcpy
0000000000000000      DF *UND*	0000000000000000  GLIBC_2.17  clock_gettime
0000000000000000      DF *UND*	0000000000000000  GLIBC_2.27  expf
0000000000000000      DF *UND*	0000000000000000  GLIBC_2.29  exp

../build % objdump -T REDACTED_B.so | egrep 'GLIBC_2\.[0-9][0-9]'                                                        
0000000000000000      DF *UND*	0000000000000000  GLIBC_2.14  memcpy
0000000000000000      DF *UND*	0000000000000000  GLIBC_2.29  log2

Seriously‽ With the exception of log2 these functions have been around for like forever. clock_gettime has been moved to glibc starting with version 2.17, before it was in librt.so. And log2 isn't that new either.

So let's see how the void-linux builds of libc.so, librt.so and libm.so export these:

~ % objdump -T /lib/libc.so.6 | egrep ' memcpy$| clock_gettime$'
000000000008b9e0 g   iD  .text	00000000000000b7  GLIBC_2.14  memcpy
00000000000a4640 g    DF .text	0000000000000028 (GLIBC_2.2.5) memcpy
0000000000109530  w   DF .text	000000000000006d  GLIBC_2.17  clock_gettime

~ % objdump -T /lib/librt.so.1 | egrep ' clock_gettime$'
0000000000006000 g   iD  .text	0000000000000008  GLIBC_2.2.5 clock_gettime

~ % objdump -T /lib/libm.so.6 | egrep ' log2$| expf$'
0000000000042ba0 g   iD  .text	000000000000002a  GLIBC_2.27  expf
0000000000015e70 g    DF .text	0000000000000074 (GLIBC_2.2.5) expf
0000000000039a70 g    DF .text	0000000000000302  GLIBC_2.29  log2
0000000000011e40 g    DF .text	000000000000005e (GLIBC_2.2.5) log2

And all I have to say about this is: No! Just no! These functions have been around since almost the dawn of time. log2 was introduced even before "the Beaver was out of detox" (for those unfamiliar with the phrase, that's how Linus announced the release of the 2.6 series kernel). The only thing up there that's sensible is the symbol versioning reflecting the move of clock_gettime.

Now let's compare this with the versioning on the aforementioned Ubuntu installation:

~ % objdump -T /lib/x86_64-linux-gnu/libc.so.6 | egrep ' memcpy$| clock_gettime$'
000000000009f0e0 g   iD  .text	00000000000000ca  GLIBC_2.14  memcpy
00000000000bb460 g    DF .text	0000000000000028 (GLIBC_2.2.5) memcpy
0000000000130e80  w   DF .text	000000000000006d  GLIBC_2.17  clock_gettime

~ % objdump -T /lib/x86_64-linux-gnu/librt.so.1 | egrep ' clock_gettime$'
0000000000005220 g   iD  .text	0000000000000008  GLIBC_2.2.5 clock_gettime

So there's agreement for at least these symbols in libc and librt (different versions nonwithstanding). But in libm there is a discrepancy:

~ % objdump -T /lib/x86_64-linux-gnu/libm.so.6 | egrep ' log2$| expf$'
000000000004a790 g   iD  .text	000000000000002a  GLIBC_2.27  expf
0000000000012770 g    DF .text	0000000000000072 (GLIBC_2.2.5) expf
000000000000e9e0  w   DF .text	000000000000005e  GLIBC_2.2.5 log2

log2 and expf (which BTW have both been introduced at the same time) are versioned differently from the void-linux package. If this is due to different glibc versions or due to different build configurations I cannot tell. But it's irritating. IMHO the symbols versions should default to the version they were introduced, and symbol versions should not be bumped up, if the symbols' ABI didn't change.

@pullmoll
Copy link
Member

Perhaps commit 01592c9 changed what symbol versions are emitted to the glibc solibs. You may try to build your own glibc with --enable-kernel=2.6.32 and see if and how that changes or fixes your issues.

NB Void Linux does not support kernels before 3.16 (anymore).

@datenwolf
Copy link
Author

I don't know how to formulate this diplomatically. Getting an "answer" like that is just annoying. I don't want to be condescending, or – dog forbid – rude, but:

Where in that whole wall of text did I ask about a kernel versioning problem?

Did you actually read what I wrote? It's all about shared object symbol versioning. Nothing of that has anything to do with kernel versions.

@pullmoll
Copy link
Member

So you know more than I know about how glibc's symbol versioning is or is not related to kernel versions. Please excuse my uninformed answer.

@datenwolf
Copy link
Author

Well, it would be helpful, if the answer did actually explain, how the supported kernel version influences the ABI of functions which completely live in userspace. And given the fact that the 2.2.5 versioned symbols are still present, but not the linkage default raises the question why that is?

If there is a good explanation, that's related to the kernel version, then it would be helpful to reference that, or maybe even copy in verbatim. I mean, it certainly makes sense to to version symbols that are tightly coupled to the kernel version. But we're talking about stdlib functions which ABI didn't change in decades. So if there's a deeper reason, then it would be helpful to actually give an explanation why rock stable ABIs demand a symbol version bump.

@jnbr
Copy link
Contributor

jnbr commented May 20, 2019

At the risk of providing more useless information for you, here are my 2 cents:

From the glibc 2.29 NEWS:

Optimized generic exp, exp2, log, log2, pow, sinf, cosf, sincosf and tanf.

Related commit for log2:
https://sourceware.org/git/gitweb.cgi?p=glibc.git;a=commit;h=718d6542f2c0e4c8ec65453c1df6612768d6ef00

@datenwolf
Copy link
Author

@jnbr Well, that is actually useful information. It actually gives an explanation for the symbol version bump. To be fair I expected some internal change (related to error handling) with some wrapper-foo to be the reason for symbol versioning bump. But IMHO it's not very elegant. Alas, not something to be discussed here.

I think I'll escalate this with the glibc guys.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants