cgo calling share library: cannot find lib or function? - go

I'm using the sample code of chapter 13 of The Go Programming Language as below:
$ cat bzip2.c
#include <bzlib.h>
int bz2compress(bz_stream *s, int action,
char *in, unsigned *inlen, char *out, unsigned *outlen) {
s->next_in = in;
s->avail_in = *inlen;
s->next_out = out;
s->avail_out = *outlen;
int r = BZ2_bzCompress(s, action);
*inlen -= s->avail_in;
*outlen -= s->avail_out;
s->next_in = s->next_out = NULL;
return r;
}
$ cat usebzip2.go
// Package bzip provides a writer that uses bzip2 compression (bzip.org).
package main
import "C"
import (
"io"
"log"
"os"
"testing"
"unsafe"
)
type writer struct {
w io.Writer // underlying output stream
stream *C.bz_stream
outbuf [64 * 1024]byte
}
// Close flushes the compressed data and closes the stream.
// It does not close the underlying io.Writer.
func (w *writer) Close() error {
if w.stream == nil {
panic("closed")
}
defer func() {
C.BZ2_bzCompressEnd(w.stream)
C.bz2free(w.stream)
w.stream = nil
}()
for {
inlen, outlen := C.uint(0), C.uint(cap(w.outbuf))
r := C.bz2compress(w.stream, C.BZ_FINISH, nil, &inlen,
(*C.char)(unsafe.Pointer(&w.outbuf)), &outlen)
if _, err := w.w.Write(w.outbuf[:outlen]); err != nil {
return err
}
if r == C.BZ_STREAM_END {
return nil
}
}
}
// NewWriter returns a writer for bzip2-compressed streams.
func NewWriter(out io.Writer) io.WriteCloser {
const blockSize = 9
const verbosity = 0
const workFactor = 30
w := &writer{w: out, stream: C.bz2alloc()}
C.BZ2_bzCompressInit(w.stream, blockSize, verbosity, workFactor)
return w
}
func main() {
w := NewWriter(os.Stdout)
if _, err := io.Copy(w, os.Stdin); err != nil {
log.Fatalf("bzipper: %v\n", err)
}
if err := w.Close(); err != nil {
log.Fatalf("bzipper: close: %v\n", err)
}
}
First I compile the .c file:
gcc -I/usr/include -L/usr/lib -lbz2 --shared bzip2.c -fPIC -o libbzip2.so
The linux environment LD_LIBRARY_PATH contains ".", and then go build fails:
go build usebzip2.go
# command-line-arguments
/tmp/go-build677611698/b001/_x002.o: In function `_cgo_22d5d7fabfe4_Cfunc_bz2compress':
/tmp/go-build/cgo-gcc-prolog:118: undefined reference to `bz2compress'
collect2: error: ld returned 1 exit status
So how to fix it? I'm using ubuntu 18.04 LTS. Thanks a lot.

Don't run:
go build usebzip2.go
but rather:
go build
(and you don't need to invoke gcc directly on bzip2.c). When you use this process, you'll get many more (but different) errors because you have not put in the right directives before the:
import "C"
line. You need a comment (or series of comments) telling cgo about the functions you intend to provide, or providing those functions inline, and to direct the link phase to use -lbz2. In particular, you will need to:
#include <bzlib.h>
provide a bz2alloc function
provide a bz2free function
provide a declaration for your bz2compress function
set the LDFLAGS to include -lbz2
The actual bz2alloc and bz2free are short and simple and therefore can be included directly in this header block:
package main
/*
#cgo LDFLAGS: -lbz2
#include <bzlib.h>
#include <stdlib.h>
bz_stream *bz2alloc() { return calloc(1, sizeof(bz_stream)); }
int bz2compress(bz_stream *s, int action,
char *in, unsigned *intlen, char *out, unsigned *outlen);
void bz2free(bz_stream* s) { free(s); }
*/
import "C"
If you insert this and run go build you will now see a different and more useful error:
./usebzip2.go:60:2: cannot use w (type *writer) as type io.WriteCloser in return argument:
*writer does not implement io.WriteCloser (missing Write method)
which is of course because type writer does not implement Write.
(There's a completed version of exercise 13.3—not mine—at https://github.com/torbiak/gopl/tree/master/ex13.3. Note that they have augmented theirs to use locking as well, making it safe to call the write function from multiple goroutines simultaneously.)

Related

How to return go (array/slice/ist) to a C function

I have C code in which I am calling golang functions. I am able to do it for primitives data types (int/float etc.) but I want to return some other data structure like array/list/slice.
I could not find any solution on internet.
Looking for help.
Want to return a array/slice/list of string data type.
It would be helpful if you provide additional information, i.e. example code you are currently working on.
As stated from the Cgo documentation page:
Go array types are not supported; use a C pointer
To do so
hello.go
package main
// #include <stdlib.h>
import "C"
import "unsafe"
// StringSlice is a wrapper arround GoStringSlice to make it usable in C.
//export StringSlice
func StringSlice() **C.char {
x := GoStringSlice()
ret := C.malloc(C.size_t(len(x)) * C.size_t(unsafe.Sizeof(uintptr(0))))
// convert to usable format so we are able to fill it with data
pRet := (*[1<<30 - 1]*C.char)(ret)
for i, item := range x {
pRet[i] = C.CString(item)
}
return (**C.char)(ret)
}
func GoStringSlice() []string {
return []string{
"Hello",
"World",
}
}
func main() {}
hello.c
#include <stdio.h>
#include "hello.h"
int main() {
printf("Hello from C!\n");
char **slice = StringSlice();
int numItems = sizeof(slice) / sizeof(char *);
printf("Number of items: %d\n", numItems+1);
printf("String #0: %s\n", *slice);
slice++;
printf("String #1: %s\n", *slice);
return 0;
}
You have to execute go build -buildmode=c-archive hello.go which will generate a hello.h and hello.a.
The hello.a has to be compiled with your C code: gcc -pthread hello.c hello.a -o hello.

golang get char* as return value from dll

I'm using golang to call a Dll function like char* fn(), the dll is not written by myself and I cannot change it. Here's my code:
package main
import (
"fmt"
"syscall"
"unsafe"
)
func main() {
dll := syscall.MustLoadDLL("my.dll")
fn := dll.MustFindProc("fn")
r, _, _ := fn.Call()
p := (*byte)(unsafe.Pointer(r))
// define a slice to fill with the p string
data := make([]byte, 0)
// loop until find '\0'
for *p != 0 {
data = append(data, *p) // append 1 byte
r += unsafe.Sizeof(byte(0)) // move r to next byte
p = (*byte)(unsafe.Pointer(r)) // get the byte value
}
name := string(data) // convert to Golang string
fmt.Println(name)
}
I have some questions:
Is there any better way of doing this? There're hundred of dll functions like this, I'll have to write the loop for all functions.
For very-long-string like 100k+ bytes, will append() cause performance issue?
Solved. the unsafe.Pointer(r) causes linter govet shows warning possible misuse of unsafe.Pointer, but the code runs fine, how to avoid this warning? Solution: This can be solved by adding -unsafeptr=false to govet command line, for vim-ale, add let g:ale_go_govet_options = '-unsafeptr=false'.
Casting uintptr as upointer is haram.
You must read the rules:
https://golang.org/pkg/unsafe/#Pointer
But there's hacky way, that shouldn't produce warning:
//go:linkname gostringn runtime.gostringn
func gostringn(p uintptr, l int) string
//go:linkname findnull runtime.findnull
//go:nosplit
func findnull(s uintptr) int
// ....
name := gostringn(r, findnull(r))
Functions takes pointer, but we link them from runtime as uintptr because they have same sizeof.
Might work in theory. But is also frowned upon.
Getting back to your code, as JimB said, you could do it one line with:
name := C.GoString((*C.char)(unsafe.Pointer(r)))
I got the following solution by tracking the os.Args of the go source code, But I am based on go1.17. If you are in another version, you can read the source code to solve it.
func UintPtrToString(r uintptr) string {
p := (*uint16)(unsafe.Pointer(r))
if p == nil {
return ""
}
n, end, add := 0, unsafe.Pointer(p), unsafe.Sizeof(*p)
for *(*uint16)(end) != 0 {
end = unsafe.Add(end, add)
n++
}
return string(utf16.Decode(unsafe.Slice(p, n)))
}

How to return a slice in Go and calling from C?

I am trying to use cgo to use Go package in C code. Following is a piece of my code:
func LinearTransformToUInt8(frame []int64, winWidth int, winCenter int) []uint8 {
var transformed []uint8
// my cool code
return transformed
}
However, when calling from C, it says
panic: runtime error: cgo result has Go pointer
I believe the problem is the returned []uint8 is a Go type, which should be replaced by a C type. However, I don't know how to achieve it. Please help!
main.go
package main
import (
"C"
"unsafe"
)
import (
"reflect"
)
func main() {
}
//export phew
func phew() uintptr {
res := make([]uint8, 2)
for i := 0; i < 2; i++ {
res[i] = uint8(i + 1)
}
hdr := (*reflect.SliceHeader)(unsafe.Pointer(&res))
return hdr.Data
}
main.c
#include <stdio.h>
#include <inttypes.h>
#include "libtemp.h"
int main(){
uintptr_t resPtr = phew();
uint8_t *res = (uint8_t*)resPtr;
for (int i = 0; i < 2; i++){
printf("%d\n", res[i]);
}
printf("Exiting gracefully\n");
}
You cannot pass a Go pointer which contains other Go Pointer, slice,string,channel,function, interface, map contain pointers.
So one cannot pass them around, rules to passing around pointers are documented here and go's representation of basic types is documented here.
But some Go contributors were saying, one shouldn't return a Go pointer to C code in the first place.

How can I find procedure from a DLL using its ordinal value?

I'm trying to call procedure (without name) from a DLL using the ordinal value.
I can use this DLL in C#, setting the ordinal value to the property EntryPoint of DllImport.
... or you can identify the entry point by its ordinal. Ordinals are prefixed with the # sign, for example, #1. [...]
Example in C#:
[DllImport("dllname.dll", EntryPoint = "#3", CharSet = CharSet.Unicode, SetLastError = true, CallingConvention = CallingConvention.StdCall)]
public static extern int DeviceList(ref IntPtr hDeviceList);
When try find the procedure with "#" sign in Go, it shows the the following error:
Failed to find #3 procedure in dllname.dll: The specified procedure could not be found.
I used dumpbin to show the information of the DLL, no function has a name:
Is there a way to find a procedure with its ordinal value (like C#)?
There is a github issue here for this, but it seems not to have been merged as of Go 1.10.3 (the version I am using right now).
Anyway, the github issue links to a changeset with the respective function from which I extracted the code to do what you want here:
var (
kernel32 = syscall.NewLazyDLL("kernel32.dll")
procGetProcAddress = kernel32.NewProc("GetProcAddress")
)
// GetProcAddressByOrdinal retrieves the address of the exported
// function from module by ordinal.
func GetProcAddressByOrdinal(module syscall.Handle, ordinal uintptr) (uintptr, error) {
r0, _, _ := syscall.Syscall(procGetProcAddress.Addr(), 2, uintptr(module), ordinal, 0)
proc := uintptr(r0)
if proc == 0 {
return 0, syscall.EINVAL
}
return proc, nil
}
For completeness, here is the full example with which I tested this, using the Dependecy Walker I found that the first function in kernel32.dll is AcquireSRWLockExclusive and using the new function it shows that the proc addresses really match.
package main
import (
"fmt"
"syscall"
)
func main() {
dll, err := syscall.LoadDLL("kernel32.dll")
check(err)
want, err := syscall.GetProcAddress(dll.Handle, "AcquireSRWLockExclusive")
check(err)
fmt.Println(want)
first, err := GetProcAddressByOrdinal(dll.Handle, 1)
check(err)
fmt.Println(first)
}
func check(err error) {
if err != nil {
panic(err)
}
}
var (
kernel32 = syscall.NewLazyDLL("kernel32.dll")
procGetProcAddress = kernel32.NewProc("GetProcAddress")
)
// GetProcAddressByOrdinal retrieves the address of the exported
// function from module by ordinal.
func GetProcAddressByOrdinal(module syscall.Handle, ordinal uintptr) (uintptr, error) {
r0, _, _ := syscall.Syscall(procGetProcAddress.Addr(), 2, uintptr(module), ordinal, 0)
proc := uintptr(r0)
if proc == 0 {
return 0, syscall.EINVAL
}
return proc, nil
}

Create array of C struct and pass struct pointer to C function

I want to make a wrapper for my C function, which takes pointer to C struct as parameter.
In my Go code, I tried two approaches to allocate the space for C struct:
bps := make([]_Ctype_T32_Breakpoint, max) (1)
C.MyFunc((*_Ctype_T32_Breakpoint)(unsafe.Pointer(&bps[0]), C.int(max))
bps := make([]C.struct_T32_Breakpoint, max) (2)
C.MyFunc((*C.struct_T32_Breakpoint)(unsafe.Pointer(&bps[0]), C.int(max))
For the (1) method, it works, but for (2) method, I got error message :
cannot use (*[0]byte)(unsafe.Pointer(&bps[0])) (type *[0]byte) as type *_Ctype_T32_Breakpoint in function argument
Why method (2) created type of *[0]byte instead of *C.struct_T32_Breakpoint, and it seems the cast to *C.struct_T32_Breakpoint doesn't work.
What's the difference of using method (1) and (2)?
Thanks.
Test Code
File: t32.h
#ifndef __T32_H__
#define __T32_H__
typedef unsigned char byte;
typedef unsigned short word;
typedef unsigned int dword;
typedef struct t32_breakpoint {
dword address;
byte enabled;
dword type;
dword auxtype;
} T32_Breakpoint;
int T32_GetBreakpointList( int *, T32_Breakpoint*, int );
#endif /* __T32_H__ */
File: remote.c
#include "t32.h"
int T32_GetBreakpointList (int* numbps, T32_Breakpoint* bps, int max)
{
return 0;
}
File : t32.go
package t32
// #cgo linux,amd64 CFLAGS: -DT32HOST_LINUX_X64
// #cgo linux,386 CFLAGS: -DT32HOST_LINUX_X86
// #cgo windows,amd64 CFLAGS: -D_WIN64
// #cgo windows,386 CFLAGS: -D_WIN32
// #cgo windows CFLAGS: -fno-stack-check -fno-stack-protector -mno-stack-arg-probe
// #cgo windows LDFLAGS: -lkernel32 -luser32 -lwsock32
// #include "t32.h"
// #include <stdlib.h>
import "C"
import (
"errors"
"unsafe"
)
const (
_INVALID_U64 = 0xFFFFFFFFFFFFFFFF
_INVALID_S64 = -1
_INVALID_U32 = 0xFFFFFFFF
_INVALID_S32 = -1
_INVALID_U16 = 0xFFFF
_INVALID_S16 = -1
_INVALID_U8 = 0xFF
_INVALID_S8 = -1
)
type BreakPoint struct {
Address uint32
Enabled int8
Type uint32
Auxtype uint32
}
func GetBreakpointList(max int) (int32, []BreakPoint, error) {
var numbps int32
// bps := make([]_Ctype_T32_Breakpoint, max) // Method (1), can compile
// code, err := C.T32_GetBreakpointList((*C.int)(&numbps), (*_Ctype_T32_Breakpoint)(unsafe.Pointer(&bps[0])), C.int(max))
bps := make([]C.struct_T32_Breakpoint, max) // Method (2) can't compile
code, err := C.T32_GetBreakpointList((*C.int)(&numbps), (*C.struct_T32_Breakpoint)(unsafe.Pointer(&bps[0])), C.int(max))
if err != nil {
return _INVALID_S32, nil, err
} else if code != 0 {
return _INVALID_S32, nil, errors.New("T32_GetBreakpointList Error")
}
if numbps > 0 {
var gbps = make([]BreakPoint, numbps)
for i := 0; i < int(numbps); i++ {
gbps[i].Address = uint32(bps[i].address)
gbps[i].Auxtype = uint32(bps[i].auxtype)
gbps[i].Enabled = int8(bps[i].enabled)
gbps[i].Type = uint32(bps[i]._type)
}
return numbps, gbps, nil
}
return 0, nil, nil
}
There are two reasons that the second snippet doesn't compile.
The first is that the capitalization doesn't match. The C declaration declares struct t32_breakpoint, but the Go code refers to struct T32_Breakpoint. C is case-sensitive, but it permits creating a pointer to a struct that has not been defined. So that is what CGo thinks you are doing. This pointer can't be dereferenced, since the contents and size of the struct are not known. It's basically equivalent to a void pointer, but with stronger typing. Cgo treats it as a void pointer, translating it as *[0]byte, i.e. a pointer to a zero-sized object. But unlike C, Go doesn't allow passing a void pointer to just any function that takes a pointer. So it has a type mismatch.
The other issue is that if you want to pass a struct t32_breakpoint* instead of a T32_Breakpoint*, you will need to change your function declaration in C. The distinction between the two types is probably not significant in C, but it is in Go, since Go has stronger typing than C.

Resources