Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you statically link a c library in go using cgo?

Tags:

go

cgo

So there's a bunch of stuff on the group that suggests you can do this in go (although not on the cgo documentation):

package bridge  import "fmt"  // #cgo CFLAGS: -I/Users/doug/projects/c/go-bridge/include // #cgo LDFLAGS: /Users/doug/projects/c/go-bridge/build/libgb.a // #include <junk.h> import "C"  func Run() {   fmt.Printf("Invoking c library...\n")   C.x(10)   fmt.Printf("Done\n") } 

However, it doesn't seem to work:

/var/folders/.../bridge.a(bridge.cgo2.o)(__TEXT/__text): x: not defined 

This seems to work fine using a dynamic library, and inspecting the generated files, it actually has the symbol 'x' in there:

/var/folders/rg/hj4s3qlj3sz1d1b5p50ws0vc0000gn/T/go-build442792776/bridge/_obj/_cgo_.o: 0000000100001048 S _NXArgc  0000000100001050 S _NXArgv  0000000100001060 S ___progname  0000000100000dc0 T __cgo_2d7eefe3d6d4_Cfunc_x 0000000100000da0 T __cgo_allocate  0000000100000db0 T __cgo_panic 0000000100000000 T __mh_execute_header  0000000100000d90 T _crosscall2 0000000100001058 S _environ                  U _exit  0000000100000d80 T _main                  U _puts  0000000100001000 s _pvars  0000000100000de0 T _x                <------- Exists                  U dyld_stub_binder  0000000100000d40 T start 

but obviously is just a marker in bridge.cgo2.o:

/var/folders/rg/hj4s3qlj3sz1d1b5p50ws0vc0000gn/T/go-build442792776/bridge.a(bridge.cgo2.o): 0000000000000368 s EH_frame0 0000000000000000 T __cgo_2d7eefe3d6d4_Cfunc_x 0000000000000380 S __cgo_2d7eefe3d6d4_Cfunc_x.eh                  U _x 

What am I doing wrong?

For ref, the c header:

int x(int y); 

And code:

#include <junk.h> #include <stdio.h>  int x(int y) {   printf("Hello World\n");   return y; } 

--

Edit:

No, -L and -l don't work either; there's actually some specific discussion on the google group that this (-l/blah/blah.a) does not work for cgo, and the correct syntax is in fact to omit the -l and just list the .a file... but hey, if it'd worked, I'd totally just use it. But it doesn't:

dougs-mini:go doug$ go run test.go # bridge ld: library not found for -l/Users/doug/projects/c/go-bridge/build/libgb.a collect2: ld returned 1 exit status dougs-mini:go doug$ ls -l /Users/doug/projects/c/go-bridge/build/libgb.a -rw-r--r--  1 doug  staff  872 25 May 14:02 /Users/doug/projects/c/go-bridge/build/libgb.a 

verbose version:

dougs-mini:go doug$ go build -work -x test.go WORK=/var/folders/rg/hj4s3qlj3sz1d1b5p50ws0vc0000gn/T/go-build354497708 mkdir -p $WORK/bridge/_obj/ cd /Users/doug/projects/c/go-bridge/go/src/bridge /Users/doug/projects/go/go/pkg/tool/darwin_amd64/cgo -objdir $WORK/bridge/_obj/ -- -I/Users/doug/projects/c/go-bridge/include -I $WORK/bridge/_obj/ bridge.go /Users/doug/projects/go/go/pkg/tool/darwin_amd64/6c -FVw -I $WORK/bridge/_obj/ -I /Users/doug/projects/go/go/pkg/darwin_amd64 -o $WORK/bridge/_obj/_cgo_defun.6 -DGOOS_darwin -DGOARCH_amd64 $WORK/bridge/_obj/_cgo_defun.c gcc -I . -g -O2 -fPIC -m64 -pthread -fno-common -I/Users/doug/projects/c/go-bridge/include -I $WORK/bridge/_obj/ -o $WORK/bridge/_obj/_cgo_main.o -c $WORK/bridge/_obj/_cgo_main.c gcc -I . -g -O2 -fPIC -m64 -pthread -fno-common -I/Users/doug/projects/c/go-bridge/include -I $WORK/bridge/_obj/ -o $WORK/bridge/_obj/_cgo_export.o -c $WORK/bridge/_obj/_cgo_export.c gcc -I . -g -O2 -fPIC -m64 -pthread -fno-common -I/Users/doug/projects/c/go-bridge/include -I $WORK/bridge/_obj/ -o $WORK/bridge/_obj/bridge.cgo2.o -c $WORK/bridge/_obj/bridge.cgo2.c gcc -I . -g -O2 -fPIC -m64 -pthread -fno-common -o $WORK/bridge/_obj/_cgo_.o $WORK/bridge/_obj/_cgo_main.o $WORK/bridge/_obj/_cgo_export.o $WORK/bridge/_obj/bridge.cgo2.o -l/Users/doug/projects/c/go-bridge/build/libgb.a # bridge ld: library not found for -l/Users/doug/projects/c/go-bridge/build/libgb.a collect2: ld returned 1 exit status 

It's worth noting that the failure when you try to link like this (using -l) is typical of gcc failing to link because you're attempting to combine a set of object files.

ie. This:

gcc -I . -g -O2 -fPIC -m64 -pthread -fno-common -o ... -l/path/libgb.a 

Will never compile under gcc; you must link a static library like this:

gcc -I . -g -O2 -fPIC -m64 -pthread -fno-common -o ... /path/libgb.a 

ie. It is absolutely not that I'm missing a -l or -L.

like image 547
Doug Avatar asked May 25 '13 06:05

Doug


People also ask

Can you use C libraries in go?

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

Are Go programs statically linked?

Well, Go code is statically linked, but the runtime may try to dynamically load libc for DNS resolving. Use of cgo of course drastically change everything. By default, nowadays the toolchain also supports generating dynamic libraries.

Can you statically link glibc?

Most of the sources online state that you can statically link glibc, but discourage from doing so; e.g. centos package repo: The glibc-static package contains the C library static libraries for -static linking. You don't need these, unless you link statically, which is highly discouraged.

Can you statically link a shared object?

You can't statically link a shared library (or dynamically link a static one).


1 Answers

Turns out my code is 100% fine; it was a copy of Go 1.0; under go 1.1 this works. Under go 1.0, it doesn't.

(it's a bit lame answering my own question, I know; but the 'use -L -l answers below aren't right either; it had nothing to do with that).

A working solution example is up on github here for anyone who find's this question later:

https://github.com/shadowmint/go-static-linking

in short that looks like:

CGO_ENABLED=0 go build -a -installsuffix cgo -ldflags '-s' src/myapp/myapp.go 

see also: https://github.com/golang/go/issues/9344

like image 171
Doug Avatar answered Oct 02 '22 13:10

Doug