在现有的C项目中使用Go语言

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

Using Go on existing C project

问题

我有一个完全用C语言编写的程序,其中使用了多个目标(.o)文件。这些文件都打包在一个存档文件(.a)中,而这个存档文件在编译程序的主文件(.c)时被使用。

我想在这个项目中用Go语言编写一个新文件。我的想法是先编写这个.go文件,然后从中创建一个目标(.o)文件。然后,我想把这个目标文件放到前面提到的存档(.a)文件中。

这基本上意味着我想从C程序中调用Go函数。我阅读了这个问题,虽然它告诉我通过GCCGO可以实现我想要的,但具体如何做还不是100%清楚。

即使是最基本的测试,我在链接阶段也会出现错误。具体来说,这是一个基本示例之一:


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;
}

编译

为了编译Go文件,我使用了以下命令:

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

为了编译整个程序,我使用了以下命令:

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

我得到的返回消息是:

/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

这意味着GCCGO期望在Go文件中有一个main函数,而不是C文件中的main函数。

在第二个命令中使用--static-libgo-static-Wl,-R,/path/to/libgo.so's_folder选项会得到不同的结果:

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

这毫无意义,因为我已经将LD_LIBRARY_PATH环境变量正确地指向了libgo.so所在的文件夹。


我意识到我可能在这里做错了什么,但我就是看不出来是什么。几乎没有关于GCCGO及其与C交互的示例,我能找到的唯一参考资料是这个页面,但我个人觉得这还不够。

我恳请您对此事给予一些建议,并感谢您的时间。 在现有的C项目中使用Go语言

英文:

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
(
    &quot;fmt&quot;
)

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

c_caller.c

#define _GNU_SOURCE
#include &lt;stdio.h&gt;

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

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

    int result= PrintString(string_to_pass);
    if(result) {printf(&quot;Everything went as expected!\n&quot;);}
    else       {printf(&quot;Uh oh, something went wrong!\n&quot;);}
    
    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&#39;
/usr/lib64/libgo.so.4.0.0: undefined reference to `__go_init_main&#39;
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&#39;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. 在现有的C项目中使用Go语言

答案1

得分: 8

这可能不是你想要的,但在即将到来的Go 1.5中,你将能够使用go工具构建与C兼容的库。所以在你的_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;
}

在你的main.go文件中,你可以这样写:

package main

import "C"
import "fmt"

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

func main() {}

对于静态库,你可以执行以下命令:

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

对于共享库,你可以执行以下命令:

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

LD_RUN_PATH用于使链接器在构建时在相同目录中查找共享库。)

有关更多信息,请参阅Go执行模式设计文档

英文:

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 &lt;stdio.h&gt;

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

	PrintString(string_to_pass);
	return 0;
}

and this in your main.go

package main

import &quot;C&quot;
import &quot;fmt&quot;

//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

得分: 2

目前没有支持你想要的方法。Go语言始终需要其运行时的支持,而入口点始终是main函数。据我所知,gccgo也有相同的假设,并且没有提供一种容易从其他程序链接到它的方法。

如果你想以受支持的方式实现这一点,你将不得不等到go1.5+,因为目前正在进行从Go代码编译共享库的工作。

如果你真的想立即进行这方面的研究,你可以研究一下使用默认的gc工具链和-linkmode external选项的Android端口是如何工作的,该选项会将main函数在目标文件中重命名并以外部方式调用它。

英文:

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.

huangapple
  • 本文由 发表于 2015年5月6日 17:08:22
  • 转载请务必保留本文链接:https://go.coder-hub.com/30072281.html
匿名

发表评论

匿名网友

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

确定