Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Zig's way to link against glibc is quite smart: a decoy
.so
is created that contains all the symbols that a properlibc6.so
would export so that the linker is happy.But this approach has a big downside, the symbols references created this way are unversioned!
Glibc guarantees a stable ABI by exporting versioned symbols and whenever the interface is broken a new one is exported with an increased version number: this way the old applications are still working as intended since they specify what ABI version they expect and the new ones are free to use the improved and shiny stuff.
But what happens when the dynamic linker has to resolve an unversioned reference? Well, let me quote Ulrich Drepper here:
In other words, since the slots are assigned from 2 to ∞ (0 and 1 are reserved) in growing order of version (oldest to newest), we always end up picking the oldest symbol.
Why should you care? Because sometimes the old symbol is not what you want.
Let's consider the
spawnThread
function inos.zig
: it does create a new thread usingpthread_create
and also sets the stack address for it by setting the correspondingpthread_attr_t
.Calling the old symbol means that we end up running this code:
As you can see the legacy code path kindly erases the
stackaddr
andstacksize
values we set but does not care about clearing any unknown bit inflags
, such as the one regarding the two parameter it had just erased, leading to a nasty crash in__pthread_create_2_1
.The patch itself is pretty simple, the new files you see have been autogenerated using a simple Python script from glibc's
.abilist
files, and the resulting files look similar to the ones I have in/lib/i386-linux-gnu/
from aobjdump -T
point of view.This also means that we must decide what glibc version we want to impersonate. I've adopted a simple strategy:
GLIBC 2.17
so that the programs compiled by Zig can be easily run on older systems such as Centos 7.Sorry for the wall of text 🎉