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

53,618

Solution 1

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

Solution 2

You just have to link with -Ldirectory -lgb.

$ cat >toto.c
int x( int y ) { return y+1; }
$ cat >toto.h
int x(int);
$ gcc -O2 -c toto.c
$ ar q libgb.a toto.o
$ cat >test.go
package main

import "fmt"

// #cgo CFLAGS: -I.
// #cgo LDFLAGS: -L. -lgb 
// #include <toto.h>
import "C"

func main() {
  fmt.Printf("Invoking c library...\n")
  fmt.Println("Done ", C.x(10) )
}
$ go build test.go
$ ./test
Invoking c library...
Done  11

Solution 3

A straightforward Makefile to link Go code with a dynamic or static library:

static:
    gcc -c gb.c
    ar -rcs libgb.a gb.o
    go build -ldflags "-linkmode external -extldflags -static" bridge.go

dynamic:
    gcc -shared -o libgb.so gb.c
    go build bridge.go

Directives in bridge.go:

/*
#cgo CFLAGS: -I.
#cgo LDFLAGS: -L. -lgb
#include "gb.h"
*/
import "C"
...
Share:
53,618

Related videos on Youtube

Doug
Author by

Doug

Updated on July 27, 2020

Comments

  • Doug
    Doug almost 4 years

    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.

    • Intermernet
      Intermernet about 11 years
      Are you missing a -L after LDFLAGS: ?
  • Intermernet
    Intermernet about 11 years
    Damn, was hoping it was just a typo, other than that I can't see why it wouldn't be working
  • fuz
    fuz about 11 years
    Wasn't it that you need both -L/Users/doug/projects/c/go-bridge/build/ and -lgb ? (the first belongs to LDFLAGS, the second one you usually put into LDLIBS).
  • SwiftMango
    SwiftMango almost 5 years
    @Victor that is required "metadata" for cgo to find the proper files to link and call
  • George Y
    George Y about 3 years
    Interesting that under -L . it will check /usr/lib/ instead.