在Go程序中使用cgo共享库

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

Using a cgo shared library in a Go program

问题

尝试测试cgo,所以我写了下面的代码:

//go:build lib
// +build lib

package main

import "C"
import "fmt"

//export HelloWorld
func HelloWorld() {
	fmt.Printf("hello world")
}

func main() {}

// go build -tags lib -buildmode=c-shared -o golib.a lib.go

并编译为:

$ go build -tags lib -buildmode=c-shared -o golib.a lib.go

尝试在另一个代码中使用生成的共享库:

//go:build app
// +build app

package main

// #cgo CFLAGS: -g -Wall
// #include <stdlib.h>
// #include "golib.h"
import "C"

func main() {
	C.HelloWorld()
}

// go run main.go

但是我得到以下错误:

# command-line-arguments
Undefined symbols for architecture x86_64:
  "_HelloWorld", referenced from:
      __cgo_a844f0d618a1_Cfunc_HelloWorld in _x002.o
     (maybe you meant: __cgo_a844f0d618a1_Cfunc_HelloWorld)
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
# command-line-arguments
cgo-gcc-prolog:47:33: warning: unused variable '_cgo_a' [-Wunused-variable]

此外,在Mac上,我遇到了以下错误:

go list failed to return CompiledGoFiles. This may indicate failure to perform cgo processing; try building at the command line. See https://golang.org/issue/38990

在Go程序中使用cgo共享库

英文:

Trying to test cgo, so I wrote the below:

//go:build lib
// +build lib

package main

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

//export HelloWorld
func HelloWorld() {
	fmt.Printf(&quot;hello world&quot;)
}

func main() {}

// go build -tags lib -buildmode=c-shared -o golib.a lib.go

And compiled it as:

$ go build -tags lib -buildmode=c-shared -o golib.a lib.go

Trying to use the generated shared lib in another code as:

//go:build app
// +build app

package main

// #cgo CFLAGS: -g -Wall
// #include &lt;stdlib.h&gt;
// #include &quot;golib.h&quot;
import &quot;C&quot;

func main() {
	C.HelloWorld()
}

// go run main.go

But I'm getting the below error:

# command-line-arguments
Undefined symbols for architecture x86_64:
  &quot;_HelloWorld&quot;, referenced from:
      __cgo_a844f0d618a1_Cfunc_HelloWorld in _x002.o
     (maybe you meant: __cgo_a844f0d618a1_Cfunc_HelloWorld)
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
# command-line-arguments
cgo-gcc-prolog:47:33: warning: unused variable &#39;_cgo_a&#39; [-Wunused-variable]

Moreover, I'm getting the below error with VS code at mac:

go list failed to return CompiledGoFiles. This may indicate failure to perform cgo processing; try building at the command line. See https://golang.org/issue/38990

在Go程序中使用cgo共享库

答案1

得分: 0

你不能在Go程序中使用cgo共享库,因为你不能在同一个进程中有多个Go运行时。

尝试这样做会导致错误:

# command-line-arguments
cgo-gcc-prolog:67:33: warning: unused variable '_cgo_a' [-Wunused-variable]
fatal error: unexpected signal during runtime execution
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x43fd5e2]

goroutine 1 [running, locked to thread]:
runtime.throw({0x40a875b?, 0x1c00011b800?})
        /usr/local/go/src/runtime/panic.go:992 +0x71 fp=0x1c00004a960 sp=0x1c00004a930 pc=0x402f6d1
runtime: unexpected return pc for runtime.sigpanic called from 0x43fd5e2
stack: frame={sp:0x1c00004a960, fp:0x1c00004a9b0} stack=[0x1c00004a000,0x1c00004b000)
....
0x000001c00004aaa0:  0x0000000000000000  0x0000000000000000 
runtime.sigpanic()
        /usr/local/go/src/runtime/signal_unix.go:781 +0x3a9 fp=0x1c00004a9b0 sp=0x1c00004a960 pc=0x4043449
exit status 2

要从Go调用C代码,有三种潜在的方法:

  1. 内联编码,如下所示:
package main

//#include  <stdio.h>
//int Add(int a, int b){
//    printf("Welcome from external C function\n");
//    return a + b;
//}
import "C"
import "fmt"

func main() {
	fmt.Println(C.Add(5, 2))
}
  1. 静态链接,其中你有.c.h文件,如下所示:
// lib.c
#include  <stdio.h>
int Add(int a, int b){
    printf("Welcome from external C function\n");
    return a + b;
}

// libadd.h
int Add(int, int);

然后go文件为:

// main.go
package main

// #include "libadd.h"
import "C"
import "fmt"

func main() {
	x := C.Add(1, 2)
	fmt.Println(x)

}

我们必须将文件运行为go run .go run github.io/xxx
// go run main.go将不起作用,因为它只会考虑main.go,而不会考虑C文件

  1. 动态链接,你将上述c文件编译为clang -shared -fpic -Wall -g lib.c -o libadd.so,并将go文件设置为:
// main.go
package main

//#cgo CFLAGS: -g -Wall
//#cgo LDFLAGS: -L. -ladd
//#include "libadd.h"
import "C"
import "fmt"

func main() {
	x := C.Add(1, 2)
	fmt.Println(x)
}

在这里,你可以使用go run main.go,因为库是通过硬编码连接的,要分发二进制文件,需要将共享库loadadd.so与相同的二进制文件一起分发,并存在于相同的文件夹中。

我在每种情况下都上传了单独的文件夹这里

额外内容
要使用C程序调用生成的共享库中的go,请使用以下代码:

// main.c
#include <stdio.h>
#include "libadd.h" // 调用C文件
#include "libgo.h" // 调用由GO生成的共享库

int main()
{
    HelloWorld();
    int x = Add(1, 2);
    printf("%d",x);
    return 0;
}

将文件编译为:

clang -o main -L. -ladd -lgo main.c   // -ladd => -l (library) is libadd
英文:

You cannot use a cgo shared library in a Go program, because you cannot have multiple Go runtimes in the same process.

Trying to do so will give the error:

# command-line-arguments
cgo-gcc-prolog:67:33: warning: unused variable &#39;_cgo_a&#39; [-Wunused-variable]
fatal error: unexpected signal during runtime execution
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x43fd5e2]

goroutine 1 [running, locked to thread]:
runtime.throw({0x40a875b?, 0x1c00011b800?})
        /usr/local/go/src/runtime/panic.go:992 +0x71 fp=0x1c00004a960 sp=0x1c00004a930 pc=0x402f6d1
runtime: unexpected return pc for runtime.sigpanic called from 0x43fd5e2
stack: frame={sp:0x1c00004a960, fp:0x1c00004a9b0} stack=[0x1c00004a000,0x1c00004b000)
....
0x000001c00004aaa0:  0x0000000000000000  0x0000000000000000 
runtime.sigpanic()
        /usr/local/go/src/runtime/signal_unix.go:781 +0x3a9 fp=0x1c00004a9b0 sp=0x1c00004a960 pc=0x4043449
exit status 2

To call c code from go, there are 3 potential approaches:

  1. Inline coding, as:
package main

//#include  &lt;stdio.h&gt;
//int Add(int a, int b){
//    printf(&quot;Welcome from external C function\n&quot;);
//    return a + b;
//}
import &quot;C&quot;
import &quot;fmt&quot;

func main() {
	fmt.Println(C.Add(5, 2))
}
  1. Static linking, where you have both .c and .h files, as:
// lib.c
#include  &lt;stdio.h&gt;
int Add(int a, int b){
    printf(&quot;Welcome from external C function\n&quot;);
    return a + b;
}

And

// libadd.h
int Add(int, int);

And the go file be:

// main.go
package main

// #include &quot;libadd.h&quot;
import &quot;C&quot;
import &quot;fmt&quot;

func main() {
	x := C.Add(1, 2)
	fmt.Println(x)

}

We have to run the file as go run . or go run github.io/xxx
// go run main.go will not work as it will consider main.go only, and not consider the C file

  1. Dynamic linking, where you compile the above c file as clang -shared -fpic -Wall -g lib.c -o libadd.so and have the go file as:
// main.go
package main

//#cgo CFLAGS: -g -Wall
//#cgo LDFLAGS: -L. -ladd
//#include &quot;libadd.h&quot;
import &quot;C&quot;
import &quot;fmt&quot;

func main() {
	x := C.Add(1, 2)
	fmt.Println(x)
}

Here you can use go run main.go as the library is connected through the hard code, to distribute the binary, the shared library loadadd.so is required to be distributed with the same binary file and existing in the same folder.

I uploaded separate folders for each case here.

BONUS
To call the generated share library in go using a c program, we can use the below:

// main.c
#include &lt;stdio.h&gt;
#include &quot;libadd.h&quot; // calling C file
#include &quot;libgo.h&quot; // calling shared library generated by GO

int main()
{
    HelloWorld();
    int x = Add(1, 2);
    printf(&quot;%d&quot;,x);
    return 0;
}

Compiling the file as:

clang -o main -L. -ladd -lgo main.c   // -ladd =&gt; -l (library) is libadd

huangapple
  • 本文由 发表于 2022年1月24日 22:35:33
  • 转载请务必保留本文链接:https://go.coder-hub.com/70835519.html
匿名

发表评论

匿名网友

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

确定