Using a cgo shared library in a Go program

Trying to test cgo, so I wrote the below:

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

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 <stdlib.h>
// #include "golib.h"
import "C"

func main() {
	C.HelloWorld()
}

// go run main.go

But I’m getting the below error:

# 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]

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

That means to run just main.go (and ignore any other .go or .c files). You should instead write go run . to compile the package with all its files in the current directory.

Still same error.

➜  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

I guess but not sure, cgo can not call functions generated by another cgo.

I was able to do the required easily with plain .c and .h files, plain I means not generated by 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))
}

It run smoothly, and gave below output

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

I think you maybe have to specify your β€œlib” library in the LDFLAGS. Something like this:

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

I believe:

  • The -L option tells the linker where to look for the library when linking,
  • -llib says to include the lib library,
  • -Wl,-rpath,. tells the compiled binary to look for your shared library in the same folder as your go program when it executes.
1 Like

I got invalid flag in #cgo LDFLAGS: -Wl,-rpath=.

The updated code is:

package main

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

func main() {
	C.HelloWorld()
}

Looks like the syntax is different for Go; change

-Wl,-rpath=.

to

-Wl,-rpath,.

Same my friend: invalid flag in #cgo LDFLAGS: -Wl,rpath,.

Sorry, I just updated my last answer. I missed the - before rpath.

samething.

I tried:

package main

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

func main() {
	C.HelloWorld()
}

But got:

➜  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

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 '_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

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

  1. Inline coding, as:
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. Static linking, where you have both .c and .h files, as:
// lib.c
#include  <stdio.h>
int Add(int a, int b){
    printf("Welcome from external C function\n");
    return a + b;
}

And

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

And the go file be:

// main.go
package main

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

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 "libadd.h"
import "C"
import "fmt"

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

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

I uploaded seperate folders for each case [here][1]

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

// main.c
#include <stdio.h>
#include "libadd.h" // calling C file
#include "libgo.h" // calling shared library generated by GO

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

Compiling the file as:
``bash
clang -o main -L. -ladd -lgo main.c // -ladd => -l (library) is libadd



  [1]: https://github.com/hajsf/tutorial/tree/master/ffi

I face the same issue in my git when I was working on my project app developers in Dubai I fixed it with the same procedure.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.