doufen5175 2019-01-29 21:45
浏览 146
已采纳

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

I have two implementations of the same behavior that I believe should produce the same results but are instead producing different results. When compiled in Go using cgo, I get a different symbol address resolution than when compiled in C. I would like to understand why.

I reduced the problem to a couple of small examples, one in C and one in Go. I tested these in an Ubuntu 18 Docker container running on my Mac laptop.

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
", 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
", real_sym, curr_sym)
}

I get the output of Real: 0x7fd05559d7d0 Current: 0x7fd05559d7d0 when test.c gets compiled (gcc test.c -D_GNU_SOURCE -ldl). However, when I build test.go I see Real: 0x7f264583b7d0 Current: 0x7f2645b1b690.

I assume that go is wrapping some symbols itself, but I would like to know exactly what's happening. Thanks!


A couple of extra pieces after seeing some of the initial comments. I changed test.c as below and then ran in a loop (while [ 1 ]; do ./a.out; done). It's consistently getting equal addresses for me (different each run, though).

// 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
", real_sym, curr_sym);
    }
    return 0;
}

I also tried a modification of the Go code to check if it had to do with how Go called out to C, but that still did not have the addresses match:

// 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
", real_sym, curr_sym);
//     return 0;
// }
import "C"

func main() {
    C.doit()
}

Another point is that I get the two addresses to match in both the C and Go code if I look for the malloc symbol instead of accept.

  • 写回答

2条回答 默认 最新

  • dsovc00684 2019-01-31 06:49
    关注

    The reason is that Go links against libpthread, but your C program doesn't. If I add -lpthread to the gcc arguments, it prints different pointers too. So, libpthread defines its own accept and overrides the libc one (which kind of makes sense).

    The way I figured that out is that I inserted a sleep into both programs and then rummaged through /proc/$pid/maps to see what the returned pointers reference. That showed that in Go's case, the "current" pointer resides in libpthread. The "real" pointer always references libc.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥20 有关区间dp的问题求解
  • ¥15 多电路系统共用电源的串扰问题
  • ¥15 slam rangenet++配置
  • ¥15 有没有研究水声通信方面的帮我改俩matlab代码
  • ¥15 对于相关问题的求解与代码
  • ¥15 ubuntu子系统密码忘记
  • ¥15 信号傅里叶变换在matlab上遇到的小问题请求帮助
  • ¥15 保护模式-系统加载-段寄存器
  • ¥15 电脑桌面设定一个区域禁止鼠标操作
  • ¥15 求NPF226060磁芯的详细资料