当LD_PRELOAD-一个go可执行文件时,共享对象中的构造函数没有被调用。

问题描述 投票:2回答:1

在阿尔卑斯山图像中构建的GO可执行文件中,标准的LD_PRELOAD功能无法正常使用,这是个奇怪的现象。

它看起来像 构造函数不被调用 的动态加载器!我有一个go应用程序的例子

我有一个Go应用程序的例子(getgoogle.go):

package main

import (
    "fmt"
    "net/http"
)

func main() {
    resp, err := http.Get("http://google.com/")
    if err == nil {
        fmt.Println(resp.StatusCode)
    }
}

而共享对象代码的例子libldptest.c)

#include <stdio.h>

static void __attribute__((constructor)) StaticConstructor(int argc, char **argv, char **env)
{
    printf(">>> LD_PRELOADED!\n");
}

我正在用这个Docker文件创建一个基于debian的docker镜像(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

然后运行下面的命令

$docker run -it gotest /getgoogle
>>> LD_PRELOADED!
200

这意味着构造函数在这里工作。

但是当对一个基于阿尔卑斯山的docker镜像做同样的工作时

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

并运行与上述相同的命令

$docker run -it gotest /getgoogle
200
$docker run -it gotest ls
>>> LD_PRELOADED!
bin  src

意思是说,当运行go应用程序时,静态构造函数没有被调用! (但在运行go应用程序时,静态构造函数被调用了 ls)

注意,我已经检查过动态加载器将库添加到进程空间。

如果能理解为什么不能正常工作,我将非常感激。

c go alpine ld-preload
1个回答
0
投票

不要再忽略第一条评论了。如果你坚持使用Go的内部链接器,它的链接方式不兼容libc的使用,那么你就不能使用任何C代码,包括 LD_PRELOAD甚至是动态链接器本身的特性。就像 Florian (来自 glibc) 在链接的问题中所说的那样,它在 glibc 中也是无效的,"工作 "只是偶然的。

即使你以某种方式 "机械地 "找出了为什么你的ctor没有被调用,你仍然在损坏的进程状态下运行C代码,任何事情都可能出错。即使你分析了所有的事情,而且看起来很好,这种情况也会随着下一次动态链接器libc更新而完全改变。

如果你想这样做,请使用Go中的外部链接器选项。


0
投票

在GoAlpine环境中,静态构造函数有一个主要问题,从注释中可以看出。简而言之,从ABI的角度来看,调用静态构造函数的要求是分配给可执行文件而不是加载器的。Go可执行程序是没有基于C运行时的,它只调用依赖共享对象的静态构造函数,而不调用 LD_PRELOAD-的共享对象。在glibc的情况下,一个共享对象的构造函数是由 LD_PRELOAD-的共享对象,而不是由加载器设计调用的。在 musl-libc 上,它们不是。

我做了一个 "黑客 使现有的围棋应用程序能与一个名为 LD_PRELOAD-的共享对象。我使用的事实是,该库是 LD_PRELOAD在这个环境中,musl-libc正确地调用了-ed,而且Go也在调用 pthread_create 在初始化的早期阶段。

我正在重载hooking pthread_create 符号 LD_PRELOAD-的共享对象,并使用它来调用构造函数。

#include <pthread.h>
#include <dlfcn.h>

int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg)
{
    int (*pthread_create_original)(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg) = dlsym(RTLD_NEXT,"pthread_create");
    static int already_called = 0;

    if (!already_called)
    {
        already_called = 1;
        // call here your constructors
    }

    return pthread_create_original(thread,attr,start_routine,arg);
}

注意事项。 这在当前的Go运行时是可行的, 但这个解决方案的假设是建立在未来的基础上的. 下一个Go版本可以很容易地打破它。

© www.soinside.com 2019 - 2024. All rights reserved.