What's the proper way to reading a binary file written by C?
I've a C header file, there are some 'struct's.
Is it possible to use these header file instead of manual re-write them in Go.
/* sample.h */
#define HEADER_SIZE 2048 //* large then sizeof(header)
typedef struct {
uint8_t version;
uint8_t endian;
uint32_t createTime;
} header;
typedef struct {
uint64_t data1;
uint32_t data2;
char name[128];
} record;
Here is my starting Go program with pseudo code
package "main"
// #include "sample.h"
import "C"
func main() {
f, _ := os.Open("sample_file")
// read(buf, HEADER_SIZE) and print out
// use structure header to decode the buf
// while not end of file {
// read(buf, sizeof(record) and print out
// }
}
Read them using the encoding/binary package that you can find at https://golang.org/pkg/encoding/binary/
You can use binary.Read to read into a Go struct. You'll need to account for the padding the C version will add. In your example, default padding would put 2 pad bytes before createTime.
Related
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.
package main
/*
#include <inttypes.h>
#pragma pack(1)
typedef struct _sss {
uint64_t some; // ok
uint32_t wow; // ok
uint64_t some2; // not found
uint64_t some3; // not found
uint64_t some4; // not found
uint32_t some5; // ok
} Type;
#pragma pack()
*/
import "C"
import (
"fmt"
)
func main() {
s := C.Type{}
s.some = 10;
s.wow = 10;
s.some2 = 10;
s.some5 = 10;
fmt.Println("Hello, playground")
}
I declared struct named C.Type.
If pragma pack(1) and 64 bits members are used together.
╭─dire#dire-81w4 ~/workspace
╰─go run test.go 2 ↵
# command-line-arguments
./test.go:25:6: s.some2 undefined (type _Ctype_struct__sss has no field or method some2)
Compilation is possible by removing pragma pack(1).
Go 1.15 version is being used. Do you know why?
I found answer about my question.
If the fields of a C struct are aligned such that they can't be represented by a Go struct, then they cannot be accessed directly from cgo. You will have to write functions in C to read and write those fields.
I nowdays am too busy to comment. It's late, but it's answered.
I'm using syscall.Syscall(...) to call a C method in a dll.
This is the C method signature:
SENSEI_API HSENSEI SENSEI_open(const char* sensigrafo, const char* options, SENSEI_ERR* se);
This is the SENSEI_ERR struct:
typedef struct
{
int code;
char* error_string;
} SENSEI_ERR;
In my GO program I declared a struct:
type senseiErr struct {
code int
error_string *byte
}
And tried to call the method:
var nargs uintptr = 3
var err senseiErr
ret, _, callErr := syscall.Syscall(uintptr(senseiOpen),
nargs,
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr("en"))),
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(""))),
uintptr(unsafe.Pointer(&err)),
)
As you may have guessed, the SENSEI_open method fill the SENSEI_ERR argument with the code and the text of the error.
Now I need to read the content of that error.
err.code actually has the correct value.
About err.error_string I don't know. I'm new to GO and i have some questions:
Since the C struct has the field char* error_string, is error_string *byte in my GO struct correct?
Should I use []byte or something else?
How do I read the content of the error_string field?
fmt.Println(err.error_string) prints the memory address
fmt.Println(*err.error_string) prints always "101"
1) I doubt that cost char* meant to be UTF16 encoded. So all what you need is just getting raw data:
sensigrafo := "en\000" // \000 = 0 = null termination, \0 does not valid
options := "\000"
...
uintptr(*(*unsafe.Pointer)(unsafe.Pointer(&sensigrafo))
uintptr(*(*unsafe.Pointer)(unsafe.Pointer(&options))
// *(*unsafe.Pointer) are accessing the first field of string header:
type string struct {
data *byte
len int
}
// same with slices
// but for them there's less ugly way:
sensigrafo := []byte("en\000")
options := []byte("\000")
uintptr(unsafe.Pointer(&sensigrafo[0]))
uintptr(unsafe.Pointer(&options[0]))
2) C's int and Golang's int might have different sizeof, so this requires cgo declaration (C.int) or manual matching with random selection (try also int32, int64 if you don't want to use cgo)
type senseiErr struct {
code C.int /* Golang's int32/int64 */
error_string *byte // pointer types are same as C's void* or Golang's unsafe.Pointer
}
Wrong offset might cause error_string be empty or point to random addr.
3) To read content you have to use same methods as C does (read data until null terminated byte, considering that *byte points to first element of string), but I propose to use already implemented runtime functions:
//go:linkname gostringn runtime.gostringn
func gostringn(p *byte, l int) string
//go:linkname findnull runtime.findnull
//go:nosplit
func findnull(s *byte) int
...
error_string := gostringn(err.error_string, findnull(err.error_string))
// or cgo one:
type senseiErr struct {
code C.int
error_string *C.char
}
...
error_string := C.GoString(err.error_string)
This is a C struct that I want to port into Go struct:
struct InputBuffer_t {
char* buffer;
size_t buffer_length;
ssize_t input_length;
};
But there is a way to declare buffer_length variable without using C.size_t cgo pointer in Go.
This is a concern for portability. If I write the Go struct in this way, is it will be portable?
type InputBuffer struct {
Buffer string
BufferLength uint32
InputLength uint32
};
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.