I am trying to wrap around a C library with Go. The library is built with CMake and produces a static library file. I added the #cgo stuff at the beginning of the .go file with the proper CFLAGS and LDFLAGS, however, I keep getting undefined reference errors when running go build memorymodule.go.
Here is my code:
package main
/*
#cgo CFLAGS: -IMemoryModule
#cgo LDFLAGS: MemoryModule/build/MemoryModule.a
#include "MemoryModule/MemoryModule.h"
*/
import "C"
import (
"fmt"
"io/ioutil"
"os"
"unsafe"
)
const SIZE int = 1024
func end(msg string) {
fmt.Println(msg)
os.Exit(1)
}
func check(err error, msg string) {
if err != nil {
end(msg)
}
}
func main() {
bin, err := ioutil.ReadFile(os.Args[0])
check(err, "error reading file")
// Convert the args passed to this program into a C array of C strings
var cArgs []*C.char
for _, goString := range os.Args {
cArgs = append(cArgs, C.CString(goString))
}
// Load the reconstructed binary from memory
handle := C.MemoryLoadLibraryEx(
unsafe.Pointer(&bin[0]), // void *data
(C.size_t)(len(bin)), // size_t
(*[0]byte)(C.MemoryDefaultAlloc), // Alloc func ptr
(*[0]byte)(C.MemoryDefaultFree), // Free func ptr
(*[0]byte)(C.MemoryDefaultLoadLibrary), // loadLibrary func ptr
(*[0]byte)(C.MemoryDefaultGetProcAddress), // getProcAddress func ptr
(*[0]byte)(C.MemoryDefaultFreeLibrary), // freeLibrary func ptr
unsafe.Pointer(&cArgs[0]), // void *userdata
)
// Execute binary
C.MemoryCallEntryPoint(handle)
// Cleanup
C.MemoryFreeLibrary(handle)
}
Here is the results from the go build ... with -x to help with debugging:
cd /home/wlaw/go-memory-module
i686-w64-mingw32-gcc -I . -m32 -mthreads -fmessage-length=0 -fdebug-prefix-map=$WORK=/tmp/go-build -gno-record-gcc-switches -o $WORK/command-line-arguments/_obj/_cgo_.o $WORK/command-line-arguments/_obj/_cgo_main.o $WORK/command-line-arguments/_obj/_cgo_export.o $WORK/command-line-arguments/_obj/memorymodule.cgo2.o -g -O2 MemoryModule/build/MemoryModule.a
# command-line-arguments
/tmp/go-build590803163/command-line-arguments/_obj/_cgo_main.o:_cgo_main.c:(.data+0x0): undefined reference to `MemoryDefaultLoadLibrary'
/tmp/go-build590803163/command-line-arguments/_obj/_cgo_main.o:_cgo_main.c:(.data+0x4): undefined reference to `MemoryDefaultGetProcAddress'
/tmp/go-build590803163/command-line-arguments/_obj/_cgo_main.o:_cgo_main.c:(.data+0x8): undefined reference to `MemoryDefaultFreeLibrary'
/tmp/go-build590803163/command-line-arguments/_obj/_cgo_main.o:_cgo_main.c:(.data+0xc): undefined reference to `MemoryDefaultFree'
/tmp/go-build590803163/command-line-arguments/_obj/_cgo_main.o:_cgo_main.c:(.data+0x10): undefined reference to `MemoryDefaultAlloc'
/tmp/go-build590803163/command-line-arguments/_obj/memorymodule.cgo2.o: In function `cgo_50ced23471ff_Cfunc_MemoryCallEntryPoint':
/tmp/go-build/command-line-arguments/_obj/cgo-gcc-prolog:40: undefined reference to `MemoryCallEntryPoint'
/tmp/go-build590803163/command-line-arguments/_obj/memorymodule.cgo2.o: In function `cgo_50ced23471ff_Cfunc_MemoryLoadLibraryEx':
/tmp/go-build/command-line-arguments/_obj/cgo-gcc-prolog:76: undefined reference to `MemoryLoadLibraryEx'
/tmp/go-build590803163/command-line-arguments/_obj/memorymodule.cgo2.o: In function `cgo_50ced23471ff_Cfunc_MemoryFreeLibrary':
/tmp/go-build/command-line-arguments/_obj/cgo-gcc-prolog:54: undefined reference to `MemoryFreeLibrary'
collect2: error: ld returned 1 exit status
Makefile:9: recipe for target 'all' failed
make: *** [all] Error 2
Whats interesting is if I compile the MemoryModule dependency using make instead of CMake (by editing my Makefile) such that it produces an object file instead of a static library file, and modify my source code to link with that instead, I get no problems:
package main
/*
#cgo CFLAGS: -IMemoryModule
#cgo LDFLAGS: MemoryModule/MemoryModule.o
#include "MemoryModule/MemoryModule.h"
*/
import "C"
...
My Makefile for reference:
ifneq ("$(shell which i686-w64-mingw32-gcc)","")
compiler = i686-w64-mingw32-gcc
else
compiler = i586-mingw32msvc-gcc
endif
# Build the dependencies first (subdirs), then move onto the meat and potatoes.
all: MemoryModule
CC=$(compiler) CGO_ENABLED=1 GOOS=windows GOARCH=386 go build -x memorymodule.go
# Dependency build.
SUBDIRS = MemoryModule
subdirs: $(SUBDIRS)
$(SUBDIRS):
$(MAKE) -C $#
# Override default subdir build behavior (make) with cmake.
MemoryModule:
[ "`ls -A MemoryModule`" ] || git submodule update --init
# $(MAKE) -C $#
cmake -HMemoryModule -BMemoryModule/build
cmake --build MemoryModule/build --target MemoryModule
# Clean targed.
CLEANDIRS = $(SUBDIRS:%=clean-%)
clean: $(CLEANDIRS)
rm -f memorymodule.exe
$(CLEANDIRS):
$(MAKE) -C $(#:clean-%=%) clean
test:
$(MAKE) -C tests test
.PHONY: subdirs $(INSTALLDIRS) $(SUBDIRS) clean test
Any help would be appreciated!
Git project here: https://github.com/wheelerlaw/go-memory-module
Update:
Okay, so I stumbled across this SO question. So if I change my go build command to:
GOOS="windows" GOARCH="amd64" CGO_ENABLED="1" CC="x86_64-w64-mingw32-gcc" go build -x
from:
GOOS="windows" GOARCH="386" CGO_ENABLED="1" CC="i686-w64-mingw32-gcc" go build -x
... it works. So it looks like an incompatibility between the different archs. I'm going to download a 32 bit version of Go to see if that helps.
But what I don't understand is that the program compiles just fine even with the mis-matched archs when I compile C library into an object instead of a static library. Any ideas?
Related
I am trying to link guile to an Rcpp file. It seems like things compile but there is an error when loading:
sourceCpp("test_2.cpp", rebuild = TRUE, showOutput = TRUE)
/usr/lib/R/bin/R CMD SHLIB --preclean -o 'sourceCpp_2.so' 'test_2.cpp'
g++-10 -I"/usr/share/R/include" -DNDEBUG -I"/home/matias/R/x86_64-pc-linux-gnu-library/4.0/Rcpp/include" -I"/home/matias/Documentos/Program/R/guile" -fpic -O3 -march=native -mtune=native -fPIC -pthread -I"/usr/include/guile/3.0" -c test_2.cpp -o test_2.o
g++-10 -shared -L/usr/lib/R/lib -lm -ldl -lgmpxx -lgmp -lmpfr -lmpc -lguile-3.0 -lgc -o sourceCpp_2.so test_2.o -L/usr/lib/R/lib -lR
Error in dyn.load("/tmp/Rtmpm2flY8/sourceCpp-x86_64-pc-linux-gnu-1.0.5/sourcecpp_29e2d33505085/sourceCpp_2.so") :
unable to load shared object '/tmp/Rtmpm2flY8/sourceCpp-x86_64-pc-linux-gnu-1.0.5/sourcecpp_29e2d33505085/sourceCpp_2.so':
/tmp/Rtmpm2flY8/sourceCpp-x86_64-pc-linux-gnu-1.0.5/sourcecpp_29e2d33505085/sourceCpp_2.so: undefined symbol: scm_init_guile
The linking works fine if I remove the Rcpp header and build directly with g++ instead.
My Makevars look like this:
CXX = g++-10
CXXFLAGS = -O3 -march=native -mtune=native -fPIC -pthread -I"/usr/include/guile/3.0"
CXXSTD = -std=c++11
LDFLAGS = -lm -ldl -lgmpxx -lgmp -lmpfr -lmpc -lguile-3.0 -lgc
The .cpp file:
#include <Rcpp.h>
#include <stdio.h>
#include <libguile.h>
using namespace Rcpp;
// [[Rcpp::export]]
int test_guile() {
SCM func, func2;
scm_init_guile();
scm_c_primitive_load("script.scm");
func = scm_variable_ref(scm_c_lookup("simple-func"));
func2 = scm_variable_ref(scm_c_lookup("quick-test"));
scm_call_0(func);
scm_call_0(func2);
return 0;
}
You are so, so close. You essentially solved this. I just took your file, made a small modification of making the script an argument and (as you didn't post script.scm) commented out the content-specific stuff. We still load it though:
#include <Rcpp.h>
#include <stdio.h>
#include <libguile.h>
using namespace Rcpp;
// [[Rcpp::export]]
int test_guile(std::string file) {
SCM func, func2;
scm_init_guile();
scm_c_primitive_load(file.c_str());
//func = scm_variable_ref(scm_c_lookup("simple-func"));
//func2 = scm_variable_ref(scm_c_lookup("quick-test"));
//scm_call_0(func);
//scm_call_0(func2);
return 0;
}
Similarly I just added a src/Makevars to the Rcpp.package.skeleton() created file. This is not good enough to ship as you need some minimal configure or alike logic to get these values from guile-config-3.0 or alike. But it passes the litmus test. C++11 is the default already under R 4.0.*, and the compiler is recent on my box anyway so we just have this (after removing a few GNU GMP and related parts we do not need):
PKG_CXXFLAGS = -I"/usr/include/guile/3.0"
PKG_LIBS = -lguile-3.0 -lgc
This now builds, installs, and runs just fine:
> file <- system.file("guile", "script.scm", package="RcppGuile")
> RcppGuile::test_guile(file)
[1] 0
>
For reference, I committed and pushed the entire example package here. If you provide a pointer to script.scm we can add that too.
Edit: A few seconds of googling leads to the script.scm you may have used so now we have a fully working example with a working embedded Guile interpreter:
> library(RcppGuile)
> test_guile(system.file("guile", "script.scm", package="RcppGuile"))
Script called, now I can change this
Adding another function, can modify without recompilation
Called this, without recompiling the C code
[1] 0
>
I wanna import a c-shared-library to go that generated by Cython in python 3.7, try do it by cgo.
in this case:
go version go1.12.7 linux/amd64
Python 3.7.3
Cython version 0.29.12
os: Manjaro 18.0.4
Kernel: x86_64 Linux 5.1.19-1
I will continue:
make a python file vim pylib.pyx:
#!python
cdef public void hello():
print("hello world!")
and run python -m cython pylib.pyx for generate the c-shared-library, I have two files, pylib.c and pylib.h.
now, try import these to golang, so make a go file vim test.go:
package main
/*
#include </usr/include/python3.7m/Python.h>
#include "pylib.h"
*/
import "C"
import "fmt"
func main() {
C.hello()
fmt.Println("done")
}
finaly, I run go run test.go:
I have the following output:
# command-line-arguments
/usr/bin/ld: $WORK/b001/_x002.o: in function `_cgo_51159acd5c8e_Cfunc_hello':
/tmp/go-build/cgo-gcc-prolog:48: undefined reference to `hello'
collect2: error: ld returned 1 exit status
I try import it to c too but I encountered a similar output like this:
undefined reference to `hello'
ld returned 1 exit status
I don't know what to do, help me, please.
:(
I run go run test.go: I have the following output:
# command-line-arguments
/usr/bin/ld: $WORK/b001/_x002.o: in function `_cgo_51159acd5c8e_Cfunc_hello':
/tmp/go-build/cgo-gcc-prolog:48: undefined reference to `hello'
collect2: error: ld returned 1 exit status
We can generate an equivalent error message with the following code.
package main
/*
#include <math.h>
*/
import "C"
import "fmt"
func main() {
cube2 := C.pow(2.0, 3.0)
fmt.Println(cube2)
}
Output:
$ go run cube2.go
# command-line-arguments
/usr/bin/ld: $WORK/b001/_x002.o: in function `_cgo_f6c6fa139eda_Cfunc_pow':
/tmp/go-build/cgo-gcc-prolog:53: undefined reference to `pow'
collect2: error: ld returned 1 exit status
$
In both cases, ld (the linker) can't find a C function after looking in the usual places: undefined reference to 'pow' or undefined reference to 'hello'.
Let's tell cgo where to find the C pow function in the C math library: m.
For cgo, using ld flags,
#cgo LDFLAGS: -lm
GCC: 3.14 Options for Linking
-llibrary
Search the library named library when linking.
Updating the previous code,
package main
/*
#cgo LDFLAGS: -lm
#include <math.h>
*/
import "C"
import "fmt"
func main() {
cube2 := C.pow(2.0, 3.0)
fmt.Println(cube2)
}
Output:
$ go run cube2.go
8
$
This illustrates a basic cgo principle: include a C header file for your C library and point to the location of the C library.
References:
Cgo and Python : Embedding CPython: a primer
I am trying to integrate Golang script with existing C shared library.
The C shared library in turn loads other shared libraries at run time.
While I am trying to run
go run gotest.go
it throws error
./libtest.so: undefined reference to `dlopen'
./libtest.so: undefined reference to `dlclose'
./libtest.so: undefined reference to `dlerror'
./libtest.so: undefined reference to `dlsym'
collect2: error: ld returned 1 exit status
I have created separate C executable to load this libtest.so at run time and that is working properly.
Any idea what should be done to fix the issue?
I have tried following commands
sudo go build -compiler=gccgo -gccgoflags="-ltest" gotest.go
also tried the command
sudo go build -compiler=gccgo -gccgoflags="-ltest -ldl" gotest.go
Also tried the command
go run gotest.go
===============================================
Here is the code I am using
testapi.h
typedef Students{
char name[40];
int id;
} Student;
Student* getStudent();
test.c
#include <stdlib.h>
#include <stdio.h>
#include "mylib.h"
#include "testapi.h"
int rc = 0;
Student *s = 0;
Student* getStudent(){
rc = loadmylib(); //This function loads another shared library
if (!rc)
{
rc = initlib(); //This calls dlsym to get the necessary function from opened shared library
if (!rc)
{
s= (student *)malloc(sizeof(Student));
//use the function pointer received from dlsym to populate the Student struct 's'
}
}
return s;
}
I have following entries in my gotest.go file
package main
/*
#cgo CFLAGS: -I.
#cgo LDFLAGS: -L. -ltest -ldl
#include "testapi.h"
*/
import "C"
import (
"fmt"
)
type Student struct{
name string
id int
}
func main() {
st := C.getStudent()
fmt.Println("Name: ", string(st.name))
}
I have copied testapi.h, gotest.go, mylib.h, libmylib.so, libtest.so in the same directory.
Then run the command
go run gotest.go
It has thrown the error " error: undefined reference to 'getStudent' "
What am I doing wrong here?
Am I supposed to run command
sudo go build -compiler=gccgo -gccgoflags="-ltest" gotest.go
These are my C codes simply print “Hello" Message. And I want to make mylib.c as shared library.
[mylib.c]
#include <stdio.h>
int mylib();
int main(){
mylib();
return 0;
}
int mylib(){
printf("### Hello I am mylib #####\n");
return 0;
}
[drive.c]
#include <stdio.h>
int mylib();
int main(){
mylib();
return 0;
}
At the firest I compiled mylib.c with folowing command line to make mylib.o
gcc –fPIC –g –c –Wall mylib.c
Then tried to make it shared librarly like this
gcc -shared -Wl,-soname,libmylib.so.1 -o /opt/lib/libmylib.so.1.0.1 mylib.o -lc
And I did ldconfig to update /etc/ld.so.cache
Finaly I compiled drive.c link with mylib but linker showed error
gcc -g -Wall -Wextra -pedantic -I./ -L./ -o drive drive.c –lmylib
/usr/bin/ld: cannot find –lmylib
Dose someone tell me how can I compile it?
In my way, you have to follow some ways to use shared library in C.
At first I have created a header file named "shared_library.h", in this file I have introduced a function named "method" as a function of this library.
The code is following:
/*-------This is starting of shared_library.h file-----------*/
void method();
/*-------------This is ending of shared_library.h file--------*/
Then I have defined the method in another file named "shared_library.c". The definition as in code is:
/*-------------This is starting of shared_library.c file---------*/
#include "shared_library.h"
void method()
{
printf("Method is called");
}
/*-------------This is ending of shared_library.c file---------*/
And finally, the header "shared_library.h" is ready to use. I use the library in my main C file named "main.c". The contents of "main.c" are as follows:
/*-------------This is starting of main.c file----------------*/
#include <stdio.h>
#include "shared_library.h"
int main()
{
method();
return 0;
}
/*-------------This is ending of main.c file----------------\*/
I found this article ld cannot find an existing library.
It works if I change to gcc -g -Wall -Wextra -pedantic -I./ -L/opt/lib -o drive drive.c –l:libmylib.so.1
I want to reproduce this Perl code in C, bundling API and CLI in the same C source code file (scriptedmain). This is done in Python with if __name__=="__main__": main() and in gcc/Unix, this looks like:
$ gcc -o scriptedmain scriptedmain.c scriptedmain.h
$ ./scriptedmain
Main: The meaning of life is 42
$ gcc -o test test.c scriptedmain.c scriptedmain.h
$ ./test
Test: The meaning of life is 42
scriptedmain.h
int meaning_of_life();
scriptedmain.c
#include <stdio.h>
int meaning_of_life() {
return 42;
}
int __attribute__((weak)) main() {
printf("Main: The meaning of life is %d\n", meaning_of_life());
return 0;
}
test.c
#include "scriptedmain.h"
#include <stdio.h>
extern int meaning_of_life();
int main() {
printf("Test: The meaning of life is %d\n", meaning_of_life());
return 0;
}
However, when I try to compile with gcc/Strawberry, I get:
C:\>gcc -o scriptedmain scriptedmain.c scriptedmain.h
c:/strawberry/c/bin/../lib/gcc/i686-w64-mingw32/4.4.3/../../../../i686-w64-mingw32/lib/libmingw32.a(lib32_libmingw32_a-crt0_c.o): In function `main':
/opt/W64_156151-src.32/build-crt/../mingw-w64-crt/crt/crt0_c.c:18: undefined reference to `WinMain#16'
collect2: ld returned 1 exit status
And when I try to compile with gcc/MinGW, I get:
$ gcc -o scriptedmain -mwindows scriptedmain.c scriptedmain.h
c:/mingw/bin/../lib/gcc/mingw32/4.5.2/../../../libmingw32.a(main.o):main.c:(.text+0x104): undefined reference to `WinMain#16'
collect2: ld returned 1 exit status
How can I get GCC in Windows to recognize the __attribute__((weak)) syntax?
Also, G++ shows the same error.
I found a solution that works in Windows and in Unix: Simply wrap main() in preprocessor instructions that omits it unless explicit compiler flags are set.
scriptedmain.c:
#include <stdio.h>
int meaning_of_life() {
return 42;
}
#ifdef SCRIPTEDMAIN
int main() {
printf("Main: The meaning of life is %d\n", meaning_of_life());
return 0;
}
#endif
Now main() will be entirely omitted unless you compile with
gcc -o scriptedmain -DSCRIPTEDMAIN scriptedmain.c scriptedmain.h
This code is safe to import into other C code, because the preprocessor will strip out main(), leaving you to code your own main. The best part is that this solution no longer depends on obscure compiler macros, only simple preprocessor instructions. This solution works for C++ as well.
This isn't good practice in C regardless of operating system. Best practice in C for anything complicated enough to be worth separating into library and driver is to put main in a file all by itself.