4

I'm running a go program in a docker container (golang:1.18-bullseye).

I haev tried running it both with go run main.go and go run .

My code looks likes this, both header files are located in the Include directory given in the CFLAGS:

/*
#cgo LDFLAGS: -Lvendor/MyCoolLibrary/1.14.2/Bin/Linux/libSDK-Linux-Shipping.so
#cgo CFLAGS: -I vendor/MyCoolLibrary/1.14.2/Include/
#include "my_cool_sdk.h"
#include "my_cool_logging.h"*/
import "C"
import (
    "fmt"
    "log"
    "os"
    "runtime"
)

func main() {
ret := C.MyCoolFunc()
}

When I run this code, I get this error message:

/usr/bin/ld: $WORK/b001/_x002.o: in function `_cgo_6bb9bcf96ac6_Cfunc_MyCoolFunc':
/tmp/go-build/cgo-gcc-prolog:110: undefined reference to `MyCoolFunc'

How can I fix this?

Edit: I changed the header to:

/*
#cgo LDFLAGS: -L${SRCDIR}/vendor/MyCoolLibrary/1.14.2/Bin/Linux/libSDK-Linux-Shipping.so -lSDK-Linux-Shipping
#cgo CFLAGS: -I ${SRCDIR}/vendor/MyCoolLibrary/1.14.2/Include/
#include "my_cool_sdk.h"
#include "my_cool_logging.h"*/

and ran go build -x . and this is the output:

WORK=/tmp/go-build1175764972
mkdir -p $WORK/b001/
cd /home/helloworld
TERM='dumb' CGO_LDFLAGS='"-g" "-O2" "-L/home/helloworld/vendor/MyCoolLibrary/1.14.2/Bin/Linux/libSDK-Linux-Shipping.so" "-lSDK-Linux-Shipping"' /usr/local/go/pkg/tool/linux_amd64/cgo -objdir $WORK/b001/ -importpath go-sandbox -- -I $WORK/b001/ -g -O2 -I ./vendor/MyCoolLibrary/1.14.2/Include/ ./main.go
cd $WORK
gcc -fno-caret-diagnostics -c -x c - -o /dev/null || true
gcc -Qunused-arguments -c -x c - -o /dev/null || true
gcc -fdebug-prefix-map=a=b -c -x c - -o /dev/null || true
gcc -gno-record-gcc-switches -c -x c - -o /dev/null || true
cd $WORK/b001
TERM='dumb' gcc -I /home/helloworld -fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=$WORK/b001=/tmp/go-build -gno-record-gcc-switches -I ./ -g -O2 -I /home/helloworld/vendor/MyCoolLibrary/1.14.2/Include/ -o ./_x001.o -c _cgo_export.c
TERM='dumb' gcc -I /home/helloworld -fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=$WORK/b001=/tmp/go-build -gno-record-gcc-switches -I ./ -g -O2 -I /home/helloworld/vendor/MyCoolLibrary/1.14.2/Include/ -o ./_x002.o -c main.cgo2.c
TERM='dumb' gcc -I /home/helloworld -fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=$WORK/b001=/tmp/go-build -gno-record-gcc-switches -I ./ -g -O2 -I /home/helloworld/vendor/MyCoolLibrary/1.14.2/Include/ -o ./_cgo_main.o -c _cgo_main.c
cd /home/helloworld
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 -g -O2 -L/home/helloworld/vendor/MyCoolLibrary/1.14.2/Bin/Linux/libSDK-Linux-Shipping.so -lSDK-Linux-Shipping
# go-sandbox
/usr/bin/ld: cannot find -lSDK-Linux-Shipping
collect2: error: ld returned 1 exit status
Jessica Chambers
  • 1,246
  • 5
  • 28
  • 56
  • 1
    How exactly do you "run this code"? I mean, do you use `go run`? – kostix Apr 05 '22 at 10:33
  • `go run main.go` or `go run .` – Jessica Chambers Apr 05 '22 at 10:43
  • Does `go build` runs OK in the same directory? Also: the `-L` flag in your `LDFLAGS` definition is passed a relative pathname to a file while it's supposed to (augment) the list of search paths to find libraries passed to the linker via the `-l` (small letter el) command-line option—please refer to the `ld(1)` manual page. Hence I think the reason is simple: the linker fails to link in your library and hence a symbol it is supposed to export is not found. – kostix Apr 05 '22 at 13:26
  • 1
    Also: please never ever use `go run` at least you are 100% confident it is needed; use `go build`/`go install`, then run the resulting executable image file. The exact reasoning is a bit too long to expain; for short, Go is not a scripting language—at least in its stock implementation which you're apparently using. – kostix Apr 05 '22 at 13:27
  • Running `go build` with the `-x` command-line option produces copious output which contains calls to all the tools involved in creating the resulting executable, and their output. Consider using it to see what might have gone wrong with the build process. Also you could use a tool such as `nm` or `objdump` to verify that your `.so` library file indeed exports that `MyCoolFunc` symbol. (Well, for `.so` files you supposedly should use `lld`.) – kostix Apr 05 '22 at 13:30
  • @kostix I have used `lld` just to be sure, and can confirm the functions are there as expected. Notes taken on `go run` though, I had no idea. – Jessica Chambers Apr 05 '22 at 14:14

1 Answers1

3

I was able to reproduce and fix this. There are also some additional gotchas. Start by just focusing on running go build:

Ok so the go compiler has found the header file, but cannot find the shared library. I think you modified your code for the question slightly and this is not an issue, but the path for -L in LDFLAGS has to be either:

  • relative to the source directory using ${SRCDIR}
  • an absolute path
  • avoid this entirely and leverage pkg-config I just used the relative directory containing the so file as my argument for -L.

Ok, that aside, you must also give a -l argument in LDFLAGS to find the file in the paths you pointed to (IE: libvendera.so needs -lvendora).

Once go build works you have an application that still needs the know where the so file is to run (so hence a shared library). To do this you will likely need to set LD_LIBRARY_PATH and point to the directory containing the so file much like you did with -L.

Liam Kelly
  • 3,524
  • 1
  • 17
  • 41
  • Ok! (Disclosure: I did not edit the file header in any way. main.go is on the same level as vendor) - So essentially I need to point it at the `.so` library 3 times? Once with `-L`, once with `LD_LIBRARY_PATH` and once with `-lmylibrary`? – Jessica Chambers Apr 05 '22 at 14:12
  • 2
    Then you should use the `${SRCDIR}` special variable: `// #cgo LDFLAGS: -L${SRCDIR}/libs -lfoo` to reference ./libs/libfoo.so. [Reference](https://pkg.go.dev/cmd/cgo#:~:text=//%20%23cgo%20LDFLAGS%3A%20%2DL%24%7BSRCDIR%7D/libs%20%2Dlfoo) – Liam Kelly Apr 05 '22 at 14:26
  • Cool. when it comes to the `-l` argument, how do you know the name to use? I tried `-llibSDK-Linux-Shipping` and `-lSDK-Linux-Shipping` and it claims it can't find either – Jessica Chambers Apr 05 '22 at 14:39
  • 1
    Usually you omit the `lib` prefix. Please try building with the `-x` command-line option and see what `gcc` outputs. – kostix Apr 05 '22 at 14:41
  • @kostix I have added it to the Question ^ – Jessica Chambers Apr 05 '22 at 14:47
  • @JessicaChambers, you consistently miss what we're saying about what kind of pathnames the `-L` command-line option to the linker accepts ;-) It adds the indicated _directory_ to the list to look up the libs listed via the `-l` command line-option. – kostix Apr 05 '22 at 14:58
  • 2
    try `#cgo LDFLAGS: -L${SRCDIR}/vendor/MyCoolLibrary/1.14.2/Bin/Linux -lSDK-Linux-Shipping` – Liam Kelly Apr 05 '22 at 15:00
  • Oh whoops, sorry for being dense That seems to have done the trick, thanks! – Jessica Chambers Apr 05 '22 at 15:02