ffi

package module
v0.1.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 28, 2024 License: MIT Imports: 2 Imported by: 3

README

ffi

Go Reference

A purego binding for libffi.

Purpose

You can use purego to call C code without cgo. ffi provides extra functionality (e.g. passing and returning structs by value).

Requirements

OS/Architecture
  • darwin/amd64
  • darwin/arm64
  • freebsd/amd64
  • freebsd/arm64
  • linux/amd64
  • linux/arm64
  • windows/amd64
  • windows/arm64
Software

libffi is preinstalled on most distributions, because it also is a dependency of Python and Ruby. If not, you can install it explicitly:

Arch Linux
sudo pacman -S libffi
Debian 12, Ubuntu 22.04, Ubuntu 24.04
sudo apt install libffi8
Debian 11, Ubuntu 20.04
sudo apt install libffi7
FreeBSD
pkg install libffi

Note: Use this -gcflags="github.com/ebitengine/purego/internal/fakecgo=-std" build flag when cross compiling or having CGO_ENABLED set to 0 (FreeBSD only).

Windows

You need a libffi-8.dll next to the executable/root folder of your project or inside C:\Windows\System32. If you don't want to build libffi from source, you can find this dll for example inside the Windows embeddable package of Python.

macOS

You can use Homebrew to install libffi:

brew install libffi

Note: If dlopen can't find the libffi.8.dylib file, you can try setting this environment variable:

export DYLD_FALLBACK_LIBRARY_PATH=$DYLD_FALLBACK_LIBRARY_PATH:/opt/homebrew/opt/libffi/lib

Examples

In this example we use the puts function inside the standard C library to print "Hello World!" to the console:

int puts(const char *s);
package main

import (
	"unsafe"

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

func main() {
	// open the C library
	libm, err := purego.Dlopen("libc.so.6", purego.RTLD_LAZY)
	if err != nil {
		panic(err)
	}

	// get the address of puts
	puts, err := purego.Dlsym(libm, "puts")
	if err != nil {
		panic(err)
	}

	// describe the function's signature
	var cif ffi.Cif
	if status := ffi.PrepCif(&cif, ffi.DefaultAbi, 1, &ffi.TypeSint32, &ffi.TypePointer); status != ffi.OK {
		panic(status)
	}

	// convert the go string into a pointer
	text, _ := unix.BytePtrFromString("Hello World!")

	// call the puts function
	var result ffi.Arg
	ffi.Call(&cif, puts, unsafe.Pointer(&result), unsafe.Pointer(&text))
}

You can find more examples inside the examples folder of this repository.

Documentation

Index

Constants

View Source
const (
	Void = iota
	Int
	Float
	Double
	Longdouble
	Uint8
	Sint8
	Uint16
	Sint16
	Uint32
	Sint32
	Uint64
	Sint64
	Struct
	Pointer
	Complex
)

These constants are used for the Type field of Type.

Variables

View Source
var (
	TypeVoid              = Type{1, 1, Void, nil}
	TypeUint8             = Type{1, 1, Uint8, nil}
	TypeSint8             = Type{1, 1, Sint8, nil}
	TypeUint16            = Type{2, 2, Uint16, nil}
	TypeSint16            = Type{2, 2, Sint16, nil}
	TypeUint32            = Type{4, 4, Uint32, nil}
	TypeSint32            = Type{4, 4, Sint32, nil}
	TypeUint64            = Type{8, 8, Uint64, nil}
	TypeSint64            = Type{8, 8, Sint64, nil}
	TypeFloat             = Type{4, 4, Float, nil}
	TypeDouble            = Type{8, 8, Double, nil}
	TypePointer           = Type{8, 8, Pointer, nil}
	TypeLongdouble        = Type{16, 16, Longdouble, nil}
	TypeComplexFloat      = Type{8, 4, Complex, &[]*Type{&TypeFloat, nil}[0]}
	TypeComplexDouble     = Type{16, 8, Complex, &[]*Type{&TypeDouble, nil}[0]}
	TypeComplexLongdouble = Type{32, 16, Complex, &[]*Type{&TypeLongdouble, nil}[0]}
)

Predefined variables for primitive data types.

For bool you can use TypeUint8 and check its value:

byte(returnValue) != 0

A string is just an array of characters in C (often seen as char * or const char *). Use TypePointer for that.

To convert strings between C and Go take a look at golang.org/x/sys/unix.BytePtrFromString and golang.org/x/sys/unix.BytePtrToString. The Windows counterparts are golang.org/x/sys/windows.BytePtrFromString and golang.org/x/sys/windows.BytePtrToString.

Slices are treated as pointers as well. You can use unsafe.Slice to convert a pointer into a slice.

Functions

func Call

func Call(cif *Cif, fn uintptr, rValue unsafe.Pointer, aValues ...unsafe.Pointer)

Call calls the function fn according to the description given in cif. cif must have already been prepared using PrepCif.

  • fn is the address of the desired function. Use purego.Dlsym to get one.
  • rValue is a pointer to a variable that will hold the result of the function call. Provide nil if the function has no return value. You cannot use integer types smaller than 8 bytes here (float32 and structs are not affected). Use Arg instead and typecast afterwards.
  • aValues are pointers to the argument values. Leave empty or provide nil if the function takes none.

Example:

int ilogb(double x);

var result ffi.Arg
x := 1.0
ffi.Call(&cif, ilogb, unsafe.Pointer(&result), unsafe.Pointer(&x))
fmt.Printf("%d\n", int32(result))

Types

type Abi

type Abi uint32
const (
	DefaultAbi Abi = 2
)

type Arg

type Arg uint64

Arg can be used as a return value for functions, which return integers smaller than 8 bytes.

See Call.

type Cif

type Cif struct {
	Abi      uint32
	NArgs    uint32
	ArgTypes **Type
	RType    *Type
	Bytes    uint32
	Flags    uint32
}

Cif stands for "Call InterFace". It describes the signature of a function.

Use PrepCif to initialize it.

type Status

type Status uint32
const (
	OK Status = iota
	BadTypedef
	BadAbi
	BadArgType
)

func PrepCif

func PrepCif(cif *Cif, abi Abi, nArgs uint32, rType *Type, aTypes ...*Type) Status

PrepCif initializes cif.

  • abi is the ABI to use. Normally DefaultAbi is what you want.
  • nArgs is the number of arguments. Use 0 if the function has none.
  • rType is the return type. Use TypeVoid if the function has none.
  • aTypes are the arguments. Leave empty or provide nil if the function has none.

The returned status code will be OK, if everything worked properly.

Example:

double cos(double x);

var cif ffi.Cif
status := ffi.PrepCif(&cif, ffi.DefaultAbi, 1, &ffi.TypeDouble, &ffi.TypeDouble)
if status != ffi.OK {
	panic(status)
}

func PrepCifVar

func PrepCifVar(cif *Cif, abi Abi, nFixedArgs, nTotalArgs uint32, rType *Type, aTypes ...*Type) Status

PrepCifVar initializes cif for a call to a variadic function.

In general its operation is the same as for PrepCif except that:

  • nFixedArgs is the number of fixed arguments, prior to any variadic arguments. It must be greater than zero.
  • nTotalArgs is the total number of arguments, including variadic and fixed arguments. aTypes must have this many elements.

This function will return BadArgType if any of the variable argument types is TypeFloat. Same goes for integer types smaller than 4 bytes. See issue 608.

Note that, different cif's must be prepped for calls to the same function when different numbers of arguments are passed.

Also note that a call to this function with nFixedArgs = nTotalArgs is NOT equivalent to a call to PrepCif.

Example:

int printf(const char *restrict format, ...);

var cif ffi.Cif
status := ffi.PrepCifVar(&cif, ffi.DefaultAbi, 1, 2, &ffi.TypeSint32, &ffi.TypePointer, &ffi.TypeDouble)
if status != ffi.OK {
	panic(status)
}

text, _ := unix.BytePtrFromString("Pi is %f\n")
pi := math.Pi
var nCharsPrinted int32
ffi.Call(&cif, printf, unsafe.Pointer(&nCharsPrinted), unsafe.Pointer(&text), unsafe.Pointer(&pi))

func (Status) String

func (s Status) String() string

type Type

type Type struct {
	Size      uint64 // Initialize to 0 (automatically set by libffi as needed).
	Alignment uint16 // Initialize to 0 (automatically set by libffi as needed).
	Type      uint16 // Use ffi.Struct for struct types.
	Elements  **Type // Pointer to the first element of a nil-terminated slice.
}

Type is used to describe the structure of a data type.

Example:

typedef struct Point {
	int x;
	int y;
} Point;

typePoint := ffi.Type{Type: ffi.Struct, Elements: &[]*ffi.Type{&ffi.TypeSint32, &ffi.TypeSint32, nil}[0]}

Primitive data types are already defined (e.g. TypeDouble for float64).

Directories

Path Synopsis
examples

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL