从动态加载的Golang到CPP的未知字符串

huangapple go评论84阅读模式
英文:

Unknown string from Dynamic Loaded Golang to CPP

问题

所以,我尝试在C++项目中使用动态加载运行我的Go代码。它工作得很好,除了返回值中有一些不需要的字符串。如下所述,我从Go中获取了一些不需要的信息。

我的Go代码:

package main

import "C"

func main() {}

//export GetTestString
func GetTestString() string {
	return "test"
}

我使用以下命令构建它:
go build -buildmode=c-shared -o test.so test.go

在我的CPP项目中使用以下函数动态加载它:

typedef struct { const char *p; ptrdiff_t n; } GoString;

void getTestString() {
    void *handle;
    char *error;
    handle = dlopen("./test.so", RTLD_LAZY);
    if (!handle) {
        fputs(dlerror(), stderr);
        exit(1);
    }

    // 解析getTestString符号并分配给函数指针
    auto getTestString = (GoString (*)())dlsym(handle, "GetTestString");
    if ((error = dlerror()) != NULL)  {
        fputs(error, stderr);
        exit(1);
    }

    // 调用GetTestString()
    GoString testString = (*getTestString)();
    printf("%s\n", testString.p);

    // 完成后关闭文件句柄
    dlclose(handle);
}

输出结果为:
>"testtrue ...\n H_T= H_a= H_g= MB, W_a= and cnt= h_a= h_g= h_t= max= ptr siz= tab= top= u_a= u_g=, ..., fp:argp=falsefaultgcingpanicsleepsse41sse42ssse3 (MB)\n addr= base code= ctxt: curg= goid jobs= list= m->p= next= p->m= prev= span= varp=(...)\n, not SCHED efenceerrno objectpopcntscvg: selectsweep (scan (scan) MB in dying= locks= m->g0= nmsys= s=nil\n, goid=, size=, sys: GODEBUGIO waitSignal \ttypes \tvalue=cs fs gctracegs panic: r10 r11 r12 r13 r14 r15 r8 r9 rax rbp rbx rcx rdi rdx rflags rip rsi rsp runningsignal syscallunknownwaiting etypes goalΔ= is not mcount= minutes nalloc= newval= nfree..."

英文:

So, I tried to run my go code on C++ project with dynamic loading. It's working great, except there is some unwanted string on returned value. As I explained down, I got some information from Go that unwanted.

My go code:

package main

import "C"

func main() {}

//export GetTestString
func GetTestString() string {
	return "test"
}

I build it with:
go build -buildmode=c-shared -o test.so test.go

Dynamically load it on my CPP project with this function:

typedef struct { const char *p; ptrdiff_t n; } GoString;

void getTestString() {
    void *handle;
    char *error;
    handle = dlopen ("./test.so", RTLD_LAZY);
    if (!handle) {
        fputs (dlerror(), stderr);
        exit(1);
    }

    // resolve getTestString symbol and assign to fn ptr
    auto getTestString = (GoString (*)())dlsym(handle, "GetTestString");
    if ((error = dlerror()) != NULL)  {
        fputs(error, stderr);
        exit(1);
    }

    // call GetTestString()
    GoString testString = (*getTestString)();
    printf("%s\n", testString.p);

    // close file handle when done
    dlclose(handle);
}

Output is:
>"testtrue ...\n H_T= H_a= H_g= MB, W_a= and cnt= h_a= h_g= h_t= max= ptr siz= tab= top= u_a= u_g=, ..., fp:argp=falsefaultgcingpanicsleepsse41sse42ssse3 (MB)\n addr= base code= ctxt: curg= goid jobs= list= m->p= next= p->m= prev= span= varp=(...)\n, not SCHED efenceerrno objectpopcntscvg: selectsweep (scan (scan) MB in dying= locks= m->g0= nmsys= s=nil\n, goid=, size=, sys: GODEBUGIO waitSignal \ttypes \tvalue=cs fs gctracegs panic: r10 r11 r12 r13 r14 r15 r8 r9 rax rbp rbx rcx rdi rdx rflags rip rsi rsp runningsignal syscallunknownwaiting etypes goalΔ= is not mcount= minutes nalloc= newval= nfree..."

答案1

得分: 1

在通过指针传递字符串到 C 时,你需要使用 GoString 中的长度 (n) 来获取正确数量的字符,因为指向 p 的字符串不是以 \0 结尾的。或者你可以返回 *C.char 而不是 string,并使用 C.CString() 在 C 堆上分配副本(然后在使用后负责释放)。请参阅 Cgo 文档 这里

在你的代码中发生的情况是,printf() 简单地从 string.p 指向的位置开始打印所有字符,直到遇到 \0 终止符为止 - 这就是为什么你看到了 test 之后的内存内容。

所以你可以这样做:

printf("%.*s\n", testString.n, testString.p);

(但请注意,大多数操作预期为以 \0 结尾的 C 字符串的函数在这个指针上将无法工作,除非它们还接受字符串的长度)

或者将 Go 部分更改为以下内容,然后在 C 端使用后释放指针:

func GetTestString() *C.char {
    return C.CString("test") // CString 将在 C 堆上分配字符串
}
英文:

When passing strings via pointer to C you need either use length (n) in GoString to fetch right number of characters as string at p is not \0 terminated. Or you can return *C.char instead of string and use C.CString() to allocate copy on C heap (which you then are responsible for freeing after use). See Cgo documentation here.

What is happening in your code is that printf() simply prints all characters starting from location pointed to by string.p until it hits \0 terminator - that's why you see contents of memory after test.

So you can do either something like:

printf("%.*s\n", testString.n, testString.p);

(but note that most functions that operate on C strings which are expected to be \0 terminated will not work on this pointer unless they also take length of string)

or change Go part to something like this and then free() pointer after use on C side:

func GetTestString() *C.char {
    return C.CString("test") // CString will allocate string on C heap
}

huangapple
  • 本文由 发表于 2021年12月9日 15:11:07
  • 转载请务必保留本文链接:https://go.coder-hub.com/70285938.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定