Skip to content

Commit

Permalink
Add Zig bindings.
Browse files Browse the repository at this point in the history
  • Loading branch information
alexrp committed Jul 16, 2024
1 parent 0494067 commit 20f91e0
Show file tree
Hide file tree
Showing 21 changed files with 708 additions and 63 deletions.
66 changes: 26 additions & 40 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
# Vezel libffi Fork

This is a friendly fork of [libffi](https://sourceware.org/libffi). The notable
change made in this fork is the addition of a [Zig](https://ziglang.org) build
script, making it easy to integrate libffi into Zig projects using the Zig
package manager. Additionally, to reduce the package download size, we have
removed a large number of files that are unnecessary when using libffi in a Zig
project. Importantly, **all library source code is identical to upstream**, so
in terms of API/ABI compatibility, using this fork is no different from linking
to a system libffi package.
changes made in this fork are the additions of a [Zig](https://ziglang.org)
build script, making it easy to integrate libffi into Zig projects using the Zig
package manager, and a set of idiomatic Zig bindings for libffi's main API.
Additionally, to reduce the package download size, we have removed a large
number of files that are unnecessary when using libffi in a Zig project.
Importantly, **all library source code is identical to upstream**, so in terms
of API/ABI compatibility, using this fork is no different from linking to a
system libffi package.

## Usage

Expand All @@ -24,65 +25,50 @@ zig fetch --save=ffi https://github.com/vezel-dev/libffi/archive/vX.Y.Z-B.tar.gz
zig fetch --save=ffi git+https://github.com/vezel-dev/libffi.git#vX.Y.Z-B
```

(You can find the latest libffi version on the
(You can find the latest version on the
[releases page](https://github.com/vezel-dev/libffi/releases).)

Then, in your `build.zig`:
Consume the library in your `build.zig`:

```zig
const ffi = b.dependency("ffi", .{});
exe.linkLibrary(ffi.artifact("ffi"));
const ffi = b.dependency("ffi", .{
.target = target,
.optimize = optimize,
});
exe.root_module.addImport("ffi", ffi.module("ffi"));
// Or, if you want to be able to integrate with a system package:
if (b.systemIntegrationOption("ffi", .{})) {
exe.linkSystemLibrary("ffi");
} else {
const ffi = b.dependency("ffi", .{
.target = target,
.optimize = optimize,
});
exe.linkLibrary(ffi.artifact("ffi"));
}
```

You can then use the C header in your Zig code:
You can now use the Zig bindings in your code:

```zig
const builtin = @import("builtin");
const std = @import("std");
const stdio = @cImport(@cInclude("stdio.h"));
const ffi = @cImport(@cInclude("ffi.h"));
const ffi = @import("ffi");
pub fn main() !void {
std.debug.print("Calling C puts() on {s}.\n", .{builtin.cpu.arch.genericName()});
var cif: ffi.ffi_cif = undefined;
var params = [_]?*ffi.ffi_type{
&ffi.ffi_type_pointer,
var func: ffi.Function = undefined;
var params = [_]*ffi.Type{
ffi.types.pointer,
};
switch (ffi.ffi_prep_cif(
&cif,
ffi.FFI_DEFAULT_ABI,
params.len,
&ffi.ffi_type_sint32,
params[0..params.len],
)) {
ffi.FFI_OK => {},
else => |e| return switch (e) {
ffi.FFI_BAD_TYPEDEF => error.BadTypeDefinition,
ffi.FFI_BAD_ABI => error.BadAbi,
ffi.FFI_BAD_ARGTYPE => error.BadArgumentType,
else => unreachable,
},
}
var result: ffi.ffi_arg = undefined;
var args = [params.len]?*anyopaque{
try func.prepare(ffi.Abi.default, params.len, ffi.types.sint32, params[0..params.len]);
var result: ffi.uarg = undefined;
var args = [params.len]*anyopaque{
@ptrCast(@constCast(&"Hello World")),
};
ffi.ffi_call(&cif, @ptrCast(&stdio.puts), &result, args[0..args.len]);
func.call(@ptrCast(&stdio.puts), &result, args[0..args.len]);
if (result == stdio.EOF)
return error.IOError;
Expand Down
43 changes: 20 additions & 23 deletions build.zig
Original file line number Diff line number Diff line change
@@ -1,29 +1,34 @@
const std = @import("std");

const name = "libffi";
const version = "3.4.6"; // TODO: https://github.com/ziglang/zig/issues/14531

pub fn build(b: *std.Build) anyerror!void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});

const check = b.step("check", "Run source code checks");
const fmt = b.step("fmt", "Fix source code formatting");
const check_tls = b.step("check", "Run source code checks");
const fmt_tls = b.step("fmt", "Fix source code formatting");

const fmt_paths = &[_][]const u8{
"build.zig",
"build.zig.zon",
};

check.dependOn(&b.addFmt(.{
check_tls.dependOn(&b.addFmt(.{
.paths = fmt_paths,
.check = true,
}).step);

fmt.dependOn(&b.addFmt(.{
fmt_tls.dependOn(&b.addFmt(.{
.paths = fmt_paths,
}).step);

_ = b.addModule("ffi", .{
.root_source_file = b.path(b.pathJoin(&.{ "lib", "ffi.zig" })),
.target = target,
.optimize = optimize,
});

const lib = b.addStaticLibrary(.{
.name = "ffi",
.target = target,
Expand Down Expand Up @@ -222,13 +227,6 @@ pub fn build(b: *std.Build) anyerror!void {
"v9.S",
};
},
.wasm32 => {
arch_name = "wasm32";
arch_target = "wasm32";
arch_sources = &.{
"ffi.c",
};
},
.xtensa => {
arch_name = "xtensa";
arch_target = "XTENSA";
Expand Down Expand Up @@ -258,7 +256,11 @@ pub fn build(b: *std.Build) anyerror!void {
.linux => t.cpu.arch.isPPC() or t.cpu.arch.isPPC64(),
else => false,
};
const long_double: LongDouble = if (t.cpu.arch.isMIPS() and (t.os.tag == .freebsd or t.os.tag == .linux or t.os.tag == .openbsd))
const long_double: enum {
false,
true,
mips64,
} = if (t.cpu.arch.isMIPS() and (t.os.tag == .freebsd or t.os.tag == .linux or t.os.tag == .openbsd))
.mips64
else if (long_double_variant or long_double_size > double_size)
.true
Expand Down Expand Up @@ -329,11 +331,11 @@ pub fn build(b: *std.Build) anyerror!void {
.HAVE_UNISTD_H = true,
.LIBFFI_GNU_SYMBOL_VERSIONING = null,
.LT_OBJDIR = null, // Not used.
.PACKAGE = name,
.PACKAGE = "libffi",
.PACKAGE_BUGREPORT = "http://github.com/libffi/libffi/issues",
.PACKAGE_NAME = name,
.PACKAGE_STRING = name ++ " " ++ version,
.PACKAGE_TARNAME = name,
.PACKAGE_NAME = "libffi",
.PACKAGE_STRING = "libffi " ++ version,
.PACKAGE_TARNAME = "libffi",
.PACKAGE_URL = "",
.PACKAGE_VERSION = version,
.SIZEOF_DOUBLE = double_size,
Expand All @@ -357,12 +359,7 @@ pub fn build(b: *std.Build) anyerror!void {
lib.addConfigHeader(h);
}

// libffi has historically put its header files directly in the include path, rather than a subdirectory.
lib.installConfigHeader(ffi_h);
lib.installHeader(b.path(b.pathJoin(&.{ "src", arch_name, "ffitarget.h" })), "ffitarget.h");
}

const LongDouble = enum {
false,
true,
mips64,
};
1 change: 1 addition & 0 deletions build.zig.zon
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"build.zig.zon",
"fficonfig.zig.h",
"include",
"lib",
"src",
},
.dependencies = .{},
Expand Down
43 changes: 43 additions & 0 deletions lib/aarch64.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// SPDX-License-Identifier: MIT

const builtin = @import("builtin");

const ffi = @import("ffi.zig");
const default = @import("default.zig");

pub const have_complex_type = builtin.os.tag != .windows;

const arg_longlong = builtin.cpu.arch == .aarch64_32 or builtin.os.tag == .windows;

pub const uarg = if (arg_longlong) c_ulonglong else c_ulong;
pub const sarg = if (arg_longlong) c_longlong else c_long;

pub const Abi = enum(i32) {
sysv = 1,
win64 = 2,
_,

pub const default: Abi = if (builtin.os.tag == .windows) .win64 else .sysv;
};

pub const Function = if (builtin.os.tag.isDarwin()) extern struct {
abi: Abi,
nargs: c_uint,
arg_types: ?[*]*ffi.Type,
rtype: *ffi.Type,
bytes: c_uint,
flags: c_uint,
aarch64_nfixedargs: c_uint,

pub usingnamespace @import("function.zig");
} else if (builtin.os.tag == .windows) extern struct {
abi: Abi,
nargs: c_uint,
arg_types: ?[*]*ffi.Type,
rtype: *ffi.Type,
bytes: c_uint,
flags: c_uint,
is_variadic: c_uint,

pub usingnamespace @import("function.zig");
} else default.Function(Abi);
12 changes: 12 additions & 0 deletions lib/arc.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// SPDX-License-Identifier: MIT

const default = @import("default.zig");

pub const Abi = enum(i32) {
arcompact = 1,
_,

pub const default: Abi = .arcompact;
};

pub const Function = default.Function(Abi);
30 changes: 30 additions & 0 deletions lib/arm.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// SPDX-License-Identifier: MIT

const builtin = @import("builtin");

const ffi = @import("ffi.zig");

pub const have_complex_type = builtin.os.tag != .windows;

pub const Abi = enum(i32) {
sysv = 1,
vfp = 2,
_,

pub const default: Abi = if (builtin.abi.floatAbi() != .soft or builtin.os.tag == .windows) .vfp else .sysv;
};

pub const Function = extern struct {
abi: Abi,
nargs: c_uint,
arg_types: ?[*]*ffi.Type,
rtype: *ffi.Type,
bytes: c_uint,
flags: c_uint,
vfp_used: c_int,
vfp_reg_free: c_ushort,
vfp_nargs: c_ushort,
vfp_args: [16]i8,

pub usingnamespace @import("function.zig");
};
18 changes: 18 additions & 0 deletions lib/avr32.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// SPDX-License-Identifier: MIT

const ffi = @import("ffi.zig");
const default = @import("default.zig");

pub const Abi = default.Abi;

pub const Function = extern struct {
abi: Abi,
nargs: c_uint,
arg_types: ?[*]*ffi.Type,
rtype: *ffi.Type,
bytes: c_uint,
flags: c_uint,
rstruct_flag: c_uint,

pub usingnamespace @import("function.zig");
};
7 changes: 7 additions & 0 deletions lib/csky.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// SPDX-License-Identifier: MIT

const default = @import("default.zig");

pub const Abi = default.Abi;

pub const Function = default.Function(Abi);
23 changes: 23 additions & 0 deletions lib/default.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// SPDX-License-Identifier: MIT

const ffi = @import("ffi.zig");

pub const Abi = enum(i32) {
sysv = 1,
_,

pub const default: Abi = .sysv;
};

pub fn Function(TAbi: type) type {
return extern struct {
abi: TAbi,
nargs: c_uint,
arg_types: ?[*]*ffi.Type,
rtype: *ffi.Type,
bytes: c_uint,
flags: c_uint,

pub usingnamespace @import("function.zig");
};
}
Loading

0 comments on commit 20f91e0

Please sign in to comment.