Repository: go
Issue: cmd/link: shared object constructor functions are not invoked with internal linker and musl libc
Labels: help wanted,NeedsInvestigation,compiler/runtime

<!-- Please answer these questions before submitting your issue. Thanks! -->

### What version of Go are you using (`go version`)?
1.11.2

<pre>
$ go version
go version go1.11.2 linux/amd64
</pre>

### Does this issue reproduce with the latest release?
yes


### What operating system and processor architecture are you using (`go env`)?

<details><summary><code>go env</code> Output</summary><br><pre>
$ go env
GOARCH="amd64"
GOBIN=""
GOCACHE="/root/.cache/go-build"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOOS="linux"
GOPATH="/go"
GOPROXY=""
GORACE=""
GOROOT="/usr/local/go"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/linux_amd64"
GCCGO="gccgo"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD=""
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build960632520=/tmp/go-build -gno-record-gcc-switches"

</pre></details>

### What did you do?

- Create a dynamically linked Go application with the internal linker.
- Create a shared object (DLL) which has a constructor function - e.g. a C function attributed with \_\_attribute\_\_((constructor))
- Use LD_PRELOAD to preload the shared object into the Go application process - e.g LD_PRELOAD=libsotest.so ./myapp

<!--
If possible, provide a recipe for reproducing the error.
A complete runnable program is good.
A link on play.golang.org is best.
-->

This issue could be the root cause for failing alpine tests in https://github.com/golang/go/issues/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 from `preload.c`, and the Go application `gogo.go` is built once to `gogo-int-linker` using the internal and once to `gogo-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 into `gogo-int-linker` will not execute the constructor and therefore no message is printed to console. Repeating this with `gogo-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](https://git.musl-libc.org/cgit/musl/tree/crt/crt1.c). musl libc dynamic linker loads the application and preloaded/depending shared object to memory. Then it [jumps to the application entry point](https://git.musl-libc.org/cgit/musl/tree/ldso/dynlink.c#n1742). Typically, the application entry point is the code defined in [crt1.c](https://git.musl-libc.org/cgit/musl/tree/crt/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](https://www.openwall.com/lists/musl/2018/11/21/3)

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.