为什么 dlsym 在 cgo 中产生的结果与在 c 中产生的结果不同?
Why does dlsym produce different results in cgo than in c?
我有两个相同行为的实现,我认为它们应该产生相同的结果,但却产生了不同的结果。当使用 cgo
在 Go 中编译时,我得到的符号地址解析与在 C 中编译时不同。我想了解为什么。
我将问题简化为几个小例子,一个用 C 语言,一个用 Go 语言。我在我的 Mac 笔记本电脑上的 Ubuntu 18 Docker 容器 运行ning 中测试了这些。
test.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;
}
test.go:
// 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)
}
当 test.c
被编译 (gcc test.c -D_GNU_SOURCE -ldl
) 时,我得到了 Real: 0x7fd05559d7d0 Current: 0x7fd05559d7d0
的输出。但是,当我构建 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
// #cgo CFLAGS: -D_GNU_SOURCE
// #cgo LDFLAGS: -ldl
// #include <dlfcn.h>
// #include <stdio.h>
// int doit() {
// 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;
// }
import "C"
func main() {
C.doit()
}
另一点是,如果我寻找 malloc
符号而不是 accept
.
,我会在 C 和 Go 代码中得到两个匹配的地址
符号没有加载到内存中的固定地址;他们去装载机决定放置它们的任何地方。
这是我 运行 你的 C 程序在我的机器上多次的输出。
govind@Govind-PC:/mnt/c/Temp$ ./dlst
Real: 0x7f4b5f3127d0 Current: 0x7f4b5f26ee30
govind@Govind-PC:/mnt/c/Temp$ ./dlst
Real: 0x7f45727127d0 Current: 0x7f457266ee30
govind@Govind-PC:/mnt/c/Temp$ ./dlst
Real: 0x7fc3373127d0 Current: 0x7fc33726ee30
govind@Govind-PC:/mnt/c/Temp$ ./dlst
Real: 0x7f0e555127d0 Current: 0x7f0e5546ee30
govind@Govind-PC:/mnt/c/Temp$ ./dlst
Real: 0x7f2fdd9127d0 Current: 0x7f2fdd86ee30
govind@Govind-PC:/mnt/c/Temp$ ./dlst
Real: 0x7fec7db127d0 Current: 0x7fec7da6ee30
govind@Govind-PC:/mnt/c/Temp$ ./dlst
Real: 0x7f07de1127d0 Current: 0x7f07de06ee30
govind@Govind-PC:/mnt/c/Temp$
另请参阅:
原因是 Go 链接到 libpthread,但你的 C 程序没有。如果我将 -lpthread
添加到 gcc 参数,它也会打印不同的指针。因此,libpthread 定义了它自己的 accept
并覆盖了 libc 的(这是有道理的)。
我想出来的方法是,我在两个程序中都插入了一个睡眠,然后翻遍 /proc/$pid/maps
以查看返回的指针引用的内容。这表明在 Go 的例子中,"current" 指针驻留在 libpthread 中。 "real" 指针始终引用 libc。
我有两个相同行为的实现,我认为它们应该产生相同的结果,但却产生了不同的结果。当使用 cgo
在 Go 中编译时,我得到的符号地址解析与在 C 中编译时不同。我想了解为什么。
我将问题简化为几个小例子,一个用 C 语言,一个用 Go 语言。我在我的 Mac 笔记本电脑上的 Ubuntu 18 Docker 容器 运行ning 中测试了这些。
test.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;
}
test.go:
// 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)
}
当 test.c
被编译 (gcc test.c -D_GNU_SOURCE -ldl
) 时,我得到了 Real: 0x7fd05559d7d0 Current: 0x7fd05559d7d0
的输出。但是,当我构建 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
// #cgo CFLAGS: -D_GNU_SOURCE
// #cgo LDFLAGS: -ldl
// #include <dlfcn.h>
// #include <stdio.h>
// int doit() {
// 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;
// }
import "C"
func main() {
C.doit()
}
另一点是,如果我寻找 malloc
符号而不是 accept
.
符号没有加载到内存中的固定地址;他们去装载机决定放置它们的任何地方。
这是我 运行 你的 C 程序在我的机器上多次的输出。
govind@Govind-PC:/mnt/c/Temp$ ./dlst
Real: 0x7f4b5f3127d0 Current: 0x7f4b5f26ee30
govind@Govind-PC:/mnt/c/Temp$ ./dlst
Real: 0x7f45727127d0 Current: 0x7f457266ee30
govind@Govind-PC:/mnt/c/Temp$ ./dlst
Real: 0x7fc3373127d0 Current: 0x7fc33726ee30
govind@Govind-PC:/mnt/c/Temp$ ./dlst
Real: 0x7f0e555127d0 Current: 0x7f0e5546ee30
govind@Govind-PC:/mnt/c/Temp$ ./dlst
Real: 0x7f2fdd9127d0 Current: 0x7f2fdd86ee30
govind@Govind-PC:/mnt/c/Temp$ ./dlst
Real: 0x7fec7db127d0 Current: 0x7fec7da6ee30
govind@Govind-PC:/mnt/c/Temp$ ./dlst
Real: 0x7f07de1127d0 Current: 0x7f07de06ee30
govind@Govind-PC:/mnt/c/Temp$
另请参阅:
原因是 Go 链接到 libpthread,但你的 C 程序没有。如果我将 -lpthread
添加到 gcc 参数,它也会打印不同的指针。因此,libpthread 定义了它自己的 accept
并覆盖了 libc 的(这是有道理的)。
我想出来的方法是,我在两个程序中都插入了一个睡眠,然后翻遍 /proc/$pid/maps
以查看返回的指针引用的内容。这表明在 Go 的例子中,"current" 指针驻留在 libpthread 中。 "real" 指针始终引用 libc。