goffi

module
v0.3.7 Latest Latest
Warning

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

Go to latest
Published: Jan 3, 2026 License: MIT

README ΒΆ

goffi - Zero-CGO FFI for Go

CI Coverage Go Report Card GitHub release Go version License Go Reference

Pure Go Foreign Function Interface (FFI) for calling C libraries without CGO. Primary use case: WebGPU bindings for GPU computing in pure Go.

// Call C functions directly from Go - no CGO required!
handle, _ := ffi.LoadLibrary("wgpu_native.dll")
wgpuCreateInstance := ffi.GetSymbol(handle, "wgpuCreateInstance")
ffi.CallFunction(&cif, wgpuCreateInstance, &result, args)

✨ Features

  • 🚫 Zero CGO - Pure Go, no C compiler needed
  • ⚑ Fast - ~100ns FFI overhead (benchmarks)
  • 🌐 Cross-platform - Windows + Linux + macOS (AMD64 + ARM64)
  • πŸ”„ Callbacks - C-to-Go function calls for async APIs (v0.2.0)
  • πŸ”’ Type-safe - Runtime type validation with detailed errors
  • πŸ“¦ Production-ready - 87% test coverage, comprehensive error handling
  • 🎯 WebGPU-optimized - Designed for wgpu-native bindings

πŸš€ Quick Start

Installation
go get github.com/go-webgpu/goffi
Basic Example
package main

import (
	"fmt"
	"runtime"
	"unsafe"

	"github.com/go-webgpu/goffi/ffi"
	"github.com/go-webgpu/goffi/types"
)

func main() {
	// Load standard library
	var libName, funcName string
	switch runtime.GOOS {
	case "linux":
		libName, funcName = "libc.so.6", "strlen"
	case "windows":
		libName, funcName = "msvcrt.dll", "strlen"
	default:
		panic("Unsupported OS")
	}

	handle, err := ffi.LoadLibrary(libName)
	if err != nil {
		panic(err)
	}
	defer ffi.FreeLibrary(handle)

	strlen, err := ffi.GetSymbol(handle, funcName)
	if err != nil {
		panic(err)
	}

	// Prepare call interface (reuse for multiple calls!)
	cif := &types.CallInterface{}
	err = ffi.PrepareCallInterface(
		cif,
		types.DefaultCall,                // Auto-detects platform
		types.UInt64TypeDescriptor,       // size_t return
		[]*types.TypeDescriptor{types.PointerTypeDescriptor}, // const char* arg
	)
	if err != nil {
		panic(err)
	}

	// Call strlen("Hello, goffi!")
	testStr := "Hello, goffi!\x00"
	strPtr := unsafe.Pointer(unsafe.StringData(testStr))
	var length uint64

	err = ffi.CallFunction(cif, strlen, unsafe.Pointer(&length), []unsafe.Pointer{strPtr})
	if err != nil {
		panic(err)
	}

	fmt.Printf("strlen(%q) = %d\n", testStr[:len(testStr)-1], length)
	// Output: strlen("Hello, goffi!") = 13
}

πŸ“Š Performance

FFI Overhead: ~88-114 ns/op (Windows AMD64, Intel i7-1255U)

Benchmark Time vs Direct Go
Empty function 88.09 ns ~400x slower
Integer arg 113.9 ns ~500x slower
String processing 97.81 ns ~450x slower

Verdict: βœ… Excellent for WebGPU (GPU calls are 1-100Β΅s, FFI is 0.1Β΅s = 0.1-10% overhead)

See docs/PERFORMANCE.md for comprehensive analysis, optimization strategies, and when NOT to use goffi.


⚠️ Known Limitations

Critical

Windows: C++ exceptions crash the program (Go issue #12516)

  • Libraries using C++ exceptions (including Rust with panic=unwind) will crash
  • This is a Go runtime limitation, not goffi-specific - affects CGO too
  • Workaround: Build native libraries with panic=abort or use Linux/macOS
  • Fix planned: Go 1.26 (#58542)

Variadic functions NOT supported (printf, sprintf, etc.)

  • Workaround: Use non-variadic wrappers (puts instead of printf)
  • Planned: v0.5.0 (Q2 2025)

Struct packing follows System V ABI only

  • Windows #pragma pack directives NOT honored
  • Workaround: Manually specify Size/Alignment in TypeDescriptor
  • Planned: v0.5.0 (platform-specific rules)
Architectural
  • Composite types (structs) require manual initialization
  • Cannot interrupt C functions mid-execution (use CallFunctionContext for timeouts)
  • ARM64 - Tested on Apple Silicon (M3 Pro), Linux ARM64 cross-compile verified
  • No bitfields in structs

See CHANGELOG.md for full details.


πŸ“– Documentation


πŸ› οΈ Advanced Usage

Typed Error Handling
import "errors"

handle, err := ffi.LoadLibrary("nonexistent.dll")
if err != nil {
	var libErr *ffi.LibraryError
	if errors.As(err, &libErr) {
		fmt.Printf("Failed to %s %q: %v\n", libErr.Operation, libErr.Name, libErr.Err)
		// Output: Failed to load "nonexistent.dll": The specified module could not be found
	}
}

goffi provides 5 typed error types for precise error handling:

  • InvalidCallInterfaceError - CIF preparation failures
  • LibraryError - Library loading/symbol lookup
  • CallingConventionError - Unsupported calling conventions
  • TypeValidationError - Type descriptor validation
  • UnsupportedPlatformError - Platform not supported
Context Support (Timeouts/Cancellation)
import (
	"context"
	"time"
)

ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
defer cancel()

err := ffi.CallFunctionContext(ctx, cif, funcPtr, &result, args)
if err == context.DeadlineExceeded {
	fmt.Println("Function call timed out!")
}
Cross-Platform Calling Conventions
// Auto-detect platform (recommended)
convention := types.DefaultCall

// Or explicit:
switch runtime.GOOS {
case "windows":
	convention = types.WindowsCallingConvention // Win64 ABI
case "linux", "freebsd":
	convention = types.UnixCallingConvention   // System V AMD64
}

ffi.PrepareCallInterface(cif, convention, returnType, argTypes)

πŸ—οΈ Architecture

goffi uses a 4-layer architecture for safe Go→C transitions:

Go Code (User Application)
    ↓ ffi.CallFunction()
runtime.cgocall (Go Runtime)
    ↓ System stack switch + GC coordination
Assembly Wrapper (Platform-specific)
    ↓ Register loading (RDI/RCX + XMM0-7)
JMP Stub (Function pointer indirection)
    ↓ Indirect jump
C Function (External Library)

Key technologies:

  • runtime.cgocall for GC-safe stack switching
  • Hand-written assembly for System V AMD64 (Linux) and Win64 (Windows) ABIs
  • Runtime type validation (no codegen/reflection)

See docs/dev/TECHNICAL_ARCHITECTURE.md for deep dive (internal docs).


πŸ—ΊοΈ Roadmap

v0.2.0 - Callback Support βœ… RELEASED!
  • Callback API (NewCallback) for C-to-Go function calls
  • 2000-entry trampoline table for async operations
  • WebGPU async APIs now fully supported
v0.3.x - ARM64 Support βœ… RELEASED!
  • ARM64 support (Linux + macOS AAPCS64 ABI)
  • AAPCS64 calling convention with X0-X7, D0-D7 registers
  • HFA (Homogeneous Floating-point Aggregate) returns
  • Nested struct and mixed int/float struct support
  • 2000-entry callback trampolines for ARM64
  • Tested on Apple Silicon M3 Pro
v0.5.0 - Usability + Variadic (Q2 2025)
  • Builder pattern API: lib.Call("func").Arg(...).ReturnInt()
  • Variadic function support (printf, sprintf, etc.)
  • Platform-specific struct alignment (Windows #pragma pack)
  • Windows ARM64 (experimental)
v1.0.0 - Stable Release (Q1 2026)
  • API stability guarantee (SemVer 2.0)
  • Security audit
  • Reference implementations (WebGPU, Vulkan, SQLite bindings)
  • Performance benchmarks vs CGO/purego published

See CHANGELOG.md for detailed roadmap.


πŸ§ͺ Testing

# Run all tests
go test ./...

# Run with coverage
go test -cover ./...
# Current coverage: 89.1%

# Run benchmarks
go test -bench=. -benchmem ./ffi

# Platform-specific tests
go test -v ./ffi  # Auto-detects Windows/Linux

🌍 Platform Support

Platform Architecture Status Notes
Windows amd64 βœ… v0.1.0 Win64 ABI, full support
Linux amd64 βœ… v0.1.0 System V ABI, full support
macOS amd64 βœ… v0.1.1 System V ABI, full support
FreeBSD amd64 βœ… v0.1.0 System V ABI (untested)
Linux arm64 βœ… v0.3.0 AAPCS64 ABI, cross-compile verified
macOS arm64 βœ… v0.3.7 AAPCS64 ABI, tested on M3 Pro

🀝 Contributing

Contributions welcome! See CONTRIBUTING.md for guidelines.

Quick checklist:

  1. Fork the repository
  2. Create feature branch (git checkout -b feat/amazing-feature)
  3. Write tests (maintain 80%+ coverage)
  4. Run linters (golangci-lint run)
  5. Commit with conventional commits (feat:, fix:, docs:)
  6. Open pull request

πŸ“œ License

MIT License - see LICENSE for details.


πŸ™ Acknowledgments

  • purego - Inspiration for CGO-free FFI approach
  • libffi - Reference for FFI architecture patterns
  • Go runtime - runtime.cgocall for safe stack switching


Made with ❀️ for GPU computing in pure Go

Last updated: 2026-01-03 | goffi v0.3.7

Directories ΒΆ

Path Synopsis
Package ffi provides callback support for Foreign Function Interface (Unix version).
Package ffi provides callback support for Foreign Function Interface (Unix version).
internal
dl
syscall
System V AMD64 ABI syscall implementation (Linux, macOS, FreeBSD, etc.) This calling convention is IDENTICAL on all Unix-like systems.
System V AMD64 ABI syscall implementation (Linux, macOS, FreeBSD, etc.) This calling convention is IDENTICAL on all Unix-like systems.

Jump to

Keyboard shortcuts

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