为什么 dlsym 在 cgo 中产生的结果与在 c 中产生的结果不同?

我有两个相同行为的实现,我认为它们应该产生相同的结果,但却产生了不同的结果。在 Go 中使用 编译时cgo,我得到的符号地址解析与在 C 中编译时不同。我想了解原因。


我将问题简化为几个小例子,一个用 C 语言,一个用 Go 语言。我在我的 Mac 笔记本电脑上运行的 Ubuntu 18 Docker 容器中测试了这些。


测试.c:


// gcc test.c -D_GNU_SOURCE -ldl

// Output: Real: 0x7fd05559d7d0 Current: 0x7fd05559d7d0


#include <dlfcn.h>

#include <stdio.h>


int main() {

    void * fd = dlopen("libc.so.6", RTLD_LAZY);

    void * real_sym = dlsym(fd, "accept");

    void * curr_sym = dlsym(RTLD_NEXT, "accept");

    printf("Real: %p Current: %p\n", real_sym, curr_sym);

    return 0;

}

测试去:


// go build test.go

// Output: Real: 0x7f264583b7d0 Current: 0x7f2645b1b690

package main


// #cgo CFLAGS: -D_GNU_SOURCE

// #cgo LDFLAGS: -ldl

// #include <dlfcn.h>

import "C"

import "fmt"


func main() {

    fp := C.dlopen(C.CString("libc.so.6"), C.RTLD_LAZY)

    real_sym := C.dlsym(fp, C.CString("accept"))

    curr_sym := C.dlsym(C.RTLD_NEXT, C.CString("accept"))

    fmt.Printf("Real: %p Current: %p\n", real_sym, curr_sym)

}

Real: 0x7fd05559d7d0 Current: 0x7fd05559d7d0我得到了when test.cgets compiled ( )的输出gcc test.c -D_GNU_SOURCE -ldl。但是,当我构建时,test.go我看到了Real: 0x7f264583b7d0 Current: 0x7f2645b1b690.


我假设 go 本身正在包装一些符号,但我想知道到底发生了什么。谢谢!


看到一些最初的评论后,再补充几件。我更改test.c如下,然后循环运行(while [ 1 ]; do   ./a.out; done)。它一直为我获得相同的地址(虽然每次运行都不同)。


// gcc test.c -D_GNU_SOURCE -ldl

// Output: Real: 0x7fd05559d7d0 Current: 0x7fd05559d7d0


#include <dlfcn.h>

#include <stdio.h>


    int main() {

    void * fd = dlopen("libc.so.6", RTLD_LAZY);

    void * real_sym = dlsym(fd, "accept");

    void * curr_sym = dlsym(RTLD_NEXT, "accept");

    if(real_sym != curr_sym) {

        printf("Real: %p Current: %p\n", real_sym, curr_sym);

    }

    return 0;

}

我还尝试修改 Go 代码以检查它是否与 Go 调用 C 的方式有关,但地址仍然不匹配:


// go build dos.go

// Output: Real: 0x7f264583b7d0 Current: 0x7f2645b1b690

package main


另一点是,如果我查找符号而malloc不是accept.


拉风的咖菲猫
浏览 135回答 2
2回答

慕侠2389804

原因是 Go 链接到 libpthread,但你的 C 程序没有。如果我添加-lpthread到 gcc 参数,它也会打印不同的指针。因此,libpthread 定义了它自己的accept并覆盖了 libc 的(这是有道理的)。我想出来的方法是,我在两个程序中都插入了一个睡眠,然后翻查/proc/$pid/maps以查看返回的指针引用的内容。这表明在 Go 的例子中,“当前”指针驻留在 libpthread 中。“真正的”指针总是引用 libc。

小怪兽爱吃肉

符号不会加载到内存中的固定地址;他们去装载机决定放置它们的任何地方。这是我在我的机器上多次运行你的 C 程序的输出。govind@Govind-PC:/mnt/c/Temp$ ./dlstReal: 0x7f4b5f3127d0 Current: 0x7f4b5f26ee30govind@Govind-PC:/mnt/c/Temp$ ./dlstReal: 0x7f45727127d0 Current: 0x7f457266ee30govind@Govind-PC:/mnt/c/Temp$ ./dlstReal: 0x7fc3373127d0 Current: 0x7fc33726ee30govind@Govind-PC:/mnt/c/Temp$ ./dlstReal: 0x7f0e555127d0 Current: 0x7f0e5546ee30govind@Govind-PC:/mnt/c/Temp$ ./dlstReal: 0x7f2fdd9127d0 Current: 0x7f2fdd86ee30govind@Govind-PC:/mnt/c/Temp$ ./dlstReal: 0x7fec7db127d0 Current: 0x7fec7da6ee30govind@Govind-PC:/mnt/c/Temp$ ./dlstReal: 0x7f07de1127d0 Current: 0x7f07de06ee30govind@Govind-PC:/mnt/c/Temp$
打开App,查看更多内容
随时随地看视频慕课网APP