Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cmd/cgo: can't find type of function in shared library #32374

Closed
aacebedo opened this issue May 31, 2019 · 7 comments

Comments

@aacebedo
Copy link

commented May 31, 2019

What version of Go are you using (go version)?

go version go1.12.5 linux/amd64

Does this issue reproduce with the latest release?

Yes

What operating system and processor architecture are you using (go env)?

GOARCH="amd64" GOBIN="" GOCACHE="/root/.cache/go-build" GOEXE="" GOFLAGS="" GOHOSTARCH="amd64" GOHOSTOS="linux" GOOS="linux" GOPATH="/tmp/test" GOPROXY="" GORACE="" GOROOT="/opt/go" GOTMPDIR="" GOTOOLDIR="/opt/go/pkg/tool/linux_amd64" GCCGO="gccgo" CC="gcc" CXX="g++" CGO_ENABLED="1" GOMOD="" CGO_CFLAGS="-g -O2" CGO_CPPFLAGS="" CGO_CXXFLAGS="-g -O2" CGO_FFLAGS="-g -O2" CGO_LDFLAGS="-g -O2" PKG_CONFIG="pkg-config" GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build134582758=/tmp/go-build -gno-record-gcc-switches"

What did you do?

I am trying to create a golang program which will use an external C library. I want to be able to use the "go get" without having to manually run swig.

I have created the following structure:

|-- libfoo
|   |-- foo.c
|   |-- foo.h
|   `-- libfoo.so
`-- src
    |-- example_test.go
    |-- lib.go
    `-- libfoo.swig

In the libfoo folder I have a simple shared library. The files contain: foo.c:

#include "foo.h"

int foo(int c){
    return c+1;
};

foo.h

int foo(int c);

That I've compiled it with:

gcc -o ./libfoo.so -fPIC -shared ./foo.c

Then the golang files: libfoo.swig

%module libfoo

%{
  extern int foo(int a);
%}

extern int foo(int a);

lib.go

package libfoo

/*
#cgo LDFLAGS: -L../libfoo -lfoo
*/

import "C"

And finally example_test.go

package libfoo

import (
    "testing"
    "fmt"
)

func TestFoo(*testing.T) {
    i := Foo(1)
    fmt.Printf("%v\n",i)
}

When I try to build with "go build -x" It seems that the cgo directives located in the "lib.go" file are not taken into account as I do not see the flags appearing in the output. Here is the output

06aa7e308d6:/mnt/data/src# go build -x
WORK=/tmp/go-build551116655
mkdir -p $WORK/b001/
swig -version
cd $WORK
/opt/go/pkg/tool/linux_amd64/compile -o ./b001/_go_.o -trimpath ./b001 -p main -complete -goversion go1.12.5 -D _$WORK -c=4 ./swig_intsize.go
cd /mnt/data/src
swig -go -cgo -intgosize 64 -module libfoo -o $WORK/b001/libfoo_wrap.c -outdir $WORK/b001/ libfoo.swig
CGO_LDFLAGS='"-g" "-O2"' /opt/go/pkg/tool/linux_amd64/cgo -objdir $WORK/b001/ -importpath _/mnt/data/src -- -I $WORK/b001/ -g -O2 ./lib.go $WORK/b001/_libfoo_swig.go
cd $WORK
gcc -fno-caret-diagnostics -c -x c - || true
gcc -Qunused-arguments -c -x c - || true
gcc -fdebug-prefix-map=a=b -c -x c - || true
gcc -gno-record-gcc-switches -c -x c - || true
cd $WORK/b001
TERM='dumb' gcc -I /mnt/data/src -fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=$WORK/b001=/tmp/go-build -gno-record-gcc-switches -I ./ -g -O2 -o ./_x001.o -c _cgo_export.c
TERM='dumb' gcc -I /mnt/data/src -fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=$WORK/b001=/tmp/go-build -gno-record-gcc-switches -I ./ -g -O2 -o ./_x002.o -c lib.cgo2.c
TERM='dumb' gcc -I /mnt/data/src -fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=$WORK/b001=/tmp/go-build -gno-record-gcc-switches -I ./ -g -O2 -o ./_x003.o -c _libfoo_swig.cgo2.c
TERM='dumb' gcc -I /mnt/data/src -fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=$WORK/b001=/tmp/go-build -gno-record-gcc-switches -I ./ -g -O2 -o ./_x004.o -c libfoo_wrap.c
TERM='dumb' gcc -I /mnt/data/src -fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=$WORK/b001=/tmp/go-build -gno-record-gcc-switches -I ./ -g -O2 -o ./_cgo_main.o -c _cgo_main.c
cd /mnt/data/src
TERM='dumb' gcc -I . -fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=$WORK/b001=/tmp/go-build -gno-record-gcc-switches -o $WORK/b001/_cgo_.o $WORK/b001/_cgo_main.o $WORK/b001/_x001.o $WORK/b001/_x002.o $WORK/b001/_x003.o $WORK/b001/_x004.o -g -O2
# _/mnt/data/src
/tmp/go-build551116655/b001/_x004.o: In function `_wrap_foo_libfoo_0f106433f15c8754':
/tmp/go-build/libfoo_wrap.c:255: undefined reference to `foo'
collect2: error: ld returned 1 exit status

What did you see instead?

f I set the CGO_LDFLAGS using an environment variable, it works. However I want to be able to just use "go get" without having those variables before.

I don't understand why go does not take my #cgo directive into account. Did I miss something ?

@ianlancetaylor

This comment has been minimized.

Copy link
Contributor

commented May 31, 2019

The cgo tool needs to figure out the type of foo in order to permit calling it from Go code. It tries to do this by linking a program and examing the debug info. It doesn't know how to get the type from the C header file, and it doesn't know how to get the type from a shared library. So I suspect that this general approach won't work at present. There's no reason in principle that it couldn't work, but someone would have to do the work.

@ianlancetaylor ianlancetaylor changed the title Build issue with SWIG file and external C library cmd/cgo: can't find type of function in shared library May 31, 2019

@ianlancetaylor ianlancetaylor added this to the Unplanned milestone May 31, 2019

@aacebedo

This comment has been minimized.

Copy link
Author

commented Jun 1, 2019

What I don't understand is that if I set env vars for CGO_LDFLAGS with the content of the #cgo directive I have in the lib.go file it works.

In the documentation it is said that "go build" shall use that directive in any of the go files of the project and use it during build. It seems not be the case.

@AlexRouSg

This comment has been minimized.

Copy link
Contributor

commented Jun 1, 2019

You have a newline between import "C" and the comment. Remove it so it looks like

/*
#cgo LDFLAGS: -L../libfoo -lfoo
*/
import "C"
@aacebedo

This comment has been minimized.

Copy link
Author

commented Jun 1, 2019

That's working !
I didn't see anything in the documentation about not letting a newline here.
Is this a normal behavior ?

@AlexRouSg

This comment has been minimized.

Copy link
Contributor

commented Jun 1, 2019

Yes it is

See https://golang.org/cmd/cgo/

If the import of "C" is immediately preceded by a comment, that comment, called the preamble, is used as a header when compiling the C parts of the package.

@aacebedo

This comment has been minimized.

Copy link
Author

commented Jun 1, 2019

Thanks ! I didn't see this word.
I am closing the issue.

@aacebedo aacebedo closed this Jun 1, 2019

@ianlancetaylor

This comment has been minimized.

Copy link
Contributor

commented Jun 3, 2019

@AlexRouSg Thanks for spotting that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
3 participants
You can’t perform that action at this time.