JupiterRider / ffi

A purego binding for libffi.
MIT License
54 stars 2 forks source link
freebsd go golang libffi linux macos windows

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

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.