Golang程序中使用CGO共享库的方法

Golang程序中使用CGO共享库的方法 尝试测试 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 上的 VS Code 中遇到了以下错误:

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

Screen Shot 2022-01-24 at 5.30.58 PM


更多关于Golang程序中使用CGO共享库的方法的实战教程也可以访问 https://www.itying.com/category-94-b0.html

10 回复

抱歉,我刚刚更新了上一个回答。我漏掉了 rpath 前面的 -

更多关于Golang程序中使用CGO共享库的方法的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


我在处理我的项目时,在 Git 中也遇到了同样的问题,我通过相同的步骤解决了它。

看起来Go的语法有所不同;将

-Wl,-rpath=.

改为

-Wl,-rpath,.

skillian:

-Wl,rpath,.

我的朋友也遇到了同样的问题:invalid flag in #cgo LDFLAGS: -Wl,rpath,.

// go run main.go

这意味着运行 main.go 文件(而忽略任何其他的 .go 或 .c 文件)。你应该使用 go run . 来编译当前目录下包含所有文件的包。

我认为你可能需要在 LDFLAGS 中指定你的 “lib” 库。类似这样:

#cgo LDFLAGS: -L. -llib -Wl,-rpath,.

我的理解是:

  • -L 选项告诉链接器在链接时去哪里查找库,
  • -llib 表示包含 lib 库,
  • -Wl,-rpath,. 告诉编译后的二进制文件,在它执行时,在与你的 Go 程序相同的文件夹中查找你的共享库。

skillian:

#cgo LDFLAGS: -L. -llib -Wl,-rpath=.

我遇到了错误:invalid flag in #cgo LDFLAGS: -Wl,-rpath=.

更新后的代码如下:

package main

// #cgo LDFLAGS: -L. -llib -Wl,-rpath=.
// #cgo CFLAGS: -g -Wall
// #include <stdlib.h>
// #include "golib.h"
import "C"

func main() {
	C.HelloWorld()
}

同样的问题。

我尝试了:

package main

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

func main() {
	C.HelloWorld()
}

但得到了:

➜  gocallclib git:(master) ✗ go run main-dl.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 run .

仍然是同样的错误。

➜  ffi git:(master) ✗ go run .
# github.io/hajsf/ffi
Undefined symbols for architecture x86_64:
  "_HelloWorld", referenced from:
      __cgo_da04f5d85884_Cfunc_HelloWorld in _x002.o
     (maybe you meant: __cgo_da04f5d85884_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)
# github.io/hajsf/ffi
cgo-gcc-prolog:47:33: warning: unused variable '_cgo_a' [-Wunused-variable]
➜  ffi git:(master) ✗ clang --version
Apple clang version 12.0.5 (clang-1205.0.22.9)
Target: x86_64-apple-darwin21.2.0
Thread model: posix
InstalledDir: /Library/Developer/CommandLineTools/usr/bin

我猜测(但不完全确定),cgo 无法调用由另一个 cgo 生成的函数。

我能够轻松地使用普通的 .c.h 文件(这里的“普通”指的是不由 cgo 生成)完成所需的功能。

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

// #include "main.h"
// #include  <stdio.h>
// int Add2(int x)
// {
//	    printf("Welcome from inline C function\n");
//	    return x + 2;
// }
import "C"
import "fmt"

func main() {
	fmt.Println(C.Add(1, 2))
	fmt.Println(C.Add2(5))
}

它运行顺利,并给出了以下输出:

➜  ffi git:(master) ✗ go run .
Welcome from external C function
3
Welcome from inline C function
7

在CGO中,当使用-buildmode=c-shared生成共享库时,需要确保头文件正确生成并被引用。你的问题在于生成的golib.h头文件中函数声明缺少extern前缀。

首先检查生成的golib.h文件内容:

$ cat golib.h

应该类似这样:

/* Code generated by cmd/cgo; DO NOT EDIT. */

#line 1 "cgo-builtin-export-prolog"

#include <stddef.h> /* for ptrdiff_t below */

#ifndef GO_CGO_EXPORT_PROLOGUE_H
#define GO_CGO_EXPORT_PROLOGUE_H

#ifndef GO_CGO_GOSTRING_TYPEDEF
typedef struct { const char *p; ptrdiff_t n; } _GoString_;
#endif

#endif

/* Start of preamble from import "C" comments.  */




/* End of preamble from import "C" comments.  */


#ifdef __cplusplus
extern "C" {
#endif

extern void HelloWorld();

#ifdef __cplusplus
}
#endif

注意extern void HelloWorld();这一行。如果你的头文件缺少extern,需要手动修复。

正确的使用示例:

lib.go:

//go:build lib
// +build lib

package main

import "C"
import "fmt"

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

func main() {}

编译共享库:

go build -tags lib -buildmode=c-shared -o libgolib.dylib lib.go

main.go:

//go:build app
// +build app

package main

// #cgo CFLAGS: -g -Wall
// #cgo LDFLAGS: -L. -lgolib
// #include "libgolib.h"
import "C"

func main() {
	C.HelloWorld()
}

编译并运行:

go build -tags app -o app main.go
./app

对于Mac系统,共享库扩展名应为.dylib而不是.a.a是静态库,.dylib是动态库。修改编译命令:

go build -tags lib -buildmode=c-shared -o libgolib.dylib lib.go

然后更新main.go中的链接标志:

// #cgo LDFLAGS: -L. -lgolib
// #include "libgolib.h"

如果问题仍然存在,尝试清理并重新构建:

rm -f *.h *.dylib
go build -tags lib -buildmode=c-shared -o libgolib.dylib lib.go
go build -tags app -o app main.go

对于VS Code的错误,可以尝试在终端中直接构建来验证CGO处理是否正常。

回到顶部