Cgo "undefined reference" (gstreamer) - go

I am writing something using cgo to interact with the gstreamer-1.0 library. I have everything almost working perfectly, but for some reason an entire header file's objects are not getting imported correctly.
go version go1.15.2 linux/amd64 for whatever that is worth
package main
// #cgo pkg-config: gstreamer-1.0
// #cgo CFLAGS: -Wno-deprecated-declarations
// #include <gst/gst.h> // using this file extensively with no issues
// #include <gst/app/gstappsink.h> // objects in this file are not getting read, but the compiler is having no issue reading it
import "C"
func init () { C.gst_init(nil, nil) }
func main () {
// ...
C.gst_app_sink_pull_sample() // a non-variadic function that does take args
// but cgo doesn't even think it exists.
// ...
}
The error back from the compiler: /tmp/go-build/cgo-gcc-prolog:64: undefined reference to 'gst_app_sink_pull_sample'
I've looked at the header file and gst_app_sink_pull_sample is indeed there. I can reproduce this both trying to build locally and in the golang docker container.
If I remove the include entirely the error is different: could not determine kind of name for C.gst_app_sink_pull_sample.
So am I the problem or is gstreamer the problem?

The appsrc and appsink symbols are not part of the base gstreamer library. Instead they are found in the extra library gstreamer-app-1.0. Add this library to your cgo pkgconfig line and it should find the missing symbols.

"undefined reference to xxx" means the C compiler of cgo recognize the definitions but it can't find the implementations (covered by corresponding C libraries)
This indicates that you have your C header files imported correctly. To solve the undefined reference problem, you just have to add some thing as below if your dynamic library is called libgstreamer.so.1.0.0
# cgo LDFLAGS: -lgstreamer

Related

How to use this portaudio Go binding?

I'm trying to dynamically play audio in a Go application. Basically the only way I found was to use a Go binding to call a C library, more specifically portaudio.
I found this binding but I don't know how to use it. I downloaded these pre-compiled DLLs and replaced the lines
/*
#cgo pkg-config: portaudio-2.0
#include <portaudio.h>
extern PaStreamCallback* paStreamCallback;
*/
with
/*
#cgo LDFLAGS: -LC:\portaudio-binaries -lportaudio
#include "portaudio.h"
extern PaStreamCallback* paStreamCallback;
*/
but when I try to compile the Go project I still get
cc1.exe: sorry, unimplemented: 64-bit mode not compiled in
I also got this error when trying to import versions of portaudio I compiled using mingw. Its output had the format of a .la file alongside a .libs folder, which contained a .dll file plus others. But I'm not sure how I'm supposed to import this format of libraries.

Include libsodium using cgo in go project fails

I am trying to include the libsodium into my Go project. For that, I've copied the repo inside my project
// #cgo CFLAGS: -I/mypath/libsodium/src/libsodium/include/sodium
// #include <stdlib.h>
// #include "crypto_sign_ed25519.h"
import "C"
When trying to build the project I get the following error:
/tmp/go-build/cgo-gcc-prolog:53: undefined reference to `crypto_sign_ed25519_pk_to_curve25519'
collect2: error: ld returned 1 exit status
The file can be found but the error is there.
I've also tried to reference the '.c' file as well as to copy the crypto_sign_ed25519.h into the src folder but it does not work.
My question is do I have to add LDFLAGS and therefore generate a .so file from the library or that is not needed and there is another possible way of doing it?
UPDATE: I've achieved to make it running by installing the library on my local ubuntu:
$ ./configure
$ make && make check
$ sudo make install
and adding
// #cgo LDFLAGS: -L/usr/local/lib -lsodium
But how can I do it without adding the local path?
You indeed need to link the library, the headers themselves are only the interface to the library and don't link the actual libsodium code to your binary.
Assuming libsodium ships a pkg-config file (it seems to be the case), you can use something like
// #cgo pkg-config: libsodium
// #include "crypto_sign_ed25519.h"
See https://golang.org/cmd/cgo/ for more information about pkg-config support.
To see what cflags/libs you'd be getting (so what cgo will use), run:
pkg-config --cflags --libs libsodium
After manually installing a library on Linux, you have to type ldconfig so that the linker becomes aware of it.
Also, in order to get libsodium prototypes, you should simply include <sodium.h> not <sodium/crypto_sign_ed25519.h> (not meant to be included directly), and call sodium_init() before any other function so that internal data structures are properly initialized.
See how this is done in existing bindings for Go: https://github.com/jamesruan/sodium/blob/master/core.go
You may want to use these bindings instead of reinventing your own. If they are missing some of the functions you need, their maintainers will probably be happy to accept your pull requests.
The two main Go bindings for libsodium that I am aware of are sodium and libsodium-go.

Why is it possible to override symbols from some static libraries but not others?

I'm working on a tool that links against Clang, and I need to implement a small number of changes to some operations. To improve development times, instead of rebuilding Clang, I decided to redefine the symbols of interest in my program code, and let the linker take care of the rest: in most cases, the program version of a symbol that is defined in both program code and a static library takes precedence at link-time without a fuss. (The linked answer relates to Linux, but I found that to work on macOS too–usually.)
This worked great when I was using the stock Clang build for macOS that can be downloaded from the LLVM website. However, I am currently trying to switch to my company's customized Clang (which I built once from source, and hoped to further modify in the same way), and now I get duplicate symbol errors.
I don't know what is causing this issue. My project's linker flags have remained unchanged (save for one new static library): importantly, they do not contain -all_load or its -force_load cousin, which tell the linker to try to include every symbol defined in static libraries. The symbols that I'm trying to override look defined the same way when I check them with nm in the stock archive and in the custom archive. The difference has to be with how I built LLVM, but just knowing that doesn't really help me figure out what I need to change.
For instance, say that I want to redefine clang::Qualifiers::getAsString() const. I could do that just fine using the stock LLVM libraries, but now I would get a duplicate symbol error:
duplicate symbol __ZNK5clang10Qualifiers11getAsStringEv in:
.../Objects-normal/x86_64/TypePrinter.o
clang+llvm-internal/lib/libclangAST.a(TypePrinter.cpp.o)
Using nm -f darwin to inspect both archives, I would get very similar results for __ZNK5clang10Qualifiers11getAsStringEv:
# clang+llvm-6.0.0/lib/libclangAST.a
(undefined) external __ZNK5clang10Qualifiers11getAsStringEv
0000000000000bb0 (__TEXT,__text) external __ZNK5clang10Qualifiers11getAsStringEv
# clang+llvm-internal/lib/libclangAST.a
(undefined) external __ZNK5clang10Qualifiers11getAsStringEv
0000000000000d00 (__TEXT,__text) external __ZNK5clang10Qualifiers11getAsStringEv
So, assuming more or less identical symbol definitions, and identical linker flags, why was I able to override static library symbols this way before, and why am I no longer able to?
This part of the premise isn't quite correct:
In most cases, the program version of a symbol that is defined in both program code and a static library takes precedence at link-time without a fuss. (The linked answer relates to Linux, but I found that to work on macOS too–usually.)
The linked answer appears correct, but I originally misunderstood it. The actual behavior, as evidenced by passing -Wl,-why_load to Clang (or -why_load to the linker), goes as follow:
if a symbol is referenced, try to find its definition in the program code.
if it is defined in the program code, you're done; do not search static libraries.
if it is not defined in the program code, look up the .__SYMDEF file in the static library to know which object file has it.
use all the definitions from that object file.
The issue was that switching to the custom Clang, I accidentally pulled in references to symbols that were defined in the same object file as symbols that I am redefining, causing the linker to see both definitions. I was able to solve the problem by using the -why_load argument to the linker, and then looking for which symbol caused the problem object file to be loaded. I then duplicated the definition of that symbol to my program, and now the linker doesn't complain anymore.
The morale of the story is that this technique isn't as reliable on macOS as it is on Linux, and that if you do it, you kind of have to go all in. It's better to take the entire source file and copy it to your project than to try to pick symbols piecewise.
Actually this behavior is the same for Linux, see this reproducer:
First case: build library where symbols are in different object files:
//val.cpp - contains needed symbol
int val=42;
//wrong_main.cpp - contains duplicate symbol
int main(){
return 21;
}
>>> g++ -c val.cpp -o val.o
>>> g++ -c wrong_main.cpp -o wrong.o
>>> ar rcs libsingle.a val.o wrong.o
Linking against this library works, no multiple definition of main-error is issued, because no symbols at all are used from the object file wrong_main.o at all:
//main.cpp
extern int val;
int main(){
return val;
}
>>> g++ main.cpp -L. -lsingle -o works
Second case: both symbols are in the same object file:
//together.cpp - contains both, needed and duplicate, symbols
#include "val.cpp"
#include "wrong_main.cpp"
>>> g++ -c together.cpp -o together.o
>>> ar rcs libtogether.a all.o
Linking against libtogether.a doesn't work:
>>> g++ main.cpp -L. -ltogether -o doesntwork
./libtogether.a(all.o): In function `main':
all.cpp:(.text+0x0): multiple definition of `main'
/tmp/cc38isDb.o:main.cpp:(.text+0x0): first defined here
collect2: ld returned 1 exit status
The linker takes either the whole object file from a static library or nothing. In this case val is needed and so the object file together.o will be taken, but it also contains the duplicate symbol main and thus the linker issues an error.
A great description how the linker works on Linux (and very very similar on MacOS) is this article.

gcc 5.40 doesn't include standard include files?

gcc 5.4.0
cygwin 2.8.0
Win10
I've been been knocking my head around this problem.When I compile a simple program, see below, I get an error in one of the gcc include files. I checked the cygwin mailing list and no one has reported an error in the gcc download so I think it's a misunderstanding on my part but I can't figure what I did wrong. Prior to this point all the gcc include fileswere included automatically. Oh, and the compile is correct for other libraries.
The code is:
gcc -std=c++11 test.cpp or gcc test.cpp
include iostream
using namespace std;
int main(int argc, char** argv) { }
and the error message is:
/tmp/ccfBvaqg.o:test.cpp:(.text+0x44): undefined reference to std::ios_base::Init::Init()'
/tmp/ccfBvaqg.o:test.cpp:(.text+0x44): relocation truncated to fit: R_X86_64_PC32 against undefined symbolstd::ios_base::Init::Init()'
/tmp/ccfBvaqg.o:test.cpp:(.rdata$.refptr._ZNSt8ios_base4InitD1Ev[.refptr._ZNSt8ios_base4InitD1Ev]+0x0): undefined reference to `std::ios_base::Init::~Init()'
gcc is the C compiler driver. The compiler automatically detects the language based on the file name; that is why the compilation succeeded. However, the linker is not affected by the names of the source files. By default, the C compiler driver does not link with the C++ standard library.
Since you used the standard library (<iostream> is a bit atypical header file in such a way that merely including it causes a standard library function to be called at the start of the program), but did not link it, the linker fails. The solution is to link with the C++ standard library. The simplest way to do that is to use the C++ compiler driver (g++) which links the standard library by default.

Preprocessor flag to detect CGO build?

In a c file that's beside my go file and is compiled together via CGO, I'd like to check via preprocessor whether it's being compiled via go or not. I'd like to do this because, for example, I'd like to protect #include _cgo_export.h with #ifdef flags, since such header exists solely during compilation and I don't want my editor to warn about its absence.
From the documentation just do:
// #cgo CFLAGS: -DWHATEVER_YOU_WANT_TO_INDICATE_CGO=1
import "C"
(or just -D FOO if you don't want a value) or set CGO_CFLAGS in the environment.
You can see what is happening behind the scenes with go build -x.
For me it shows -D GOOS_freebsd -D GOARCH_amd64 while compiling the cgo generated _cgo_defun.c file, but only for that file and not to my own *.c files. So I don't think there are any usable predefined preprocessor flags (and the documentation also doesn't mention any).

Resources