Как вы статически слинкуете библиотеку c в go с помощью cgo?

Итак, в группе есть куча вещей, которые предполагают, что вы можете сделать это в go (хотя и не в документации cgo):

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")
}

Однако, похоже, это не работает:

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

Кажется, это отлично работает с использованием динамической библиотеки, и при проверке сгенерированных файлов там действительно есть символ «x»:

/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

но, очевидно, это просто маркер в 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

Что я делаю неправильно?

Для ссылки, заголовок c:

int x(int y);

И код:

#include <junk.h>
#include <stdio.h>

int x(int y) {
  printf("Hello World\n");
  return y;
}

--

Редактировать:

Нет, -L и -l тоже не работают; на самом деле в группе Google есть специальное обсуждение того, что это (-l/blah/blah.a) не работает для cgo, и правильный синтаксис состоит на самом деле в том, чтобы опустить -l и просто перечислить .a файл... но эй, если бы это сработало, я бы просто использовал его. Но это не так:

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

развернутая версия:

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

Стоит отметить, что сбой при попытке связать таким образом (используя -l) типичен для gcc, который не может связать, потому что вы пытаетесь объединить набор объектных файлов.

т.е. Этот:

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

Никогда не будет компилироваться под gcc; вы должны связать статическую библиотеку следующим образом:

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

т.е. Дело вовсе не в том, что мне не хватает -l или -L.


person Doug    schedule 25.05.2013    source источник
comment
Вам не хватает -L после LDFLAGS: ?   -  person Intermernet    schedule 25.05.2013


Ответы (4)


Оказывается, мой код на 100% в порядке; это была копия Go 1.0; под go 1.1 это работает. Под go 1.0 это не так.

(я знаю, что это немного неубедительно, отвечая на мой собственный вопрос, но приведенные ниже ответы «использовать -L -l» тоже неверны; это не имеет к этому никакого отношения).

Пример рабочего решения находится на github здесь для тех, кто найдет этот вопрос позже:

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

короче это выглядит так:

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

см. также: https://github.com/golang/go/issues/9344

person Doug    schedule 25.05.2013

Вам просто нужно связать с -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
person Didier Spezia    schedule 25.05.2013
comment
@Victor, который требует метаданных для cgo, чтобы найти правильные файлы для ссылки и вызова - person texasbruce; 22.07.2019

Простой Makefile для связывания кода Go с динамической или статической библиотекой:

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

Директивы в bridge.go:

/*
#cgo CFLAGS: -I.
#cgo LDFLAGS: -L. -lgb
#include "gb.h"
*/
import "C"
...
person algorix    schedule 08.04.2015
comment
Интересно, что под -L . вместо него будет проверяться /usr/lib/. - person George Y; 10.04.2021

Пытаться:

// #cgo LDFLAGS: -l/Users/doug/projects/c/go-bridge/build/libgb.a

Вы пропустили -l в директиве LDFLAGS.

person Intermernet    schedule 25.05.2013
comment
Черт, я надеялся, что это просто опечатка, иначе я не понимаю, почему это не сработает. - person Intermernet; 25.05.2013
comment
Разве вам не нужны оба -L/Users/doug/projects/c/go-bridge/build/ и -lgb ? (первый принадлежит LDFLAGS, второй обычно помещается в LDLIBS). - person fuz; 25.05.2013