JupiterRider / ffi

A purego binding for libffi.
MIT License
54 stars 2 forks source link

Struct send #1

Closed ring-c closed 6 months ago

ring-c commented 6 months ago

As a continue of purego/issues/236

When it comes to string, it really is a *byte in golang.

It does not work, even for struct with 1 string param. Getting SIGSEGV: segmentation violation

In your example you are using pointer to result of syscall.ByteSliceFromString, so *[]byte.

JupiterRider commented 6 months ago

Hey ring-c,

I updated the example to be more clear. It uses unix.BytePtrFromString now. You cannot use slices as struct fields. It has to be a pointer.

JupiterRider commented 6 months ago

@ring-c Here is another example, which works for me:

.
├── go.mod
├── go.sum
├── hello.c
├── libhello.so
└── main.go

hello.c

#include <stdio.h>

typedef struct {
    const char *question;
    const char *answer;
} Dialog;

Dialog ask(Dialog in)
{
    if (in.question != NULL)
    {
        puts(in.question);
    }

    Dialog out = { 0 };
    out.question = in.question;
    out.answer = "I am not your AI assistant!";
    return out;
}

main.go

package main

import (
    "fmt"
    "unsafe"

    "github.com/ebitengine/purego"
    "github.com/jupiterrider/ffi"
    "golang.org/x/sys/unix"
)

type Dialog struct {
    Question, Answer *byte
}

func main() {
    hello, err := purego.Dlopen("./libhello.so", purego.RTLD_LAZY)
    if err != nil {
        panic(err)
    }

    ask, err := purego.Dlsym(hello, "ask")
    if err != nil {
        panic(err)
    }

    var typDialog ffi.Type
    typDialog.Type = ffi.Struct
    typDialog.Elements = &[]*ffi.Type{&ffi.TypePointer, &ffi.TypePointer, nil}[0]

    var cif ffi.Cif
    ffi.PrepCif(&cif, ffi.DefaultAbi, 1, &typDialog, &typDialog)

    var in, out Dialog
    in.Question, _ = unix.BytePtrFromString("How will the weather be?")

    ffi.Call(&cif, ask, unsafe.Pointer(&out), unsafe.Pointer(&in))

    fmt.Println(unix.BytePtrToString(out.Answer))
}
[jupiterrider@pc42 demo]$ gcc -shared -o libhello.so -fPIC hello.c
[jupiterrider@pc42 demo]$ go run .
How will the weather be?
I am not your AI assistant!
[jupiterrider@pc42 demo]$
ring-c commented 6 months ago

@JupiterRider thanks, that helped. I did two major mistakes in my code: did not append nil in my ffi.Type def, and tried to call a function with accepting a pointer to a struct (as far as i understand C).

Is there a way to call such a function? Some thing like that:

void myFunk(myStruct* args) 
JupiterRider commented 6 months ago

If the c function wants a pointer, then you can just use the ffi.TypePointer. Here is the the example:

#include <stdio.h>

typedef struct {
    const char *question;
    const char *answer;
} Dialog;

Dialog* ask(Dialog* in)
{
    if (in->question != NULL)
    {
        puts(in->question);
    }

    Dialog out = { 0 };
    out.question = in->question;
    out.answer = "I am not your AI assistant!";
    Dialog *r = &out;
    return r;
}
package main

import (
    "fmt"
    "unsafe"

    "github.com/ebitengine/purego"
    "github.com/jupiterrider/ffi"
    "golang.org/x/sys/unix"
)

type Dialog struct {
    Question, Answer *byte
}

func main() {
    hello, err := purego.Dlopen("./libhello.so", purego.RTLD_LAZY)
    if err != nil {
        panic(err)
    }

    ask, err := purego.Dlsym(hello, "ask")
    if err != nil {
        panic(err)
    }

    var cif ffi.Cif
    ffi.PrepCif(&cif, ffi.DefaultAbi, 1, &ffi.TypePointer, &ffi.TypePointer)

    in := new(Dialog)
    in.Question, _ = unix.BytePtrFromString("How will the weather be?")

    var out *Dialog
    ffi.Call(&cif, ask, unsafe.Pointer(&out), unsafe.Pointer(&in))

    fmt.Println(unix.BytePtrToString(out.Answer))
}

You can also use plain purego:

package main

import (
    "fmt"

    "github.com/ebitengine/purego"
    "golang.org/x/sys/unix"
)

type Dialog struct {
    Question, Answer *byte
}

func main() {
    hello, err := purego.Dlopen("./libhello.so", purego.RTLD_LAZY)
    if err != nil {
        panic(err)
    }

    var ask func(*Dialog) *Dialog
    purego.RegisterLibFunc(&ask, hello, "ask")

    in := new(Dialog)
    in.Question, _ = unix.BytePtrFromString("How will the weather be?")
    out := ask(in)
    fmt.Println(unix.BytePtrToString(out.Answer))
}

purego can work with pointers to structs, but not with actual structs.

ring-c commented 6 months ago

@JupiterRider thanks, this works.