Using Go on existing C project

2020-03-24 04:01发布

问题:

I have a program entirely written in C that uses multiple object (.o) files in it. These files are all packed inside an archive file (.a) which, in turn, is used at compile-time of the program's main (.c) file.

I want to write a new file for this project in Go. My idea is to write this .go file and then create an object (.o) file from it. Afterwards, I want to put this object file inside the already mentioned archive (.a) file.

This basically means that I want to call Go functions from a C program. I've read this question, and while it showed me that what I want is possible via GCCGO, it's not 100% clear as to how to do it.

Even with the most basic of tests, I get errors during the linking phase. More specifically, here's one of such basic example:


printString.go

package main

import
(
    "fmt"
)

func PrintString(buff string) int {
    fmt.Printf(buff)
    return 1
}

c_caller.c

#define _GNU_SOURCE
#include <stdio.h>

extern int PrintString(char*) __asm__ ("print.main.PrintString");

int main() {
    char *string_to_pass= NULL;
    asprintf(&string_to_pass, "This is a test.");

    int result= PrintString(string_to_pass);
    if(result) {printf("Everything went as expected!\n");}
    else       {printf("Uh oh, something went wrong!\n");}

    return result;
}

Compiling

In order to compile the Go file, I used this command:

gccgo -c printString.go -o printString.o -fgo-prefix=print -Wall -Werror -march=native

In order to compile the entire thing, I used this command:

gccgo -o main c_caller.c printString.o -Wall -Werror -march=native

The return message I'm getting is:

/usr/lib64/libgo.so.4.0.0: undefined reference to `main.main'
/usr/lib64/libgo.so.4.0.0: undefined reference to `__go_init_main'
collect2: error: ld returned 1 exit status

Which means that GCCGO's expecting a main function in the Go file instead of the C one.

Using the --static-libgo, -static and -Wl,-R,/path/to/libgo.so's_folder options on the second command yield a different result:

/usr/bin/ld: cannot find -lgo
collect2: error: ld returned 1 exit status

Which makes no sense, since I have the LD_LIBRARY_PATH environment variable properly pointing to libgo.so's folder.


I realize that I'm probably doing something wrong here, but I just can't see what that is. There's next to no examples of GCCGO and its interaction with C out there, and the only reference I could find was this page, which I personally feel like it's not enough.

I ask kindly for some advice on this matter and thank you for your time. :)

回答1:

This may not be what you want, but in Go 1.5, that's coming this August, you'll be able to build C-compatible libraries with the go tool. So with this in your _main.c

#include <stdio.h>

int main()
{
    char *string_to_pass = NULL;
    if (asprintf(&string_to_pass, "This is a test.") < 0) {
        printf("asprintf fail");
        return -1;
    }

    PrintString(string_to_pass);
    return 0;
}

and this in your main.go

package main

import "C"
import "fmt"

//export PrintString
func PrintString(cs *C.char) {
    s := C.GoString(cs)
    fmt.Println(s)
}

func main() {}

You can do, for static library:

go build -buildmode c-archive -o mygopkg.a
gcc -o main _main.c mygopkg.a -lpthread

For shared library:

go build -buildmode c-shared -o mygopkg.so
LD_RUN_PATH=$(pwd) gcc -o main _main.c mygopkg.so -lpthread

(LD_RUN_PATH is here to make the linker look for the shared library in the same directory you're building.)

See the Go execution modes design document for more info.



回答2:

There currently isn't a supported way to do what you want. Go always needs the support of its runtime, and the entry point for that is always main. AFAIK, gccgo also makes these same assumptions, and doesn't provide a way to easily link into from other programs.

If you want to do this in a supported manner, you will have to wait until go1.5+ where work is being done to compile shared libraries from Go code.

If you really want to hack on this now, you can look into how the Android port works using the default gc toolchain with -linkmode external, which renames main in the object file and calls it externally.



标签: c go gccgo