-
Notifications
You must be signed in to change notification settings - Fork 17.6k
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
cmd/link: shared object constructor functions are not invoked with internal linker and musl libc #28909
Comments
Is there an official position on the state of musl support? |
We had a Linux Alpine builder at some point, but then it was removed because it was always broken. AFAIK the current status is 1) Go assumes glibc 2) musl may work sometimes, but since it's untested, it is not officially supported |
Thanks for the investigation. My initial take on this is that the musl dynamic linker is not implementing the ELF ABI. I'm not opposed to changing this in Go if there is a clean fix, but offhand I'm not sure what that would look like. |
It looks to me that the internal linker does not link against |
Preamble - I am no expert in ELF or Linux ABI. I will try to digest the information and draw (hopefully) correct conclusions: For me, a core statement in Rich Felkers reply to my inquiry was
It seems, that neither with glibc nor with musl libc Using the external (system) linker or linking crt code with internal linker, solves the Thus, my first take away would be to either promote using the external linker or make internal linker linking crt code. At least users should be made aware about potential consequences using the internal linker. |
@Hollerberg From a glibc perspective, this is mainly a forward compatibility problem. There is a lot of code in Anyway, the current internal linker behavior breaks these things for glibc, as far as I can tell:
I have not checked whether the Go linker uses On the glibc side, we need to take the existing Go binaries into account going forward. It would have been nice if we could have switched to the simpler musl approach (I have a patch that changes |
For what it's worth, the internal linker is only used for pure Go programs that are not linked against any C code. Such programs have no constructors. I suppose it's true that The point of internal linking mode is to permit building Go programs without having a C development environment installed. Admittedly this case is uninteresting on GNU/Linux; it is more useful on Darwin and Windows. |
I see your point - definitely, requiring external linker would reduce Go quality of service for developers. I see three issues for (more or less) pure Go programs:
Could Go startup code call |
Calling I'm perfectly comfortable in saying that anybody who wants to use |
Static vs dynamic linking is not a matter of preference. With glibc and static linking, many things simply do not work, and you do a disservice to your users by pretending otherwise (particularly when they hard-code static linking because it happens to work on amd64 at present, but breaks horribly on other architectures). |
The purpose of internal linking is to avoid requiring C development tools. It wouldn't make sense to start linking against crt1.o, etc., even if we were able to keep up with the many variations. If internal linking can't work, then it can't work; we shouldn't try to make it more like external linking. When using external linking, then of course the external linker is responsible for choosing the startup object files. Regarding static linking, we aren't pretending anything one way or another. Our users want static linking, and they do so by passing |
What version of Go are you using (
go version
)?1.11.2
Does this issue reproduce with the latest release?
yes
What operating system and processor architecture are you using (
go env
)?go env
OutputWhat did you do?
This issue could be the root cause for failing alpine tests in #20120.
I have created docker image with a reproducer for this issue: https://gist.github.com/Hollerberg/5fc64f8abf0f16d4e801c4ad348f21b6
Download the files from the github gist and execute
build-and-run-container.sh
. This script will build the container image. Within the docker build process,libpreload.so
is built frompreload.c
, and the Go applicationgogo.go
is built once togogo-int-linker
using the internal and once togogo-ext-linker
using the external (system) linker.Once the docker image is built and started, the two applications can be started with script
runtest.sh
.The shared object constructor function in
libpreload.so
will write a message to stdout. Preloading the shared object intogogo-int-linker
will not execute the constructor and therefore no message is printed to console. Repeating this withgogo-ext-linker
, the constructor message is printed to console.The root cause for this behavior is a subtle difference between musl and glibc dynamic linker behavior. glibc seems to call shared object constructors in dynamic linker context (
_dl_start_user
), while musl libc executes constructors in__libc_start_main
called by crt1.c. musl libc dynamic linker loads the application and preloaded/depending shared object to memory. Then it jumps to the application entry point. Typically, the application entry point is the code defined in crt1.c - but not in case when the application has been linked with the internal Go linker -__libc_start_main
is never invoked.See also Rich Felkers comments on musl libc mailing list
Short background info on why we are preloading shared objects into Go processes - my company provides an application monitoring solution and the shared object pulls diverse data from the executing Go application. Thus, the use case is very relevant to us in order to be able to support musl libc based systems without constraints.
What did you expect to see?
The constructor function in the preloaded shared object should be invoked before Go application main function.
What did you see instead?
The shared object constructor is not invoked at all.
The text was updated successfully, but these errors were encountered: