Home > Software design >  Unknown string from Dynamic Loaded Golang to CPP
Unknown string from Dynamic Loaded Golang to CPP

Time:12-09

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..."

CodePudding user response:

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
}
  • Related