Hello I'm going to work with thirdparty library (.so file) with golang in linux environment. So I tried to practice a bit with something trivial, like importing functions from linux native libs. And got stuck on importing and calling sqrt function. Here is my code:
package main
// #cgo LDFLAGS: -ldl
// #include <dlfcn.h>
// #include <stdio.h>
import "C"
import "fmt"
func main() {
export_name := "sqrt"
lib_path := "/lib/libm.so.6"
//Loading .so
handle := C.dlopen(C.CString(lib_path), C.RTLD_LAZY)
if handle == nil {
fmt.Println(lib_path+":\tNOT FOUND")
return
} else {
fmt.Println(lib_path+":\tSUCCESS")
}
//looking for function address
func_pointer := C.dlsym(handle, C.CString(export_name ))
if func_pointer == nil {
fmt.Println(export_name+":\tNOT FOUND")
return
} else {
fmt.Println(export_name+":\t", func_pointer)
}
//negotiating datatypes
//From c lib description: double sqrt(double x);
sqrt := *(*(func(float64)float64))(func_pointer)
//Calling function
sqrt(4)
}
when I run it, I always get segmentation violation:
/lib/libm.so.6: SUCCESS
sqrt: 0x7f37117ea270
unexpected fault address 0x0
fatal error: fault
[signal SIGSEGV: segmentation violation code=0x80 addr=0x0 pc=0x4019fa]
goroutine 1 [running]:
runtime.throw(0x4a6643, 0x5)
/usr/lib/go/src/runtime/panic.go:566 +0x95 fp=0xc42004be00 sp=0xc42004bde0
runtime.sigpanic()
/usr/lib/go/src/runtime/sigpanic_unix.go:27 +0x288 fp=0xc42004be58 sp=0xc42004be00
main.main()
/home/afx/goc/so.go:37 +0x2ba fp=0xc42004bf48 sp=0xc42004be58
runtime.main()
/usr/lib/go/src/runtime/proc.go:183 +0x1f4 fp=0xc42004bfa0 sp=0xc42004bf48
runtime.goexit()
/usr/lib/go/src/runtime/asm_amd64.s:2086 +0x1 fp=0xc42004bfa8 sp=0xc42004bfa0
goroutine 17 [syscall, locked to thread]:
runtime.goexit()
/usr/lib/go/src/runtime/asm_amd64.s:2086 +0x1
exit status 2
What is the problem ?
Thank you in advance.
P.S.
When I'm redefining function pointers of native Go functions (like here Go: convert unsafe.Pointer to function pointer and vice versa) everything works fine. But import fails.
Here is solution. I had to use bridge C function:
package main
// #cgo LDFLAGS: -ldl
// #include <dlfcn.h>
// #include <stdio.h>
//
// double
// my_sqrt_bridge(void *f, double x)
// {
// //description: ((return_data_type (*)(input_data_type))bridge_input_function_pointer) (bridge_input_value)
// return ((double (*)(double))f)(x);
// }
import "C"
import "fmt"
func main() {
export_name := "sqrt"
lib_path := "/lib/libm.so.6"
//Loading .so
handle := C.dlopen(C.CString(lib_path), C.RTLD_LAZY)
if handle == nil {
fmt.Println(lib_path + ":\tNOT FOUND")
return
} else {
fmt.Println(lib_path + ":\tSUCCESS")
}
//looking for function address
func_pointer := C.dlsym(handle, C.CString(export_name))
if func_pointer == nil {
fmt.Println(export_name + ":\tNOT FOUND")
return
} else {
fmt.Println(export_name+":\t", func_pointer)
}
fmt.Printf("%f", C.my_sqrt_bridge(func_pointer, 2))
}
Would it be possible in your case to let cgo link the library for you? Eg:
package main
/*
#cgo LDFLAGS: -lm
#include <math.h>
*/
import "C"
import "fmt"
func main() {
fmt.Printf("%g\n", C.sqrt(2)) //prints 1.4142135623730951
}
For a third party library /some/lib/dir/libxxx.so:
LDFLAGS: -L/some/lib/dir -lxxx
Related
I use GO code build a so file, and call it from go. at begin, i want to do some init job, so make sure the go call the shared library can reuse some variable. for example:
main.go
package main
import "C"
import (
"fmt"
"os"
"os/signal"
"syscall"
"time"
)
var (
globalConfig string
)
func main() {
}
//export search
func search() {
fmt.Println(globalConfig)
}
//export start
func start() {
// just keep running to keep globalConfig in RAM
go forever()
quitChannel := make(chan os.Signal, 1)
signal.Notify(quitChannel, syscall.SIGINT, syscall.SIGTERM)
<-quitChannel
}
func forever() {
v := "global value"
globalConfig = v
for {
time.Sleep(10 * time.Second)
fmt.Println(globalConfig)
}
}
and then generate the shared library.
go build -buildmode=c-shared -o libsdk.so main.go
call the shared lib.
cgo1.go
package main
/*
first step: export LD_LIBRARY_PATH="xxx sopath xxx"
*/
// #include "libsdk.h"
// #cgo LDFLAGS: -L. -lsdk
import "C"
func main() {
C.start()
// C.search()
}
cgo2.go
package main
/*
first step: export LD_LIBRARY_PATH="xxx sopath xxx"
*/
// #include "libsdk.h"
// #cgo LDFLAGS: -L. -lsdk
import "C"
func main() {
// C.start()
C.search()
}
and then the output first get the global value, the the second is null.
shouldn't shared lib share memory?
I had try call so file by define c code in same go file, It works!
but when I want use .h and .so file to do it. It not work.
this is my code:
sdk.go
package main
//#include <stdio.h>
//#include <stdlib.h>
//#include <string.h>
import "C"
func main() {
}
//export query
func query(info *C.char) *C.char {
GoStr := C.GoString(info)
return C.CString(GoStr)
}
i generate the so file with command:
go build -buildmode=c-shared -o sdk.so sdk.go
now I want to call it:
package main
/*
#cgo LDFLAGS: -L../lib -lsdk
#include "sdk.h"
*/
import "C"
import "fmt"
func main() {
val := C.query(C.CString("123"))
fmt.Println(val)
}
but some err occured,
Starting: /home/go/bin/dlv dap --check-go-version=false --listen=127.0.0.1:45412 --log-dest=3 from /root/goProject/src/cgo/src
DAP server listening at: 127.0.0.1:45412
Build Error: go build -o /root/goProject/src/cgo/src/__debug_bin -gcflags all=-N -l .
# CGO/src
./main.go:11:9: could not determine kind of name for C.query
cgo:
gcc errors for preamble:
In file included from ./main.go:5:0:
cgo-gcc-export-header-prolog:49:17: error: expected '=', ',', ';', 'asm' or '__attribute__' before 'query' (exit status 2)
I think this may because the sdk.h format.
it looks like https://go.dev/play/p/rxlUp16s-u9
any thoughts or suggestions are appreciated...
last
i think the problem is can't receive go string from so file, so i change the data type from go source file, string to *C.char, and everything is ok now.
I want to use Go library with some adjusted in C.
I made GoAdder Go function that having 3 arguments int x, y and function typed f.
And GoAdder function going to call f argument.
adder.go
package main
import "fmt"
import "C"
//export Ftest
type Ftest func(C.int);
//export GoAdder
func GoAdder(x, y int, f Ftest) int {
fmt.Printf("Go says: adding %v and %v\n", x, y)
f(10);
return x + y
}
func main() {} // Required but ignored
And I built go package as a static library named libadder.a in above like this:
go build -buildmode=c-archive -o libadder.a adder.go
And then I have written C++ codes bellow.
main.c
#include <stdio.h>
#include "adder/libadder.h"
void a( GoInt a ){
printf("Hello %d", a);
}
int main() {
printf("C says: about to call Go...\n");
int total = GoAdder(1, 7, &a);
printf("C says: Go calculated our total as %i\n", total);
return 0;
}
I have complied the source like this:
gcc -pthread -o static_go_lib main.c adder/libadder.a
when executing codes above it occurs errors
unexpected fault address 0x0
fatal error: fault
[signal SIGSEGV: segmentation violation code=0x80 addr=0x0 pc=0x563c99b74244]
goroutine 17 [running, locked to thread]:
...
How to get a correct C function address a in go function GoAdder ?
I referenced https://github.com/draffensperger/go-interlang/tree/master/c_to_go/static_go_lib
C function is just jump pointer, whereas golang's callback are complicate struct, and you cannot convert them.
There's only one (safe) way to call C function pointer:
1) Declare that somewhere:
//go:linkname cgocall runtime.cgocall
//go:nosplit
func cgocall(fn, arg unsafe.Pointer /* may be uintptr */) int32
2) Also, be type safe:
func GoAdder(x, y C.int, f unsafe.Pointer /* don't sure if this available, mb C.uintptr_t */) C.int
3) C function should take pointer (to what ever) as argument
void a(GoInt *a)
(I'd use native types)
4)
ten := 10
cgocall(f, unsafe.Pointer(&ten))
(It should be struct, if you wanna pass several args)
I want to disable the stack protection for my Go program. I'm trying to simulate a vulnerable C library and want to pivot into the Go code from there. However, I can't seem to find the right flags to disable the stack smashing detection.
Here is my go code:
package main
import "os"
import "fmt"
/*
#include "test.h"
*/
import "C"
func main() {
if (len(os.Args) >= 2){
argsWithoutProg := os.Args[1:]
if (argsWithoutProg[0] == "admin") {
secret();
}
} else {
regular()
}
}
func regular() {
fmt.Println("Go: BORING")
C.hackme()
}
func secret() {
fmt.Println("Go: SECRET FUNC")
}
and here is my c library code:
// #cgo CFLAGS: -g -O3 -fno-stack-protector
#include <stdint.h>
#include <stdio.h>
void hackme();
// this function is vulnerable and is used as an entrypoint to the go part
void hackme() {
char buf[3];
int r;
r = read(0, buf, 300);
printf("C: %d bytes read. Content: %s!\n", r, buf);
return;
}
I compile with go build -a poc.go.
As you can see, I already added some CFLAGS instructions at the beginning of my C library, but they don't seem to help. Previously I tried adding them via the -gcflags switch in my compilation command, but that was fruitless as well. Everytime I try to attack my program with a 300*A string, it is being detected:
Go: BORING
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
C: 300 bytes read. Content: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA!
*** stack smashing detected ***: <unknown> terminated
SIGABRT: abort
PC=0x7fd263dcee97 m=0 sigcode=18446744073709551610
goroutine 0 [idle]:
runtime: unknown pc 0x7fd263dcee97
stack: frame={sp:0x7ffda3507600, fp:0x0} stack=[0x7ffda2d08ad0,0x7ffda3507b00)
00007ffda3507500: 00007fd200000008 00007fd200000000
00007ffda3507510: 00007ffda3507610 0000000000000003
[...]
Checking the file with GDB also tells me the option is still active..
Could you please point me to some hints as to what I'm doing wrong or what flags I should use to disable this feature?
Thanks so much!
Start with the Go cgo command documentation.
Command cgo
Using cgo with the go command
To use cgo write normal Go code that imports a pseudo-package "C". The
Go code can then refer to types such as C.size_t, variables such as
C.stdout, or functions such as C.putchar.
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. For example:
// #include <stdio.h>
// #include <errno.h>
import "C"
The preamble may contain any C code, including function and variable
declarations and definitions. These may then be referred to from Go
code as though they were defined in the package "C". All names
declared in the preamble may be used, even if they start with a
lower-case letter. Exception: static variables in the preamble may not
be referenced from Go code; static functions are permitted.
See $GOROOT/misc/cgo/stdio and $GOROOT/misc/cgo/gmp for examples. See
"C? Go? Cgo!" for an introduction to using cgo:
https://golang.org/doc/articles/c_go_cgo.html.
CFLAGS, CPPFLAGS, CXXFLAGS, FFLAGS and LDFLAGS may be defined with
pseudo #cgo directives within these comments to tweak the behavior of
the C, C++ or Fortran compiler. Values defined in multiple directives
are concatenated together. The directive can include a list of build
constraints limiting its effect to systems satisfying one of the
constraints (see
https://golang.org/pkg/go/build/#hdr-Build_Constraints for details
about the constraint syntax). For example:
// #cgo CFLAGS: -DPNG_DEBUG=1
// #cgo amd64 386 CFLAGS: -DX86=1
// #cgo LDFLAGS: -lpng
// #include <png.h>
import "C"
In particular:
To use cgo write normal Go code that imports a pseudo-package "C".
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.
CFLAGS may be defined with
pseudo #cgo directives within these comments to tweak the behavior of
the C compiler.
For your example:
/*
#cgo CFLAGS: -g -O3 -fno-stack-protector
#include "test.h"
*/
import "C"
Output (no stack smashing detected):
$ go build -a poc.go && ./poc
Go: BORING
AAAAAAAAAAAAAAA
C: 16 bytes read. Content: AAAAAAAAAAAAAAA
!
fatal error: unexpected signal during runtime execution
[signal SIGSEGV: segmentation violation code=0x1 addr=0xa41414141 pc=0xa41414141]
runtime stack:
runtime.throw(0x4bb802, 0x2a)
/home/peter/go/src/runtime/panic.go:608 +0x72
runtime.sigpanic()
/home/peter/go/src/runtime/signal_unix.go:374 +0x2ec
goroutine 1 [syscall]:
runtime.cgocall(0x484e90, 0xc000052f38, 0x0)
/home/peter/go/src/runtime/cgocall.go:128 +0x5b fp=0xc000052f08 sp=0xc000052ed0 pc=0x403deb
main._Cfunc_hackme()
_cgo_gotypes.go:41 +0x41 fp=0xc000052f38 sp=0xc000052f08 pc=0x484c51
main.regular()
/home/peter/gopath/src/poc/poc.go:25 +0x62 fp=0xc000052f88 sp=0xc000052f38 pc=0x484d52
main.main()
/home/peter/gopath/src/poc/poc.go:19 +0x65 fp=0xc000052f98 sp=0xc000052f88 pc=0x484cd5
runtime.main()
/home/peter/go/src/runtime/proc.go:201 +0x1ec fp=0xc000052fe0 sp=0xc000052f98 pc=0x42928c
runtime.goexit()
/home/peter/go/src/runtime/asm_amd64.s:1340 +0x1 fp=0xc000052fe8 sp=0xc000052fe0 pc=0x450cd1
$
poc.go:
package main
import "os"
import "fmt"
/*
#cgo CFLAGS: -g -O3 -fno-stack-protector
#include "test.h"
*/
import "C"
func main() {
if (len(os.Args) >= 2){
argsWithoutProg := os.Args[1:]
if (argsWithoutProg[0] == "admin") {
secret();
}
} else {
regular()
}
}
func regular() {
fmt.Println("Go: BORING")
C.hackme()
}
func secret() {
fmt.Println("Go: SECRET FUNC")
}
test.h:
#include <stdint.h>
#include <stdio.h>
void hackme();
// this function is vulnerable and is used as an entrypoint to the go part
void hackme() {
char buf[3];
int r;
r = read(0, buf, 300);
printf("C: %d bytes read. Content: %s!\n", r, buf);
return;
}
Without -fno-stack-protector:
/*
#cgo CFLAGS: -g -O3
#include "test.h"
*/
import "C"
Output (stack smashing detected):
$ go build -a poc.go && ./poc
Go: BORING
AAAAAAAAAAAAAAA
C: 16 bytes read. Content: AAAAAAAAAAAAAAA
!
*** stack smashing detected ***: <unknown> terminated
SIGABRT: abort
PC=0x7f1c5323ee97 m=0 sigcode=18446744073709551610
$
I am writing some go code that exports a function like that:
package main
import "C"
//export returnString
func returnString() string {
//
gostring := "hello world"
return gostring
}
func main() {}
I build the .so and the header file by using go build -buildmode=c-shared, but when I call returnString() in my C code, I get panic: runtime error: cgo result has Go pointer
Is there a way to to this in go 1.9?
You need to convert your go string to *C.char. C.Cstring is utility function for that.
package main
import "C"
//export returnString
func returnString() *C.char {
gostring := "hello world"
return C.CString(gostring)
}
func main() {}