I manage a complex C++ project
I am getting field reports that a binary built on one system will not run on another system because of glibc version mismatch.
I just want to verify: is the version of glibc required by the binary completely determined by which version of g++ is used to compile it?
In this case, the binary is built with g++ 11, and run on another system that doesn't have g++ 11 installed.
I just want to verify: is the version of glibc required by the binary completely determined by which version of g++ is used to compile it?
Not at all. The version of GLIBC required at runtime is determined by several factors:
In this case, the binary is built with g++ 11
You can install g++-11
on a system with GLIBC-2.15, and on a system with GLIBC-2.31. For non-trivial source, the resulting binaries are likely to have different requirements on GLIBC at runtime.
See this answer to understand how symbol versioning comes into play.
TL;DR: if you want to build an executable or a shared library which is portable to different Linux distributions, select the oldest version of GLIBC you are willing to support, and build on a system with that version (you don't have to have a physical machine for this -- a VM or a docker container work find).
Due to backwards compatibility guarantees, your binary will work on all systems with the same or newer version of GLIBC.
Update:
Let's say that you build libfoo.a
on GLIBC-NN, and the end user links a.out
on GLIBC-MM, where MM < NN
.
This is a very dangerous practice: the program may silently corrupt its data, and finding the root cause of such corruption will be very difficult.
Now suppose the end user links a.out
on GLIBC-MM where NN <= MM
(this is the actual situation according to comments), then runs a.out
on a system with GLIBC-JJ, where JJ < NN
.
In that case, I the possibility of "silent" corruption is minimized, but not completely eliminated.
Consider the following hypothetical example (foo()
is part of libfoo.a
you provide):
struct passwd *foo()
{
struct passwd *pw = getpwuid(42);
if (pw->field_a == 123) // 1
pw->field_a = 1234; // 2
return pw;
}
Now suppose that in GLIBC-NN field_a
exists as part of struct passwd
, but in GLIBC-JJ it does not. In that case, at point 1
above the program may read past an end of allocated buffer, and at point 2
it may write past the end.
Note: the program above is already violating "application shall not modify the structure to which the return value points" requirement and so has undefined behavior anyway; it's just an example of how bugs due to mismatched GLIBC may come about.