There is a strange behavior around GO executable built in Alpine images where standard LD_PRELOAD feature is not working correctly.
It looks like constructor functions are not called by the dynamic loader!
I have an example go application (getgoogle.go
):
package main
import (
"fmt"
"net/http"
)
func main() {
resp, err := http.Get("http://google.com/")
if err == nil {
fmt.Println(resp.StatusCode)
}
}
And the example shared object code (libldptest.c
)
#include <stdio.h>
static void __attribute__((constructor)) StaticConstructor(int argc, char **argv, char **env)
{
printf(">>> LD_PRELOADED!\n");
}
I am creating a debian based docker image with this Dockerfile (gotest
image):
FROM golang
COPY libldptest.c hello-world.go /
RUN gcc -shared -o /libldptest.so /libldptest.c
RUN go build -gcflags='-N -l' -o /getgoogle /getgoogle.go
ENV LD_PRELOAD=/libldptest.so
Then running the following command:
$docker run -it gotest /getgoogle
>>> LD_PRELOADED!
200
This means the constructor works here.
But when doing the same with an alpine based docker image
FROM golang:1.12-alpine
RUN apk add gcc libc-dev
COPY libldptest.c hello-world.go /
RUN gcc -shared -o /libldptest.so /libldptest.c
RUN go build -gcflags='-N -l' -o /getgoogle /getgoogle.go
ENV LD_PRELOAD=/libldptest.so
And running the same command as above
$docker run -it gotest /getgoogle
200
$docker run -it gotest ls
>>> LD_PRELOADED!
bin src
Meaning the static constructor was not called when running the go application! (but is was called when running ls
)
Note that I have checked that the dynamic loader adds the library to the process space.
I'd be grateful to understand why it is not working.