Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Go on existing C project

Tags:

c

go

gccgo

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. :)

like image 522
Lopson Avatar asked May 06 '15 09:05

Lopson


People also ask

Can you use C libraries in go?

Go allows linking against C libraries or pasting in in-line code.

Where should I put my go code?

If GOPATH is set, binaries are installed to the bin subdirectory of the first directory in the GOPATH list. Otherwise, binaries are installed to the bin subdirectory of the default GOPATH ( $HOME/go or %USERPROFILE%\go ).

Does go use C?

Go does not require any C libraries if that's what you're asking.

What is import C go?

import "C" provides a special packet that will tell the Go tool to compile with CGO. Comments directly above this import are compiled to C using the GCC. We can simply define a C function like addInC and call it using the C package. C types like int but also structs you define can be accessed via the C package.


1 Answers

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.

like image 147
Ainar-G Avatar answered Oct 30 '22 22:10

Ainar-G