簡體   English   中英

為什么dlsym在cgo中產生的結果不同於c?

[英]Why does dlsym produce different results in cgo than in c?

我有兩個相同行為的實現,我認為應該產生相同的結果,但相反產生不同的結果。 當使用cgo在Go中編譯時,我獲得了與在C中編譯時不同的符號地址解析。我想了解原因。

我將問題簡化為幾個小例子,一個在C中,一個在Go中。 我在Mac筆記本電腦上運行的Ubuntu 18 Docker容器中測試了這些。

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的輸出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的情況下,“當前”指針位於libpthread中。 “真實”指針總是引用libc。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM