/ffi

A purego binding for libffi.

Primary LanguageGoMIT LicenseMIT

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.