Skip to content
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
320a085
updated for zig-master(0.15.0-dev.646+ef35c3d5f)
IamSanjid May 28, 2025
57dee56
fix some compilation errors
IamSanjid May 28, 2025
8d79486
cs_opt_value zig enum
IamSanjid May 28, 2025
f55459d
cs_opt_value zig enum exposed
IamSanjid May 28, 2025
b5a3769
added more enum variants
IamSanjid May 28, 2025
36ecfb5
expose capstone-c
IamSanjid May 28, 2025
3e0f5b8
remove unnecessary enums
IamSanjid May 28, 2025
a704610
expose capstone-c
IamSanjid May 28, 2025
93044d1
expose capstone-c properly
IamSanjid May 28, 2025
6f3fb09
expose Insn and Detail
IamSanjid May 28, 2025
13641db
Expose Detail, Arch, Insn
IamSanjid May 28, 2025
1bc2966
remove unnecessary expose
IamSanjid May 28, 2025
74352fd
fix naming enums.OptionsType
IamSanjid May 28, 2025
2bb1a02
Iter with reset
IamSanjid May 29, 2025
fd7c930
Change, restored some stuff, support for zig 0.14.1
IamSanjid May 29, 2025
1934069
using capstone pre-release 5.0.1-2 and some minor changes
IamSanjid May 30, 2025
3746dd4
Update build.zig.zon
IamSanjid May 30, 2025
2e9786e
changed [*]insn.Insn to *insn.Insn where it's appropriate
IamSanjid May 30, 2025
767e955
expose arch
IamSanjid May 30, 2025
dbde91c
some cleanups and changes to adapt current Iter/Insn alloc impl
IamSanjid May 30, 2025
facb0c4
keep the global function names close to as original capstone functions
IamSanjid May 30, 2025
d1649ef
Expose all the things of arch
IamSanjid May 31, 2025
42c1072
IterUnmanaged, createInsn, dupeInsn, destroyInsn
IamSanjid May 31, 2025
ef38def
update ci to 0.14.1
SoraTenshi Jun 1, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,12 @@ pub fn build(b: *std.Build) void {

compiled_capstone.getEmittedIncludeTree().addStepDependencies(&compiled_capstone.step);
const capstone_c = b.addTranslateC(.{
.root_source_file = compiled_capstone.getEmittedIncludeTree().path(b, "capstone/capstone.h"),
.root_source_file = compiled_capstone.getEmittedIncludeTree().path(b, "capstone.h"),
.target = target,
.optimize = optimize,
.link_libc = true,
});
const capstone_c_mod = capstone_c.createModule();

const mod = b.addModule("capstone-bindings-zig", .{
.root_source_file = b.path("capstone.zig"),
Expand All @@ -25,7 +26,7 @@ pub fn build(b: *std.Build) void {
.imports = &.{
.{
.name = "capstone-c",
.module = capstone_c.createModule(),
.module = capstone_c_mod,
},
},
});
Expand All @@ -34,13 +35,16 @@ pub fn build(b: *std.Build) void {
mod.addIncludePath(compiled_capstone.getEmittedIncludeTree());

const mod_test = b.addTest(.{
.root_source_file = mod.root_source_file.?,
.root_source_file = b.path("src/tests.zig"),
.target = target,
.optimize = optimize,
});
mod_test.step.dependOn(&compiled_capstone.step);

mod_test.root_module.addImport("capstone-c", capstone_c.createModule());
mod_test.root_module.addImport("capstone-c", capstone_c_mod);
mod_test.addLibraryPath(compiled_capstone.getEmittedBin().dirname());
mod_test.linkLibrary(capstone.artifact("capstone"));
mod_test.addIncludePath(compiled_capstone.getEmittedIncludeTree());

const run_lib_tests = b.addRunArtifact(mod_test);
const test_step = b.step("test", "Run the library tests");
Expand Down
7 changes: 4 additions & 3 deletions build.zig.zon
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
.{
.name = "capstone-bindings-zig",
.name = .capstone_bindings_zig,
.version = "5.0.1",
.fingerprint = 0x52b01062163ea867,
.minimum_zig_version = "0.14.0",

.dependencies = .{
.capstone = .{
.url = "git+https://github.com/allyourcodebase/capstone.git#5.0.1-1",
.hash = "1220f284ea55271b5b68edb4471d10c622e5d5c220dd342c6e96ff50d3b801f46486",
.url = "git+https://github.com/allyourcodebase/capstone.git#5.0.1-2",
.hash = "capstone-5.0.1-tmNUgTRHAAC1nFVy0colCWwUSRm05SWdD-4kU5MK4ZpH",
},
},

Expand Down
33 changes: 32 additions & 1 deletion capstone.zig
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
const impl = @import("src/impl.zig");
pub const Iter = impl.Iter;
pub const IterUnmanaged = impl.IterUnmanaged;
pub const IterManaged = impl.IterManaged;
pub const Handle = impl.Handle;
pub const Regs = impl.Regs;
pub const Detail = impl.insn.Detail;
pub const Insn = impl.insn.Insn;
pub const version = impl.version;
pub const support = impl.support;
pub const open = impl.open;
Expand All @@ -11,6 +15,9 @@ pub const strerror = impl.strerror;
pub const disasm = impl.disasm;
pub const free = impl.free;
pub const malloc = impl.malloc;
pub const createInsn = impl.createInsn;
pub const dupeInsn = impl.dupeInsn;
pub const destroyInsn = impl.destroyInsn;
pub const disasmIterManaged = impl.disasmIterManaged;
pub const disasmIter = impl.disasmIter;
pub const regName = impl.regName;
Expand Down Expand Up @@ -42,3 +49,27 @@ const enums = @import("src/enums.zig");
pub const Arch = enums.Arch;
pub const Mode = enums.Mode;
pub const Type = enums.Type;

pub const ManagedHandle = @import("src/ManagedHandle.zig");

const arch = @import("src/arch/arch.zig");
pub const x86 = arch.x86;
pub const arm64 = arch.arm64;
pub const arm = arch.arm;
pub const m68k = arch.m68k;
pub const mips = arch.mips;
pub const ppc = arch.ppc;
pub const sparc = arch.sparc;
pub const sysz = arch.sysz;
pub const xcore = arch.xcore;
pub const tms320c64x = arch.tms320c64x;
pub const m680x = arch.m680x;
pub const evm = arch.evm;
pub const mos65xx = arch.mos65xx;
pub const wasm = arch.wasm;
pub const bpf = arch.bpf;
pub const riscv = arch.riscv;
pub const sh = arch.sh;
pub const tricore = arch.tricore;

pub const c = @import("capstone-c");
222 changes: 222 additions & 0 deletions src/ManagedHandle.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
const builtin = @import("builtin");
const std = @import("std");

const cs = @import("capstone-c");

const err = @import("error.zig");
const insn = @import("insn.zig");
const enums = @import("enums.zig");
const impl = @import("impl.zig");

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

const Allocator = std.mem.Allocator;

// To avoid heap allocations...
const tmp_storage = if (builtin.single_threaded)
struct {
var detail: insn.Detail = std.mem.zeroes(insn.Detail);
var ins: insn.Insn = std.mem.zeroes(insn.Insn);
fn getIns() *insn.Insn {
ins.detail = &detail;
return &ins;
}
}
else
struct {
threadlocal var detail: insn.Detail = std.mem.zeroes(insn.Detail);
threadlocal var ins: insn.Insn = std.mem.zeroes(insn.Insn);
fn getIns() *insn.Insn {
ins.detail = &detail;
return &ins;
}
};

const Self = @This();

pub const Options = struct {
/// Assembly output syntax
syntax: enums.Syntax = .DEFAULT,
/// Break down instruction structure into details
detail: bool = false,
/// Skip data when disassembling. Then engine is in SKIPDATA mode.
skipdata: bool = false,
/// Setup user-defined function for SKIPDATA option
/// The pointers must be valid till the handle is closed.
skipdata_setup: ?SkipDataOption = null,
/// Customize instruction mnemonic
/// Must be valid till the handle is closed if set.
mnemonic: []const MnemonicOption = &.{},
/// print immediate operands in unsigned form
unsigned: bool = false,
/// ARM, prints branch immediates without offset.
no_branch_offset: bool = false,

// cs_opt_mnem
pub const MnemonicOption = extern struct {
id: c_uint,
mnemonic: [*:0]const u8,
};
// cs_skipdata
pub const SkipDataOption = extern struct {
mnemonic: [*:0]const u8,
callback: ?*const fn ([*c]const u8, usize, usize, ?*anyopaque) callconv(.c) usize,
user_data: ?*anyopaque = null,
};
};

native: impl.Handle,
detail_on: bool,

pub fn init(arch: enums.Arch, mode: enums.Mode, options: Options) !Self {
const handle = try impl.open(arch, mode);

if (options.syntax != .DEFAULT) {
try impl.option(handle, .SYNTAX, @intFromEnum(options.syntax));
}

if (options.detail) {
try impl.option(handle, .DETAIL, cs.CS_OPT_ON);
}

if (options.skipdata) {
try impl.option(handle, .SKIPDATA, cs.CS_OPT_ON);
}

if (options.skipdata_setup) |setup| {
// cs_option copies the values.
try impl.option(handle, .SKIPDATA_SETUP, @intFromPtr(&setup));
}

if (options.unsigned) {
try impl.option(handle, .UNSIGNED, cs.CS_OPT_ON);
}

if (options.no_branch_offset) {
try impl.option(handle, .NO_BRANCH_OFFSET, cs.CS_OPT_ON);
}

if (options.mnemonic.len > 0) {
try impl.option(handle, .MNEMONIC, @intFromPtr(options.mnemonic.ptr));
}

return Self{ .native = handle, .detail_on = options.detail };
}

/// Allocates using the cs_malloc function or the user defined one which is set through cs_option/setup.initCapstone(Manually).
/// Should be freed using cs_free, capstone provided free function or the user defined one if set through option.
pub fn disasm(self: Self, code: []const u8, address: usize, count: usize) err.CapstoneError![]insn.Insn {
return impl.disasm(self.native, code, address, count);
}

/// Copies using zig user-land allocator and frees the cs_malloc'd memory.
pub fn disasmAlloc(self: Self, allocator: std.mem.Allocator, code: []const u8, address: usize, count: usize) ![]insn.Insn {
const disass = try impl.disasm(self.native, code, address, count);
defer impl.free(disass);
return allocator.dupe(insn.Insn, disass);
}

/// Uses global/threadlocal storage to avoid heap allocations for the single Insn struct.
pub fn disasmIter(self: Self, code: []const u8, address: u64) iter.IterUnmanaged {
return impl.disasmIter(self.native, code, address, tmp_storage.getIns());
}

/// Same as `impl.createInsn` but has more context.
pub fn createInsn(self: Self, allocator: Allocator) !*insn.Insn {
return impl.createInsn(allocator, self.detail_on);
}

/// Creates a duplicate of the provided Insn object using the provided allocator.
/// The owner takes responsibility of the pointer.
/// Must be destroyed with `destroyInsn`.
pub fn dupeInsn(self: Self, allocator: Allocator, ins: *const insn.Insn) !*insn.Insn {
var new_ins = try self.createInsn(allocator);
const new_detail = new_ins.detail;
new_ins.* = ins.*;

if (self.detail_on and ins.detail == null) {
std.io.getStdErr().writer().print("Cannot duplicate `ins`, doesn't have detail.\n", .{}) catch {};
return error.NotEnoughFields;
}

if (new_detail != null) {
new_detail.?.* = ins.detail.?.*;
}
new_ins.detail = new_detail;

return new_ins;
}

/// Same as impl.destroyInsn.
pub fn destroyInsn(_: Self, allocator: Allocator, ins: *insn.Insn) void {
impl.destroyInsn(allocator, ins);
}

pub fn deinit(self: *Self) void {
impl.close(&self.native) catch |e| {
std.io.getStdErr().writer().print("Failed to close handle: {any}\n", .{impl.strerror(e)}) catch {};
};
}

test "with options" {
const code = "\x75\x14";

var handle = try init(.X86, .@"64", .{
.syntax = .INTEL,
.detail = true,
.mnemonic = &.{.{ .id = cs.X86_INS_JNE, .mnemonic = "jnz" }},
});
defer handle.deinit();

const disass = try handle.disasm(code, 0x1000, 0);
defer impl.free(disass);

try std.testing.expectEqual(1, disass.len);
try std.testing.expect(disass[0].detail != null);
try std.testing.expectEqualStrings("jnz", disass[0].mnemonic[0..3]);
}

test "disasm iter" {
const code = "\x75\x14";

var handle = try init(.X86, .@"64", .{
.syntax = .INTEL,
.detail = true,
.mnemonic = &.{.{ .id = cs.X86_INS_JNE, .mnemonic = "jnz" }},
});
defer handle.deinit();

var dis_iter = handle.disasmIter(code, 0x1000);
const ins = dis_iter.next().?;

try std.testing.expect(ins.detail != null);
try std.testing.expectEqualStrings("jnz", ins.mnemonic[0..3]);
}

test "create, dupe and destroy Insn" {
const testing = @import("std").testing;
const allocator = testing.allocator;

var handle = try init(.X86, .@"64", .{
.syntax = .INTEL,
.detail = true,
});
defer handle.deinit();

const ins = try handle.createInsn(allocator);
defer handle.destroyInsn(allocator, ins);

ins.id = 0x1234;
ins.address = 0x1000;
ins.size = 2;
ins.detail.?.groups_count = 2;

const duped_ins = try handle.dupeInsn(allocator, ins);
defer handle.destroyInsn(allocator, duped_ins);

try testing.expectEqual(ins.id, duped_ins.id);
try testing.expectEqual(ins.address, duped_ins.address);
try testing.expectEqual(ins.size, duped_ins.size);
try testing.expectEqual(ins.detail.?.groups_count, duped_ins.detail.?.groups_count);
try testing.expect(ins.detail != duped_ins.detail);
}
36 changes: 18 additions & 18 deletions src/arch/arch.zig
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
const x86 = @import("x86/all.zig");
const arm64 = @import("arm64/all.zig");
const arm = @import("arm/all.zig");
const m68k = @import("m68k/all.zig");
const mips = @import("mips/all.zig");
const ppc = @import("ppc/all.zig");
const sparc = @import("sparc/all.zig");
const sysz = @import("sysz/all.zig");
const xcore = @import("xcore/all.zig");
const tms320c64x = @import("tms320c64x/all.zig");
const m680x = @import("m680x/all.zig");
const evm = @import("evm/all.zig");
const mos65xx = @import("mos65xx/all.zig");
const wasm = @import("wasm/all.zig");
const bpf = @import("bpf/all.zig");
const riscv = @import("riscv/all.zig");
const sh = @import("sh/all.zig");
const tricore = @import("tricore/all.zig");
pub const x86 = @import("x86/all.zig");
pub const arm64 = @import("arm64/all.zig");
pub const arm = @import("arm/all.zig");
pub const m68k = @import("m68k/all.zig");
pub const mips = @import("mips/all.zig");
pub const ppc = @import("ppc/all.zig");
pub const sparc = @import("sparc/all.zig");
pub const sysz = @import("sysz/all.zig");
pub const xcore = @import("xcore/all.zig");
pub const tms320c64x = @import("tms320c64x/all.zig");
pub const m680x = @import("m680x/all.zig");
pub const evm = @import("evm/all.zig");
pub const mos65xx = @import("mos65xx/all.zig");
pub const wasm = @import("wasm/all.zig");
pub const bpf = @import("bpf/all.zig");
pub const riscv = @import("riscv/all.zig");
pub const sh = @import("sh/all.zig");
pub const tricore = @import("tricore/all.zig");

pub const Arch = extern union {
x86: x86.Arch,
Expand Down
17 changes: 12 additions & 5 deletions src/arch/arm/all.zig
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
const VectorData = @import("vectordata.zig").VectorData;
const Cps = @import("cps.zig");
const Cc = @import("cc.zig").Cc;
const Barrier = @import("barrier.zig").Barrier;
const Operand = @import("operand.zig").Operand;
pub const Barrier = @import("barrier.zig").Barrier;
pub const Cc = @import("cc.zig").Cc;
pub const Cps = @import("cps.zig");
pub const OpType = @import("op_type.zig").OpType;
pub const Register = @import("register.zig").Register;
pub const Setend = @import("setend.zig").Setend;
pub const Shift = @import("shift.zig").Shift;
pub const VectorData = @import("vectordata.zig").VectorData;
pub const OpMem = @import("op_mem.zig").OpMem;
pub const Shifter = @import("shifter.zig").Shifter;
pub const Instruction = @import("instruction.zig").Instruction;
pub const Operand = @import("operand.zig").Operand;

pub const Arch = extern struct {
usermode: bool,
Expand Down
Loading
Loading