From 320a085cc27fe64dd1964d75c4aa90244536a5f1 Mon Sep 17 00:00:00 2001 From: IamSanjid Date: Wed, 28 May 2025 13:11:49 +0600 Subject: [PATCH 01/24] updated for zig-master(0.15.0-dev.646+ef35c3d5f) --- build.zig | 2 +- build.zig.zon | 7 ++++--- src/iter.zig | 5 ----- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/build.zig b/build.zig index 46a1594..8ba1823 100644 --- a/build.zig +++ b/build.zig @@ -12,7 +12,7 @@ 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, diff --git a/build.zig.zon b/build.zig.zon index c7337cb..c203f08 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -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?ref=main#6035a194bea8472c463345b86c3ef9333490ecfe", + .hash = "capstone-5.0.1-tmNUgTRHAAC1nFVy0colCWwUSRm05SWdD-4kU5MK4ZpH", }, }, diff --git a/src/iter.zig b/src/iter.zig index 9de5a79..e73e710 100644 --- a/src/iter.zig +++ b/src/iter.zig @@ -25,9 +25,4 @@ pub const Iter = struct { pub fn deinit(self: Iter) void { cs.cs_free(@ptrCast(self.insn), 1); } - - /// Returns the current address - pub fn address(self: Iter) u64 { - return self.address; - } }; From 57dee56c3034fdc483581e3ebf0cbdda93a97d4b Mon Sep 17 00:00:00 2001 From: IamSanjid Date: Wed, 28 May 2025 13:28:21 +0600 Subject: [PATCH 02/24] fix some compilation errors --- src/setup.zig | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/setup.zig b/src/setup.zig index 60ac705..d5cecc9 100644 --- a/src/setup.zig +++ b/src/setup.zig @@ -1,3 +1,4 @@ +const builtin = @import("builtin"); const std = @import("std"); const insn = @import("insn.zig"); @@ -5,11 +6,16 @@ const err = @import("error.zig"); const cs = @import("capstone-c"); +const VaList = if (builtin.os.tag == .windows and builtin.cpu.arch == .x86_64) + cs.__builtin_va_list +else + std.builtin.VaList; + pub const MallocFunction = ?*const fn (usize) callconv(.C) ?*anyopaque; pub const CallocFunction = ?*const fn (usize, usize) callconv(.C) ?*anyopaque; pub const ReallocFunction = ?*const fn (?*anyopaque, usize) callconv(.C) ?*anyopaque; pub const FreeFunction = ?*const fn (?*anyopaque) callconv(.C) void; -pub const VsnprintfFunction = ?*const fn ([*]u8, usize, [*]const u8, [*]std.builtin.VaList) callconv(.C) c_int; +pub const VsnprintfFunction = ?*const fn ([*]u8, usize, [*]const u8, [*]VaList) callconv(.C) c_int; var ALLOCATOR: ?std.mem.Allocator = null; @@ -20,7 +26,7 @@ var ALLOCATION_TABLE: AllocationTable = .{}; fn malloc(size: usize) callconv(.C) ?*anyopaque { if (ALLOCATOR) |alloc| { - const allocated = alloc.alignedAlloc(u8, 16, size) catch return null; + const allocated = alloc.alignedAlloc(u8, .@"16", size) catch return null; ALLOCATION_TABLE.put(alloc, @intFromPtr(allocated.ptr), allocated.len) catch @panic("OOM"); return @ptrCast(allocated.ptr); } else { @@ -74,7 +80,7 @@ fn free(ptr: ?*anyopaque) callconv(.C) void { } } -extern "C" fn vsnprintf([*c]u8, usize, [*c]const u8, [*c]std.builtin.VaList) callconv(.C) c_int; +extern "C" fn vsnprintf([*c]u8, usize, [*c]const u8, [*c]VaList) callconv(.C) c_int; /// Inits Capstone to be used with Zig pub fn initCapstone(alloc: std.mem.Allocator) err.CapstoneError!void { From 8d794862aa257c2dda14580383f1f6ceec35581b Mon Sep 17 00:00:00 2001 From: IamSanjid Date: Wed, 28 May 2025 14:01:26 +0600 Subject: [PATCH 03/24] cs_opt_value zig enum --- src/enums.zig | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/enums.zig b/src/enums.zig index 259a067..197bf81 100644 --- a/src/enums.zig +++ b/src/enums.zig @@ -111,3 +111,14 @@ pub const Type = enum(cs.cs_opt_type) { UNSIGNED, NO_BRANCH_OFFSET, }; + +pub const Value = enum(cs.cs_opt_value) { + OFF = 0, // < Turn OFF an option - default for CS_OPT_DETAIL, CS_OPT_SKIPDATA, CS_OPT_UNSIGNED. + ON = 3, + SYNTAX_DEFAULT = 0, + SYNTAX_INTEL, + SYNTAX_ATT, + SYNTAX_NOREGNAME, + SYNTAX_MASM, + SYNTAX_MOTOROLA, +}; From f55459d64b6a3fd73f51add124a4e7df0da2b3da Mon Sep 17 00:00:00 2001 From: IamSanjid Date: Wed, 28 May 2025 14:03:22 +0600 Subject: [PATCH 04/24] cs_opt_value zig enum exposed --- capstone.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/capstone.zig b/capstone.zig index ee41a9b..10e2e75 100644 --- a/capstone.zig +++ b/capstone.zig @@ -42,3 +42,4 @@ const enums = @import("src/enums.zig"); pub const Arch = enums.Arch; pub const Mode = enums.Mode; pub const Type = enums.Type; +pub const Value = enums.Value; From b5a376982068026a946522f4873087b669560b94 Mon Sep 17 00:00:00 2001 From: IamSanjid Date: Wed, 28 May 2025 14:17:27 +0600 Subject: [PATCH 05/24] added more enum variants --- capstone.zig | 3 +++ src/enums.zig | 39 ++++++++++++++++++++++++++++++++------- 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/capstone.zig b/capstone.zig index 10e2e75..b552b80 100644 --- a/capstone.zig +++ b/capstone.zig @@ -43,3 +43,6 @@ pub const Arch = enums.Arch; pub const Mode = enums.Mode; pub const Type = enums.Type; pub const Value = enums.Value; +pub const OpType = enums.OpType; +pub const AcType = enums.AcType; +pub const GroupType = enums.GroupType; diff --git a/src/enums.zig b/src/enums.zig index 197bf81..661e9fe 100644 --- a/src/enums.zig +++ b/src/enums.zig @@ -113,12 +113,37 @@ pub const Type = enum(cs.cs_opt_type) { }; pub const Value = enum(cs.cs_opt_value) { - OFF = 0, // < Turn OFF an option - default for CS_OPT_DETAIL, CS_OPT_SKIPDATA, CS_OPT_UNSIGNED. + OFF = 0, ON = 3, - SYNTAX_DEFAULT = 0, - SYNTAX_INTEL, - SYNTAX_ATT, - SYNTAX_NOREGNAME, - SYNTAX_MASM, - SYNTAX_MOTOROLA, + SYNTAX_INTEL = 1, + SYNTAX_ATT = 2, + SYNTAX_MASM = 4, + SYNTAX_MOTOROLA = 5, + pub const SYNTAX_DEFAULT: @This() = .OFF; + pub const SYNTAX_NOREGNAME: @This() = .ON; +}; + +pub const OpType = enum(cs.cs_op_type) { + INVALID = 0, + REG = 1, + IMM = 2, + FP = 3, + MEM = 0x80, +}; + +pub const AcType = enum(cs.cs_ac_type) { + INVALID = 0, + READ = 1 << 0, + WRITE = 1 << 1, +}; + +pub const GroupType = enum(cs.cs_group_type) { + INVALID = 0, + JUMP = 1, + CALL = 2, + RET = 3, + INT = 4, + IRET = 5, + PRIVILEGE = 6, + BRANCH_RELATIVE = 7, }; From 36ecfb5e2eac158835d668ae0f58d207c3cc898e Mon Sep 17 00:00:00 2001 From: IamSanjid Date: Wed, 28 May 2025 14:36:23 +0600 Subject: [PATCH 06/24] expose capstone-c --- build.zig | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/build.zig b/build.zig index 8ba1823..09ee6cb 100644 --- a/build.zig +++ b/build.zig @@ -17,6 +17,8 @@ pub fn build(b: *std.Build) void { .optimize = optimize, .link_libc = true, }); + // expose it for headers... + const capstone_c_mod = capstone_c.addModule("capstone-c"); const mod = b.addModule("capstone-bindings-zig", .{ .root_source_file = b.path("capstone.zig"), @@ -25,7 +27,7 @@ pub fn build(b: *std.Build) void { .imports = &.{ .{ .name = "capstone-c", - .module = capstone_c.createModule(), + .module = capstone_c_mod, }, }, }); @@ -40,7 +42,7 @@ pub fn build(b: *std.Build) void { }); 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); const run_lib_tests = b.addRunArtifact(mod_test); const test_step = b.step("test", "Run the library tests"); From 3e0f5b82629752ed1ebe347af912bbd4a2a82c3b Mon Sep 17 00:00:00 2001 From: IamSanjid Date: Wed, 28 May 2025 14:38:36 +0600 Subject: [PATCH 07/24] remove unnecessary enums --- capstone.zig | 4 ---- src/enums.zig | 36 ------------------------------------ 2 files changed, 40 deletions(-) diff --git a/capstone.zig b/capstone.zig index b552b80..ee41a9b 100644 --- a/capstone.zig +++ b/capstone.zig @@ -42,7 +42,3 @@ const enums = @import("src/enums.zig"); pub const Arch = enums.Arch; pub const Mode = enums.Mode; pub const Type = enums.Type; -pub const Value = enums.Value; -pub const OpType = enums.OpType; -pub const AcType = enums.AcType; -pub const GroupType = enums.GroupType; diff --git a/src/enums.zig b/src/enums.zig index 661e9fe..259a067 100644 --- a/src/enums.zig +++ b/src/enums.zig @@ -111,39 +111,3 @@ pub const Type = enum(cs.cs_opt_type) { UNSIGNED, NO_BRANCH_OFFSET, }; - -pub const Value = enum(cs.cs_opt_value) { - OFF = 0, - ON = 3, - SYNTAX_INTEL = 1, - SYNTAX_ATT = 2, - SYNTAX_MASM = 4, - SYNTAX_MOTOROLA = 5, - pub const SYNTAX_DEFAULT: @This() = .OFF; - pub const SYNTAX_NOREGNAME: @This() = .ON; -}; - -pub const OpType = enum(cs.cs_op_type) { - INVALID = 0, - REG = 1, - IMM = 2, - FP = 3, - MEM = 0x80, -}; - -pub const AcType = enum(cs.cs_ac_type) { - INVALID = 0, - READ = 1 << 0, - WRITE = 1 << 1, -}; - -pub const GroupType = enum(cs.cs_group_type) { - INVALID = 0, - JUMP = 1, - CALL = 2, - RET = 3, - INT = 4, - IRET = 5, - PRIVILEGE = 6, - BRANCH_RELATIVE = 7, -}; From a70461006005f88077550a2d9d36b6497c1a8927 Mon Sep 17 00:00:00 2001 From: IamSanjid Date: Thu, 29 May 2025 00:25:08 +0600 Subject: [PATCH 08/24] expose capstone-c --- build.zig | 2 +- capstone.zig | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/build.zig b/build.zig index 09ee6cb..e19e58e 100644 --- a/build.zig +++ b/build.zig @@ -18,7 +18,7 @@ pub fn build(b: *std.Build) void { .link_libc = true, }); // expose it for headers... - const capstone_c_mod = capstone_c.addModule("capstone-c"); + const capstone_c_mod = capstone_c.createModule("capstone-c"); const mod = b.addModule("capstone-bindings-zig", .{ .root_source_file = b.path("capstone.zig"), diff --git a/capstone.zig b/capstone.zig index ee41a9b..00efa18 100644 --- a/capstone.zig +++ b/capstone.zig @@ -42,3 +42,5 @@ const enums = @import("src/enums.zig"); pub const Arch = enums.Arch; pub const Mode = enums.Mode; pub const Type = enums.Type; + +pub const c = @import("capstone-c"); From 93044d1f418a73da126810f2e032119de821ea7b Mon Sep 17 00:00:00 2001 From: IamSanjid Date: Thu, 29 May 2025 00:27:07 +0600 Subject: [PATCH 09/24] expose capstone-c properly --- build.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.zig b/build.zig index e19e58e..5dfae8e 100644 --- a/build.zig +++ b/build.zig @@ -18,7 +18,7 @@ pub fn build(b: *std.Build) void { .link_libc = true, }); // expose it for headers... - const capstone_c_mod = capstone_c.createModule("capstone-c"); + const capstone_c_mod = capstone_c.createModule(); const mod = b.addModule("capstone-bindings-zig", .{ .root_source_file = b.path("capstone.zig"), From 6f3fb090655d533dd8e0c04e89b9006f318db574 Mon Sep 17 00:00:00 2001 From: IamSanjid Date: Thu, 29 May 2025 00:46:50 +0600 Subject: [PATCH 10/24] expose Insn and Detail --- capstone.zig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/capstone.zig b/capstone.zig index 00efa18..0efb931 100644 --- a/capstone.zig +++ b/capstone.zig @@ -1,6 +1,8 @@ const impl = @import("src/impl.zig"); pub const Iter = impl.Iter; pub const Handle = impl.Handle; +pub const Insn = impl.insn.Insn; +pub const Detail = impl.insn.Detail; pub const version = impl.version; pub const support = impl.support; pub const open = impl.open; From 13641dbaddfdc0d0058681ec7914fb808bf2dbd8 Mon Sep 17 00:00:00 2001 From: IamSanjid Date: Thu, 29 May 2025 01:10:03 +0600 Subject: [PATCH 11/24] Expose Detail, Arch, Insn --- capstone.zig | 4 ++-- src/arch/arch.zig | 36 ++++++++++++++++++------------------ src/enums.zig | 2 +- src/insn.zig | 4 +++- 4 files changed, 24 insertions(+), 22 deletions(-) diff --git a/capstone.zig b/capstone.zig index 0efb931..2245e5b 100644 --- a/capstone.zig +++ b/capstone.zig @@ -1,8 +1,8 @@ const impl = @import("src/impl.zig"); pub const Iter = impl.Iter; pub const Handle = impl.Handle; -pub const Insn = impl.insn.Insn; 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; @@ -43,6 +43,6 @@ pub const initCapstoneManually = setup.initCapstoneManually; const enums = @import("src/enums.zig"); pub const Arch = enums.Arch; pub const Mode = enums.Mode; -pub const Type = enums.Type; +pub const OptionsType = enums.OptionsType; pub const c = @import("capstone-c"); diff --git a/src/arch/arch.zig b/src/arch/arch.zig index 7e6c2a1..09a4280 100644 --- a/src/arch/arch.zig +++ b/src/arch/arch.zig @@ -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, diff --git a/src/enums.zig b/src/enums.zig index 259a067..c225110 100644 --- a/src/enums.zig +++ b/src/enums.zig @@ -99,7 +99,7 @@ pub const Mode = enum(cs.cs_mode) { }; // zig fmt: on -pub const Type = enum(cs.cs_opt_type) { +pub const OptionsType = enum(cs.cs_opt_type) { INVALID, SYNTAX, DETAIL, diff --git a/src/insn.zig b/src/insn.zig index 82809e0..56c0b4a 100644 --- a/src/insn.zig +++ b/src/insn.zig @@ -8,7 +8,9 @@ pub const Detail = extern struct { groups: [8]u8, groups_count: u8, writeback: bool, - arch: arch.Arch, + arch: Arch, + + pub const Arch = arch.Arch; }; pub const Insn = extern struct { From 1bc2966cf787f45920f150844da4e693b2d5edbe Mon Sep 17 00:00:00 2001 From: IamSanjid Date: Thu, 29 May 2025 01:12:50 +0600 Subject: [PATCH 12/24] remove unnecessary expose --- src/arch/arch.zig | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/arch/arch.zig b/src/arch/arch.zig index 09a4280..7e6c2a1 100644 --- a/src/arch/arch.zig +++ b/src/arch/arch.zig @@ -1,21 +1,21 @@ -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"); +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 Arch = extern union { x86: x86.Arch, From 74352fd27f86f915d4c0756ced02509f0b3b7479 Mon Sep 17 00:00:00 2001 From: IamSanjid Date: Thu, 29 May 2025 01:14:26 +0600 Subject: [PATCH 13/24] fix naming enums.OptionsType --- src/impl.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/impl.zig b/src/impl.zig index 479fef5..37d9c5a 100644 --- a/src/impl.zig +++ b/src/impl.zig @@ -31,7 +31,7 @@ pub fn close(handle: *Handle) err.CapstoneError!void { return err.toError(cs.cs_close(@ptrCast(handle))) orelse return; } -pub fn option(handle: ?Handle, @"type": enums.Type, value: usize) err.CapstoneError!void { +pub fn option(handle: ?Handle, @"type": enums.OptionsType, value: usize) err.CapstoneError!void { return err.toError(cs.cs_option(handle orelse 0, @intFromEnum(@"type"), value)) orelse return; } From 2bb1a0229ee84898ebfa72a0f51826140f0991bf Mon Sep 17 00:00:00 2001 From: IamSanjid Date: Thu, 29 May 2025 08:56:06 +0600 Subject: [PATCH 14/24] Iter with reset --- src/impl.zig | 14 ++------------ src/iter.zig | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/src/impl.zig b/src/impl.zig index 37d9c5a..5cade60 100644 --- a/src/impl.zig +++ b/src/impl.zig @@ -71,23 +71,13 @@ pub fn malloc(handle: Handle) [*]insn.Insn { /// Same as the normal Variant, but does the allocation for you. pub fn disasmIterManaged(handle: Handle, code: []const u8, address: u64) Iter { const ins = malloc(handle); - return Iter{ - .handle = handle, - .code = code, - .address = address, - .insn = ins, - }; + return Iter.init(handle, code, address, ins); } /// Return an Iter object /// Does not yet consume any element. pub fn disasmIter(handle: Handle, code: []const u8, address: u64, ins: [*]insn.Insn) Iter { - return Iter{ - .handle = handle, - .code = code, - .address = address, - .insn = ins, - }; + return Iter.init(handle, code, address, ins); } pub fn regName(handle: Handle, reg_id: c_uint) [*:0]const u8 { diff --git a/src/iter.zig b/src/iter.zig index e73e710..57cb915 100644 --- a/src/iter.zig +++ b/src/iter.zig @@ -7,9 +7,22 @@ const Handle = cs.csh; pub const Iter = struct { handle: Handle, code: []const u8, + original_code: []const u8, + original_address: u64, address: u64, insn: [*]Insn, + pub fn init(handle: Handle, code: []const u8, address: u64, insn: [*]Insn) Iter { + return Iter{ + .handle = handle, + .code = code, + .original_code = code, + .original_address = address, + .address = address, + .insn = insn, + }; + } + // Consumes the iterator and goes to the next pub fn next(self: *Iter) ?*Insn { if (cs.cs_disasm_iter(self.handle, @ptrCast(&self.code.ptr), @ptrCast(&self.code.len), &self.address, @ptrCast(self.insn))) { @@ -21,6 +34,11 @@ pub const Iter = struct { } } + pub fn reset(self: *Iter) void { + self.address = self.original_address; + self.code = self.original_code; + } + // Clean up the iter pub fn deinit(self: Iter) void { cs.cs_free(@ptrCast(self.insn), 1); From fd7c93083199669f6f52521a5da14f826bc661a7 Mon Sep 17 00:00:00 2001 From: IamSanjid Date: Fri, 30 May 2025 05:06:46 +0600 Subject: [PATCH 15/24] Change, restored some stuff, support for zig 0.14.1 --- build.zig | 5 +- capstone.zig | 5 +- src/ManagedHandle.zig | 136 ++++++++++++++++++++++++++++++++++++++++++ src/enums.zig | 11 +++- src/impl.zig | 12 ++-- src/iter.zig | 27 ++++++++- src/setup.zig | 6 +- src/tests.zig | 4 ++ 8 files changed, 195 insertions(+), 11 deletions(-) create mode 100644 src/ManagedHandle.zig create mode 100644 src/tests.zig diff --git a/build.zig b/build.zig index 5dfae8e..47886e4 100644 --- a/build.zig +++ b/build.zig @@ -36,13 +36,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_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"); diff --git a/capstone.zig b/capstone.zig index 2245e5b..68c6a49 100644 --- a/capstone.zig +++ b/capstone.zig @@ -1,5 +1,6 @@ const impl = @import("src/impl.zig"); pub const Iter = impl.Iter; +pub const IterManaged = impl.IterManaged; pub const Handle = impl.Handle; pub const Detail = impl.insn.Detail; pub const Insn = impl.insn.Insn; @@ -43,6 +44,8 @@ pub const initCapstoneManually = setup.initCapstoneManually; const enums = @import("src/enums.zig"); pub const Arch = enums.Arch; pub const Mode = enums.Mode; -pub const OptionsType = enums.OptionsType; +pub const Type = enums.Type; + +pub const ManagedHandle = @import("src/ManagedHandle.zig"); pub const c = @import("capstone-c"); diff --git a/src/ManagedHandle.zig b/src/ManagedHandle.zig new file mode 100644 index 0000000..3b869ab --- /dev/null +++ b/src/ManagedHandle.zig @@ -0,0 +1,136 @@ +const builtin = @import("builtin"); +const std = @import("std"); + +const cs = @import("capstone-c"); + +const err = @import("error.zig"); +const insn = @import("insn.zig"); +const setup = @import("setup.zig"); +const enums = @import("enums.zig"); +const impl = @import("impl.zig"); + +const iter = @import("iter.zig"); + +const Iter = iter.Iter; +const IterManaged = iter.IterManaged; + +native: impl.Handle, + +// To avoid heap allocations... +const TmpStorage = blk: { + if (builtin.single_threaded) { + break :blk 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 { + break :blk 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, + /// 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, + }; +}; + +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.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 }; +} + +/// 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 { + return Iter.init(self.native, code, address, @ptrCast(TmpStorage.getIns())); +} + +pub fn deinit(self: *Self) void { + impl.close(&self.native) catch |e| { + std.debug.print("Failed to close handle: {any}\n", .{impl.strerror(e)}); + }; +} + +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]); +} diff --git a/src/enums.zig b/src/enums.zig index c225110..e857d00 100644 --- a/src/enums.zig +++ b/src/enums.zig @@ -99,7 +99,7 @@ pub const Mode = enum(cs.cs_mode) { }; // zig fmt: on -pub const OptionsType = enum(cs.cs_opt_type) { +pub const Type = enum(cs.cs_opt_type) { INVALID, SYNTAX, DETAIL, @@ -111,3 +111,12 @@ pub const OptionsType = enum(cs.cs_opt_type) { UNSIGNED, NO_BRANCH_OFFSET, }; + +pub const Syntax = enum(cs.cs_opt_value) { + DEFAULT = 0, + INTEL, + ATT, + NOREGNAME, + MASM, + MOTOROLA, +}; diff --git a/src/impl.zig b/src/impl.zig index 5cade60..7f2e875 100644 --- a/src/impl.zig +++ b/src/impl.zig @@ -4,8 +4,10 @@ pub const err = @import("error.zig"); pub const insn = @import("insn.zig"); pub const setup = @import("setup.zig"); pub const enums = @import("enums.zig"); +const iter = @import("iter.zig"); -pub const Iter = @import("iter.zig").Iter; +pub const Iter = iter.Iter; +pub const IterManaged = iter.IterManaged; pub const Handle = cs.csh; pub fn version(major: ?*c_int, minor: ?*c_int) err.CapstoneError!void { @@ -31,7 +33,7 @@ pub fn close(handle: *Handle) err.CapstoneError!void { return err.toError(cs.cs_close(@ptrCast(handle))) orelse return; } -pub fn option(handle: ?Handle, @"type": enums.OptionsType, value: usize) err.CapstoneError!void { +pub fn option(handle: ?Handle, @"type": enums.Type, value: usize) err.CapstoneError!void { return err.toError(cs.cs_option(handle orelse 0, @intFromEnum(@"type"), value)) orelse return; } @@ -69,9 +71,8 @@ pub fn malloc(handle: Handle) [*]insn.Insn { } /// Same as the normal Variant, but does the allocation for you. -pub fn disasmIterManaged(handle: Handle, code: []const u8, address: u64) Iter { - const ins = malloc(handle); - return Iter.init(handle, code, address, ins); +pub fn disasmIterManaged(handle: Handle, code: []const u8, address: u64) IterManaged { + return IterManaged.init(handle, code, address); } /// Return an Iter object @@ -128,4 +129,5 @@ test { @import("std").testing.refAllDecls(@import("error.zig")); @import("std").testing.refAllDecls(@import("insn.zig")); @import("std").testing.refAllDecls(@import("setup.zig")); + @import("std").testing.refAllDecls(@import("ManagedHandle.zig")); } diff --git a/src/iter.zig b/src/iter.zig index 57cb915..76b4e93 100644 --- a/src/iter.zig +++ b/src/iter.zig @@ -3,7 +3,7 @@ const cs = @import("capstone-c"); const Insn = @import("insn.zig").Insn; const Handle = cs.csh; -// The Iterator for traversing the disassembler +/// The Iterator for traversing the disassembler pub const Iter = struct { handle: Handle, code: []const u8, @@ -38,9 +38,32 @@ pub const Iter = struct { self.address = self.original_address; self.code = self.original_code; } +}; + +/// The Iterator for traversing the disassembler, but **allocates** space using capstone malloc. +pub const IterManaged = struct { + inner: Iter, + insn: [*]Insn, + + pub fn init(handle: Handle, code: []const u8, address: u64) IterManaged { + const insn: [*]Insn = @ptrCast(cs.cs_malloc(handle)); + return .{ + .inner = Iter.init(handle, code, address, insn), + .insn = insn, + }; + } + + // Consumes the iterator and goes to the next + pub fn next(self: *IterManaged) ?*Insn { + return self.inner.next(); + } + + pub fn reset(self: *IterManaged) void { + self.inner.reset(); + } // Clean up the iter - pub fn deinit(self: Iter) void { + pub fn deinit(self: IterManaged) void { cs.cs_free(@ptrCast(self.insn), 1); } }; diff --git a/src/setup.zig b/src/setup.zig index d5cecc9..ab55a59 100644 --- a/src/setup.zig +++ b/src/setup.zig @@ -6,6 +6,7 @@ const err = @import("error.zig"); const cs = @import("capstone-c"); +// TODO: Use std.builtin.VaList later for windows x86_64, it's disabled currently in the zig's builtin since 0.14.1 const VaList = if (builtin.os.tag == .windows and builtin.cpu.arch == .x86_64) cs.__builtin_va_list else @@ -26,7 +27,10 @@ var ALLOCATION_TABLE: AllocationTable = .{}; fn malloc(size: usize) callconv(.C) ?*anyopaque { if (ALLOCATOR) |alloc| { - const allocated = alloc.alignedAlloc(u8, .@"16", size) catch return null; + const allocated = if (builtin.zig_version.major == 0 and builtin.zig_version.minor == 14) + alloc.alignedAlloc(u8, 16, size) catch return null + else + alloc.alignedAlloc(u8, .@"16", size) catch return null; ALLOCATION_TABLE.put(alloc, @intFromPtr(allocated.ptr), allocated.len) catch @panic("OOM"); return @ptrCast(allocated.ptr); } else { diff --git a/src/tests.zig b/src/tests.zig new file mode 100644 index 0000000..b6c1fae --- /dev/null +++ b/src/tests.zig @@ -0,0 +1,4 @@ +test { + _ = @import("impl.zig"); + @import("std").testing.refAllDecls(@This()); +} From 1934069a63e241614c9789dd36e473351b3e1d2b Mon Sep 17 00:00:00 2001 From: IamSanjid Date: Fri, 30 May 2025 07:45:59 +0600 Subject: [PATCH 16/24] using capstone pre-release 5.0.1-2 and some minor changes --- build.zig.zon | 2 +- src/ManagedHandle.zig | 4 ++-- src/impl.zig | 10 ++++++++-- src/iter.zig | 20 ++++++++------------ src/tests.zig | 1 + 5 files changed, 20 insertions(+), 17 deletions(-) diff --git a/build.zig.zon b/build.zig.zon index c203f08..fa6ea42 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -6,7 +6,7 @@ .dependencies = .{ .capstone = .{ - .url = "git+https://github.com/allyourcodebase/capstone.git?ref=main#6035a194bea8472c463345b86c3ef9333490ecfe", + .url = "git+https://github.com/allyourcodebase/capstone.git?ref=5.0.1-2#6035a194bea8472c463345b86c3ef9333490ecfe", .hash = "capstone-5.0.1-tmNUgTRHAAC1nFVy0colCWwUSRm05SWdD-4kU5MK4ZpH", }, }, diff --git a/src/ManagedHandle.zig b/src/ManagedHandle.zig index 3b869ab..b34f020 100644 --- a/src/ManagedHandle.zig +++ b/src/ManagedHandle.zig @@ -108,12 +108,12 @@ pub fn disasmAlloc(self: Self, allocator: std.mem.Allocator, code: []const u8, a /// Uses global/threadlocal storage to avoid heap allocations for the single Insn struct. pub fn disasmIter(self: Self, code: []const u8, address: u64) Iter { - return Iter.init(self.native, code, address, @ptrCast(TmpStorage.getIns())); + return impl.disasmIter(self.native, code, address, @ptrCast(TmpStorage.getIns())); } pub fn deinit(self: *Self) void { impl.close(&self.native) catch |e| { - std.debug.print("Failed to close handle: {any}\n", .{impl.strerror(e)}); + std.io.getStdErr().writer().print("Failed to close handle: {any}\n", .{impl.strerror(e)}) catch {}; }; } diff --git a/src/impl.zig b/src/impl.zig index 7f2e875..a3c65df 100644 --- a/src/impl.zig +++ b/src/impl.zig @@ -78,7 +78,14 @@ pub fn disasmIterManaged(handle: Handle, code: []const u8, address: u64) IterMan /// Return an Iter object /// Does not yet consume any element. pub fn disasmIter(handle: Handle, code: []const u8, address: u64, ins: [*]insn.Insn) Iter { - return Iter.init(handle, code, address, ins); + return Iter{ + .handle = handle, + .code = code, + .original_code = code, + .original_address = address, + .address = address, + .insn = ins, + }; } pub fn regName(handle: Handle, reg_id: c_uint) [*:0]const u8 { @@ -129,5 +136,4 @@ test { @import("std").testing.refAllDecls(@import("error.zig")); @import("std").testing.refAllDecls(@import("insn.zig")); @import("std").testing.refAllDecls(@import("setup.zig")); - @import("std").testing.refAllDecls(@import("ManagedHandle.zig")); } diff --git a/src/iter.zig b/src/iter.zig index 76b4e93..ba3be5e 100644 --- a/src/iter.zig +++ b/src/iter.zig @@ -12,17 +12,6 @@ pub const Iter = struct { address: u64, insn: [*]Insn, - pub fn init(handle: Handle, code: []const u8, address: u64, insn: [*]Insn) Iter { - return Iter{ - .handle = handle, - .code = code, - .original_code = code, - .original_address = address, - .address = address, - .insn = insn, - }; - } - // Consumes the iterator and goes to the next pub fn next(self: *Iter) ?*Insn { if (cs.cs_disasm_iter(self.handle, @ptrCast(&self.code.ptr), @ptrCast(&self.code.len), &self.address, @ptrCast(self.insn))) { @@ -48,7 +37,14 @@ pub const IterManaged = struct { pub fn init(handle: Handle, code: []const u8, address: u64) IterManaged { const insn: [*]Insn = @ptrCast(cs.cs_malloc(handle)); return .{ - .inner = Iter.init(handle, code, address, insn), + .inner = Iter{ + .handle = handle, + .code = code, + .original_code = code, + .original_address = address, + .address = address, + .insn = insn, + }, .insn = insn, }; } diff --git a/src/tests.zig b/src/tests.zig index b6c1fae..0aa02f3 100644 --- a/src/tests.zig +++ b/src/tests.zig @@ -1,4 +1,5 @@ test { _ = @import("impl.zig"); + _ = @import("ManagedHandle.zig"); @import("std").testing.refAllDecls(@This()); } From 3746dd4b0898a9424c45af8f9e657510e3a382eb Mon Sep 17 00:00:00 2001 From: Purple Date: Fri, 30 May 2025 16:17:45 +0600 Subject: [PATCH 17/24] Update build.zig.zon Co-authored-by: Sora --- build.zig.zon | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.zig.zon b/build.zig.zon index fa6ea42..027cfbd 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -6,7 +6,7 @@ .dependencies = .{ .capstone = .{ - .url = "git+https://github.com/allyourcodebase/capstone.git?ref=5.0.1-2#6035a194bea8472c463345b86c3ef9333490ecfe", + .url = "git+https://github.com/allyourcodebase/capstone.git#5.0.1-2", .hash = "capstone-5.0.1-tmNUgTRHAAC1nFVy0colCWwUSRm05SWdD-4kU5MK4ZpH", }, }, From 2e9786e87c3a1fa9549c7c629988365e4d69e106 Mon Sep 17 00:00:00 2001 From: IamSanjid Date: Fri, 30 May 2025 16:49:39 +0600 Subject: [PATCH 18/24] changed [*]insn.Insn to *insn.Insn where it's appropriate --- capstone.zig | 3 ++- src/impl.zig | 40 ++++++++++++++++++++++++++-------------- 2 files changed, 28 insertions(+), 15 deletions(-) diff --git a/capstone.zig b/capstone.zig index 68c6a49..d1d744b 100644 --- a/capstone.zig +++ b/capstone.zig @@ -13,7 +13,8 @@ pub const errno = impl.errno; pub const strerror = impl.strerror; pub const disasm = impl.disasm; pub const free = impl.free; -pub const malloc = impl.malloc; +pub const allocInsn = impl.allocInsn; +pub const freeInsn = impl.freeInsn; pub const disasmIterManaged = impl.disasmIterManaged; pub const disasmIter = impl.disasmIter; pub const regName = impl.regName; diff --git a/src/impl.zig b/src/impl.zig index a3c65df..700547d 100644 --- a/src/impl.zig +++ b/src/impl.zig @@ -66,8 +66,13 @@ pub fn free(ins: []insn.Insn) void { cs.cs_free(@ptrCast(ins.ptr), ins.len); } -pub fn malloc(handle: Handle) [*]insn.Insn { - return @ptrCast(cs.cs_malloc(handle)); +pub fn allocInsn(handle: Handle) !*insn.Insn { + const ins: ?*insn.Insn = @ptrCast(cs.cs_malloc(handle)); + return if (ins) |i| return i else return error.OutOfMemory; +} + +pub fn freeInsn(ins: *insn.Insn) void { + cs.cs_free(@ptrCast(ins), 1); } /// Same as the normal Variant, but does the allocation for you. @@ -104,31 +109,38 @@ pub fn insnGroup(handle: Handle, ins: []const insn.Insn, group_id: c_uint) bool return cs.cs_insn_group(handle, @ptrCast(ins.ptr), group_id); } -pub fn regRead(handle: Handle, ins: []const insn.Insn, reg_id: c_uint) bool { - return cs.cs_reg_read(handle, @ptrCast(ins.ptr), reg_id); +pub fn regRead(handle: Handle, ins: *const insn.Insn, reg_id: c_uint) bool { + return cs.cs_reg_read(handle, @ptrCast(ins), reg_id); } -pub fn regWrite(handle: Handle, ins: []const insn.Insn, reg_id: c_uint) bool { - return cs.cs_reg_write(handle, @ptrCast(ins.ptr), reg_id); +pub fn regWrite(handle: Handle, ins: *const insn.Insn, reg_id: c_uint) bool { + return cs.cs_reg_write(handle, @ptrCast(ins), reg_id); } -pub fn opCount(handle: Handle, ins: []const insn.Insn, op_type: c_uint) c_int { - return cs.cs_op_count(handle, @ptrCast(ins.ptr), op_type); +pub fn opCount(handle: Handle, ins: *const insn.Insn, op_type: c_uint) c_int { + return cs.cs_op_count(handle, @ptrCast(ins), op_type); } -pub fn opIndex(handle: Handle, ins: []const insn.Insn, op_type: c_uint, position: c_uint) c_int { - return cs.cs_op_index(handle, @ptrCast(ins.ptr), op_type, position); +pub fn opIndex(handle: Handle, ins: *const insn.Insn, op_type: c_uint, position: c_uint) c_int { + return cs.cs_op_index(handle, @ptrCast(ins), op_type, position); } pub fn regsAccess( handle: Handle, - ins: []const insn.Insn, + ins: *const insn.Insn, regs_read: [*]u16, - regs_read_count: [*]u8, + regs_read_count: *u8, regs_write: [*]u16, - regs_write_count: [*]u8, + regs_write_count: *u8, ) err.CapstoneError!void { - return err.toError(cs.cs_regs_access(handle, @ptrCast(ins.ptr), regs_read, regs_read_count, regs_write, regs_write_count)) orelse return; + return err.toError(cs.cs_regs_access( + handle, + @ptrCast(ins), + regs_read, + @ptrCast(regs_read_count), + regs_write, + @ptrCast(regs_write_count), + )) orelse return; } test { From 767e955b35b30e16cbb71b809bb99d3c5a0c606f Mon Sep 17 00:00:00 2001 From: IamSanjid Date: Fri, 30 May 2025 20:09:55 +0600 Subject: [PATCH 19/24] expose arch --- capstone.zig | 20 ++++++++++++++++++++ src/arch/arch.zig | 36 ++++++++++++++++++------------------ 2 files changed, 38 insertions(+), 18 deletions(-) diff --git a/capstone.zig b/capstone.zig index d1d744b..f9fb593 100644 --- a/capstone.zig +++ b/capstone.zig @@ -49,4 +49,24 @@ 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"); diff --git a/src/arch/arch.zig b/src/arch/arch.zig index 7e6c2a1..09a4280 100644 --- a/src/arch/arch.zig +++ b/src/arch/arch.zig @@ -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, From dbde91c742b845c833f5e92181f1a2feec7466f0 Mon Sep 17 00:00:00 2001 From: IamSanjid Date: Sat, 31 May 2025 05:24:47 +0600 Subject: [PATCH 20/24] some cleanups and changes to adapt current Iter/Insn alloc impl --- build.zig | 1 - capstone.zig | 1 + src/ManagedHandle.zig | 61 ++++++++++++++++++++++++------------------- src/impl.zig | 17 ++++++------ src/iter.zig | 14 +++++----- 5 files changed, 51 insertions(+), 43 deletions(-) diff --git a/build.zig b/build.zig index 47886e4..64e1471 100644 --- a/build.zig +++ b/build.zig @@ -17,7 +17,6 @@ pub fn build(b: *std.Build) void { .optimize = optimize, .link_libc = true, }); - // expose it for headers... const capstone_c_mod = capstone_c.createModule(); const mod = b.addModule("capstone-bindings-zig", .{ diff --git a/capstone.zig b/capstone.zig index f9fb593..4fb3e81 100644 --- a/capstone.zig +++ b/capstone.zig @@ -2,6 +2,7 @@ const impl = @import("src/impl.zig"); pub const Iter = impl.Iter; 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; diff --git a/src/ManagedHandle.zig b/src/ManagedHandle.zig index b34f020..1bd0a86 100644 --- a/src/ManagedHandle.zig +++ b/src/ManagedHandle.zig @@ -5,39 +5,32 @@ const cs = @import("capstone-c"); const err = @import("error.zig"); const insn = @import("insn.zig"); -const setup = @import("setup.zig"); const enums = @import("enums.zig"); const impl = @import("impl.zig"); const iter = @import("iter.zig"); -const Iter = iter.Iter; -const IterManaged = iter.IterManaged; - native: impl.Handle, // To avoid heap allocations... -const TmpStorage = blk: { - if (builtin.single_threaded) { - break :blk 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 { - break :blk 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 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(); @@ -48,6 +41,9 @@ pub const Options = struct { 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 = &.{}, @@ -56,11 +52,17 @@ pub const Options = struct { /// ARM, prints branch immediates without offset. no_branch_offset: bool = false, - /// cs_opt_mnem + // 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, + }; }; pub fn init(arch: enums.Arch, mode: enums.Mode, options: Options) !Self { @@ -78,6 +80,11 @@ pub fn init(arch: enums.Arch, mode: enums.Mode, options: Options) !Self { 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); } @@ -107,8 +114,8 @@ pub fn disasmAlloc(self: Self, allocator: std.mem.Allocator, code: []const u8, a } /// Uses global/threadlocal storage to avoid heap allocations for the single Insn struct. -pub fn disasmIter(self: Self, code: []const u8, address: u64) Iter { - return impl.disasmIter(self.native, code, address, @ptrCast(TmpStorage.getIns())); +pub fn disasmIter(self: Self, code: []const u8, address: u64) iter.Iter { + return impl.disasmIter(self.native, code, address, tmp_storage.getIns()); } pub fn deinit(self: *Self) void { diff --git a/src/impl.zig b/src/impl.zig index 700547d..2ad2d54 100644 --- a/src/impl.zig +++ b/src/impl.zig @@ -76,13 +76,13 @@ pub fn freeInsn(ins: *insn.Insn) void { } /// Same as the normal Variant, but does the allocation for you. -pub fn disasmIterManaged(handle: Handle, code: []const u8, address: u64) IterManaged { +pub fn disasmIterManaged(handle: Handle, code: []const u8, address: u64) !IterManaged { return IterManaged.init(handle, code, address); } /// Return an Iter object /// Does not yet consume any element. -pub fn disasmIter(handle: Handle, code: []const u8, address: u64, ins: [*]insn.Insn) Iter { +pub fn disasmIter(handle: Handle, code: []const u8, address: u64, ins: *insn.Insn) Iter { return Iter{ .handle = handle, .code = code, @@ -105,8 +105,8 @@ pub fn groupName(handle: Handle, group_id: c_uint) [*:0]const u8 { return cs.cs_group_name(handle, group_id); } -pub fn insnGroup(handle: Handle, ins: []const insn.Insn, group_id: c_uint) bool { - return cs.cs_insn_group(handle, @ptrCast(ins.ptr), group_id); +pub fn insnGroup(handle: Handle, ins: *const insn.Insn, group_id: c_uint) bool { + return cs.cs_insn_group(handle, @ptrCast(ins), group_id); } pub fn regRead(handle: Handle, ins: *const insn.Insn, reg_id: c_uint) bool { @@ -125,20 +125,21 @@ pub fn opIndex(handle: Handle, ins: *const insn.Insn, op_type: c_uint, position: return cs.cs_op_index(handle, @ptrCast(ins), op_type, position); } +pub const Regs = [64]u16; pub fn regsAccess( handle: Handle, ins: *const insn.Insn, - regs_read: [*]u16, + regs_read: *Regs, regs_read_count: *u8, - regs_write: [*]u16, + regs_write: *Regs, regs_write_count: *u8, ) err.CapstoneError!void { return err.toError(cs.cs_regs_access( handle, @ptrCast(ins), - regs_read, + @ptrCast(regs_read[0..].ptr), @ptrCast(regs_read_count), - regs_write, + @ptrCast(regs_write[0..].ptr), @ptrCast(regs_write_count), )) orelse return; } diff --git a/src/iter.zig b/src/iter.zig index ba3be5e..99ea678 100644 --- a/src/iter.zig +++ b/src/iter.zig @@ -10,14 +10,13 @@ pub const Iter = struct { original_code: []const u8, original_address: u64, address: u64, - insn: [*]Insn, + insn: *Insn, // Consumes the iterator and goes to the next pub fn next(self: *Iter) ?*Insn { if (cs.cs_disasm_iter(self.handle, @ptrCast(&self.code.ptr), @ptrCast(&self.code.len), &self.address, @ptrCast(self.insn))) { - const ret = &self.insn[0]; - ret.normalizeStrings(); - return ret; + self.insn.normalizeStrings(); + return self.insn; } else { return null; } @@ -32,10 +31,11 @@ pub const Iter = struct { /// The Iterator for traversing the disassembler, but **allocates** space using capstone malloc. pub const IterManaged = struct { inner: Iter, - insn: [*]Insn, + insn: *Insn, - pub fn init(handle: Handle, code: []const u8, address: u64) IterManaged { - const insn: [*]Insn = @ptrCast(cs.cs_malloc(handle)); + pub fn init(handle: Handle, code: []const u8, address: u64) !IterManaged { + const insn_ptr: ?*Insn = @ptrCast(cs.cs_malloc(handle)); + const insn = if (insn_ptr) |i| i else return error.OutOfMemory; return .{ .inner = Iter{ .handle = handle, From facb0c41417ec8cd9ca14ea3d24df5e7f22c15b3 Mon Sep 17 00:00:00 2001 From: IamSanjid Date: Sat, 31 May 2025 05:59:34 +0600 Subject: [PATCH 21/24] keep the global function names close to as original capstone functions --- capstone.zig | 3 +-- src/impl.zig | 40 +++++++++++++++++++++++++++++++++------- 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/capstone.zig b/capstone.zig index 4fb3e81..0c38e7b 100644 --- a/capstone.zig +++ b/capstone.zig @@ -14,8 +14,7 @@ pub const errno = impl.errno; pub const strerror = impl.strerror; pub const disasm = impl.disasm; pub const free = impl.free; -pub const allocInsn = impl.allocInsn; -pub const freeInsn = impl.freeInsn; +pub const malloc = impl.malloc; pub const disasmIterManaged = impl.disasmIterManaged; pub const disasmIter = impl.disasmIter; pub const regName = impl.regName; diff --git a/src/impl.zig b/src/impl.zig index 2ad2d54..49914d2 100644 --- a/src/impl.zig +++ b/src/impl.zig @@ -62,19 +62,34 @@ pub fn disasm(handle: Handle, code: []const u8, address: usize, count: usize) er return ins[0..res_count]; } -pub fn free(ins: []insn.Insn) void { - cs.cs_free(@ptrCast(ins.ptr), ins.len); +/// Equivilent to cs_free +/// Only accepts `[]insn.Insn` or `*insn.Insn` types. +pub fn free(ins: anytype) void { + const type_info = @typeInfo(@TypeOf(ins)); + + if (type_info == .pointer) { + const pointer_type = type_info.pointer; + if (pointer_type.child != insn.Insn and pointer_type.size != .one) { + @compileError("`ins` type doesn't match `[]insn.Insn` or `*insn.Insn`"); + } + cs.cs_free(@ptrCast(ins), 1); + } else if (type_info == .array) { + const array_type = type_info.array; + if (array_type.child != insn.Insn) { + @compileError("`ins` type doesn't match `[]insn.Insn` or `*insn.Insn`"); + } + cs.cs_free(@ptrCast(ins.ptr), ins.len); + } else { + @compileError("`ins` type doesn't match `[]insn.Insn` or `*insn.Insn`"); + } } -pub fn allocInsn(handle: Handle) !*insn.Insn { +/// Equivilent to cs_malloc +pub fn malloc(handle: Handle) !*insn.Insn { const ins: ?*insn.Insn = @ptrCast(cs.cs_malloc(handle)); return if (ins) |i| return i else return error.OutOfMemory; } -pub fn freeInsn(ins: *insn.Insn) void { - cs.cs_free(@ptrCast(ins), 1); -} - /// Same as the normal Variant, but does the allocation for you. pub fn disasmIterManaged(handle: Handle, code: []const u8, address: u64) !IterManaged { return IterManaged.init(handle, code, address); @@ -150,3 +165,14 @@ test { @import("std").testing.refAllDecls(@import("insn.zig")); @import("std").testing.refAllDecls(@import("setup.zig")); } + +test "free" { + var handle = try open(.X86, .@"16"); + defer close(&handle) catch {}; + const ins = try malloc(handle); + defer free(ins); + + const disass = try disasm(handle, "\x75\x14", 0x1000, 0); + defer free(disass); + try @import("std").testing.expectEqual(1, disass.len); +} From d1649efa6ca8c0950201521d040fdca1eb496fec Mon Sep 17 00:00:00 2001 From: IamSanjid Date: Sat, 31 May 2025 09:18:33 +0600 Subject: [PATCH 22/24] Expose all the things of arch --- src/arch/arm/all.zig | 17 ++++++++++++----- src/arch/arm64/all.zig | 15 +++++++++++++++ src/arch/arm64/sys.zig | 0 src/arch/bpf/all.zig | 6 +++++- src/arch/m680x/all.zig | 8 +++++++- .../m68k/{addr_mode.zig => address_mode.zig} | 0 src/arch/m68k/all.zig | 14 ++++++++++++-- src/arch/m68k/op_size.zig | 8 ++++---- src/arch/m68k/operand.zig | 2 +- src/arch/mips/all.zig | 6 +++++- src/arch/mos65xx/all.zig | 7 +++++-- src/arch/ppc/all.zig | 11 ++++++++--- src/arch/riscv/all.zig | 5 ++++- src/arch/sh/all.zig | 13 +++++++++++-- src/arch/sparc/all.zig | 10 +++++++--- src/arch/sysz/all.zig | 8 ++++++-- src/arch/tms320c64x/all.zig | 9 ++++++--- src/arch/tricore/all.zig | 5 ++++- src/arch/wasm/all.zig | 5 ++++- src/arch/x86/all.zig | 6 +++--- src/arch/x86/avx.zig | 2 +- src/arch/x86/register.zig | 2 +- src/arch/x86/sse.zig | 2 +- src/arch/x86/xop.zig | 2 +- src/arch/xcore/all.zig | 6 +++++- src/impl.zig | 18 ++++++++++++------ src/iter.zig | 4 ++-- 27 files changed, 142 insertions(+), 49 deletions(-) delete mode 100644 src/arch/arm64/sys.zig rename src/arch/m68k/{addr_mode.zig => address_mode.zig} (100%) diff --git a/src/arch/arm/all.zig b/src/arch/arm/all.zig index 35b9167..3bca248 100644 --- a/src/arch/arm/all.zig +++ b/src/arch/arm/all.zig @@ -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, diff --git a/src/arch/arm64/all.zig b/src/arch/arm64/all.zig index 6157f9a..137f5e1 100644 --- a/src/arch/arm64/all.zig +++ b/src/arch/arm64/all.zig @@ -1,4 +1,19 @@ +pub const Barrier = @import("barrier.zig").Barrier; pub const Cc = @import("cc.zig").Cc; +pub const Extender = @import("extender.zig").Extender; +pub const OpType = @import("op_type.zig").OpType; +pub const Prefetch = @import("prefetch.zig").Prefetch; +pub const Pstate = @import("pstate.zig").Pstate; +pub const Register = @import("register.zig").Register; +pub const Shift = @import("shift.zig").Shift; +pub const Svcr = @import("svcr.zig").Svcr; +pub const SysOp = @import("sys_op.zig").SysOp; +pub const Vas = @import("vas.zig").Vas; +pub const Sme = @import("sme.zig").Sme; +pub const Shifter = @import("shifter.zig").Shifter; +const instruction = @import("instruction.zig"); +pub const OpMem = instruction.OpMem; +pub const Instruction = instruction.Instruction; pub const Operand = @import("operand.zig").Operand; pub const Arch = extern struct { diff --git a/src/arch/arm64/sys.zig b/src/arch/arm64/sys.zig deleted file mode 100644 index e69de29..0000000 diff --git a/src/arch/bpf/all.zig b/src/arch/bpf/all.zig index 14ac383..2437d5d 100644 --- a/src/arch/bpf/all.zig +++ b/src/arch/bpf/all.zig @@ -1,4 +1,8 @@ -const Operand = @import("operand.zig").Operand; +pub const OpMem = @import("op_mem.zig").OpMem; +pub const OpType = @import("op_type.zig").OpType; +pub const Register = @import("register.zig").Register; +pub const Instruction = @import("instruction.zig").Instruction; +pub const Operand = @import("operand.zig").Operand; pub const Arch = extern struct { op_count: u8, diff --git a/src/arch/m680x/all.zig b/src/arch/m680x/all.zig index 4018330..a706532 100644 --- a/src/arch/m680x/all.zig +++ b/src/arch/m680x/all.zig @@ -1,4 +1,10 @@ -const Operand = @import("operand.zig").Operand; +pub const OpExt = @import("op_ext.zig").OpExt; +pub const OpRel = @import("op_rel.zig").OpRel; +pub const OpType = @import("op_type.zig").OpType; +pub const Register = @import("register.zig").Register; +pub const OpIdx = @import("op_idx.zig").OpIdx; +pub const Instruction = @import("instruction.zig").Instruction; +pub const Operand = @import("operand.zig").Operand; pub const Arch = extern struct { flags: u8, diff --git a/src/arch/m68k/addr_mode.zig b/src/arch/m68k/address_mode.zig similarity index 100% rename from src/arch/m68k/addr_mode.zig rename to src/arch/m68k/address_mode.zig diff --git a/src/arch/m68k/all.zig b/src/arch/m68k/all.zig index 37cac9a..8a6933e 100644 --- a/src/arch/m68k/all.zig +++ b/src/arch/m68k/all.zig @@ -1,5 +1,15 @@ -const Operand = @import("operand.zig").Operand; -const OpSize = @import("op_size.zig").OpSize; +pub const AddressMode = @import("address_mode.zig").AddressMode; +pub const OpMem = @import("op_mem.zig").OpMem; +const opsize = @import("op_size.zig"); +pub const CpuSize = opsize.CpuSize; +pub const FpuSize = opsize.FpuSize; +pub const UnitSize = opsize.UnitSize; +pub const SizeType = opsize.SizeType; +pub const OpSize = opsize.OpSize; +pub const OpType = @import("op_type.zig").OpType; +pub const Register = @import("register.zig").Register; +pub const Instruction = @import("instruction.zig").Instruction; +pub const Operand = @import("operand.zig").Operand; pub const Arch = extern struct { operands: [4]Operand, diff --git a/src/arch/m68k/op_size.zig b/src/arch/m68k/op_size.zig index db2cbb2..4635eb3 100644 --- a/src/arch/m68k/op_size.zig +++ b/src/arch/m68k/op_size.zig @@ -1,25 +1,25 @@ const cs = @import("capstone-c"); -const CpuSize = enum(cs.m68k_cpu_size) { +pub const CpuSize = enum(cs.m68k_cpu_size) { NONE, BYTE, WORD, LONG = 4, }; -const FpuSize = enum(cs.m68k_fpu_size) { +pub const FpuSize = enum(cs.m68k_fpu_size) { NONE, SINGLE = 4, DOUBLE = 8, EXTENDED = 12, }; -const UnitSize = extern union { +pub const UnitSize = extern union { cpu_size: CpuSize, fpu_size: FpuSize, }; -const SizeType = enum(cs.m68k_size_type) { +pub const SizeType = enum(cs.m68k_size_type) { INVALID, CPU, FPU, diff --git a/src/arch/m68k/operand.zig b/src/arch/m68k/operand.zig index 2eccfe4..b723ef1 100644 --- a/src/arch/m68k/operand.zig +++ b/src/arch/m68k/operand.zig @@ -1,7 +1,7 @@ const Instruction = @import("instruction.zig").Instruction; const OpMem = @import("op_mem.zig").OpMem; const OpType = @import("op_type.zig").OpType; -const AddressMode = @import("addr_mode.zig").AddressMode; +const AddressMode = @import("address_mode.zig").AddressMode; const BrDisp = extern struct { disp: i32, disp_size: u8 }; pub const Operand = extern struct { diff --git a/src/arch/mips/all.zig b/src/arch/mips/all.zig index ca5d90c..1cece6e 100644 --- a/src/arch/mips/all.zig +++ b/src/arch/mips/all.zig @@ -1,4 +1,8 @@ -const Operand = @import("operand.zig").Operand; +pub const OpMem = @import("op_mem.zig").OpMem; +pub const OpType = @import("op_type.zig").OpType; +pub const Register = @import("register.zig").Register; +pub const Instruction = @import("instruction.zig").Instruction; +pub const Operand = @import("operand.zig").Operand; pub const Arch = extern struct { op_count: u8, diff --git a/src/arch/mos65xx/all.zig b/src/arch/mos65xx/all.zig index 3c08b9c..491f073 100644 --- a/src/arch/mos65xx/all.zig +++ b/src/arch/mos65xx/all.zig @@ -1,5 +1,8 @@ -const AddressMode = @import("address_mode.zig").AddressMode; -const Operand = @import("operand.zig").Operand; +pub const AddressMode = @import("address_mode.zig").AddressMode; +pub const OpType = @import("op_type.zig").OpType; +pub const Register = @import("register.zig").Register; +pub const Instruction = @import("instruction.zig").Instruction; +pub const Operand = @import("operand.zig").Operand; pub const Arch = extern struct { am: AddressMode, diff --git a/src/arch/ppc/all.zig b/src/arch/ppc/all.zig index 33ecfb6..4b1c72c 100644 --- a/src/arch/ppc/all.zig +++ b/src/arch/ppc/all.zig @@ -1,6 +1,11 @@ -const Bc = @import("bc.zig").Bc; -const Bh = @import("bh.zig").Bh; -const Operand = @import("operand.zig").Operand; +pub const Bc = @import("bc.zig").Bc; +pub const Bh = @import("bh.zig").Bh; +pub const OpMem = @import("op_mem.zig").OpMem; +pub const OpType = @import("op_type.zig").OpType; +pub const Register = @import("register.zig").Register; +pub const OpCrx = @import("op_crx.zig").OpCrx; +pub const Instruction = @import("instruction.zig").Instruction; +pub const Operand = @import("operand.zig").Operand; pub const Arch = extern struct { bc: Bc, diff --git a/src/arch/riscv/all.zig b/src/arch/riscv/all.zig index 3d6cb5e..0aedc3c 100644 --- a/src/arch/riscv/all.zig +++ b/src/arch/riscv/all.zig @@ -1,4 +1,7 @@ -const Operand = @import("operand.zig"); +pub const OpMem = @import("op_mem.zig").OpMem; +pub const OpType = @import("op_type.zig").OpType; +pub const Instruction = @import("instruction.zig").Instruction; +pub const Operand = @import("operand.zig").Operand; pub const Arch = extern struct { need_effective_addr: bool, diff --git a/src/arch/sh/all.zig b/src/arch/sh/all.zig index e7102f7..a0a35f3 100644 --- a/src/arch/sh/all.zig +++ b/src/arch/sh/all.zig @@ -1,5 +1,14 @@ -const Insn = @import("insn.zig").Insn; -const Operand = @import("operand.zig").Operand; +pub const DspCc = @import("dsp_cc.zig").DspCc; +pub const DspInsn = @import("dsp_insn.zig").DspInsn; +pub const DspOperand = @import("dsp_operand.zig").DspOperand; +pub const Insn = @import("insn.zig").Insn; +pub const MemType = @import("mem_type.zig").MemType; +pub const OpMem = @import("op_mem.zig").OpMem; +pub const OpType = @import("op_type.zig").OpType; +pub const Register = @import("register.zig").Register; +pub const OpDsp = @import("op_dsp.zig").OpDsp; +pub const Instruction = @import("instruction.zig").Instruction; +pub const Operand = @import("operand.zig").Operand; pub const Arch = extern struct { insn: Insn, diff --git a/src/arch/sparc/all.zig b/src/arch/sparc/all.zig index 59d8d7d..8e85b84 100644 --- a/src/arch/sparc/all.zig +++ b/src/arch/sparc/all.zig @@ -1,6 +1,10 @@ -const Cc = @import("cc.zig").Cc; -const Hint = @import("hint.zig").Hint; -const Operand = @import("operand.zig").Operand; +pub const Cc = @import("cc.zig").Cc; +pub const OpMem = @import("op_mem.zig").OpMem; +pub const OpType = @import("op_type.zig").OpType; +pub const Register = @import("register.zig").Register; +pub const Instruction = @import("instruction.zig").Instruction; +pub const Hint = @import("hint.zig").Hint; +pub const Operand = @import("operand.zig").Operand; pub const Arch = extern struct { cc: Cc, diff --git a/src/arch/sysz/all.zig b/src/arch/sysz/all.zig index 45e7063..151d451 100644 --- a/src/arch/sysz/all.zig +++ b/src/arch/sysz/all.zig @@ -1,5 +1,9 @@ -const Cc = @import("cc.zig").Cc; -const Operand = @import("operand.zig").Operand; +pub const Cc = @import("cc.zig").Cc; +pub const OpMem = @import("op_mem.zig").OpMem; +pub const OpType = @import("op_type.zig").OpType; +pub const Register = @import("register.zig").Register; +pub const Instruction = @import("instruction.zig").Instruction; +pub const Operand = @import("operand.zig").Operand; pub const Arch = extern struct { cc: Cc, diff --git a/src/arch/tms320c64x/all.zig b/src/arch/tms320c64x/all.zig index 7a5a7b0..0b8bcf3 100644 --- a/src/arch/tms320c64x/all.zig +++ b/src/arch/tms320c64x/all.zig @@ -1,6 +1,9 @@ -const Operand = @import("operand.zig").Operand; -const Condition = @import("condition.zig").Condition; -const Funit = @import("funit.zig").Funit; +pub const OpMem = @import("op_mem.zig").OpMem; +pub const OpType = @import("op_type.zig").OpType; +pub const Instruction = @import("instruction.zig").Instruction; +pub const Operand = @import("operand.zig").Operand; +pub const Condition = @import("condition.zig").Condition; +pub const Funit = @import("funit.zig").Funit; pub const Arch = extern struct { op_count: u8, diff --git a/src/arch/tricore/all.zig b/src/arch/tricore/all.zig index e9ebfc1..9754f76 100644 --- a/src/arch/tricore/all.zig +++ b/src/arch/tricore/all.zig @@ -1,4 +1,7 @@ -const Operand = @import("operand.zig").Operand; +pub const OpMem = @import("op_mem.zig").OpMem; +pub const OpType = @import("op_type.zig").OpType; +pub const Instruction = @import("instruction.zig").Instruction; +pub const Operand = @import("operand.zig").Operand; pub const Arch = extern struct { op_count: u8, diff --git a/src/arch/wasm/all.zig b/src/arch/wasm/all.zig index ea308fa..5802bdf 100644 --- a/src/arch/wasm/all.zig +++ b/src/arch/wasm/all.zig @@ -1,4 +1,7 @@ -const Operand = @import("operand.zig").Operand; +pub const BrTable = @import("brtable.zig").BrTable; +pub const OpType = @import("op_type.zig").OpType; +pub const Instruction = @import("instruction.zig").Instruction; +pub const Operand = @import("operand.zig").Operand; pub const Arch = extern struct { op_count: u8, diff --git a/src/arch/x86/all.zig b/src/arch/x86/all.zig index ebc7d62..24b1d98 100644 --- a/src/arch/x86/all.zig +++ b/src/arch/x86/all.zig @@ -10,14 +10,14 @@ pub const Flags = extern union { fpu_flags: u64, }; -const OpType = enum(cs.x86_op_type) { +pub const OpType = enum(cs.x86_op_type) { INVALID = 0, REG, IMM, MEM, }; -const Memory = extern struct { +pub const Memory = extern struct { segment: reg.Register, base: reg.Register, index: reg.Register, @@ -25,7 +25,7 @@ const Memory = extern struct { disp: i64, }; -const Instruction = extern union { +pub const Instruction = extern union { reg: reg.Register, imm: i64, mem: Memory, diff --git a/src/arch/x86/avx.zig b/src/arch/x86/avx.zig index 29ec210..ae98a62 100644 --- a/src/arch/x86/avx.zig +++ b/src/arch/x86/avx.zig @@ -1,4 +1,4 @@ -pub const cs = @import("capstone-c"); +const cs = @import("capstone-c"); pub const Cc = enum(cs.x86_avx_cc) { INVALID = 0, diff --git a/src/arch/x86/register.zig b/src/arch/x86/register.zig index 4fbf3e4..6e85f00 100644 --- a/src/arch/x86/register.zig +++ b/src/arch/x86/register.zig @@ -1,4 +1,4 @@ -pub const cs = @import("capstone-c"); +const cs = @import("capstone-c"); pub const Register = enum(cs.x86_reg) { INVALID = 0, diff --git a/src/arch/x86/sse.zig b/src/arch/x86/sse.zig index 11480e9..7b92ccb 100644 --- a/src/arch/x86/sse.zig +++ b/src/arch/x86/sse.zig @@ -1,4 +1,4 @@ -pub const cs = @import("capstone-c"); +const cs = @import("capstone-c"); pub const Cc = enum(cs.x86_sse_cc) { INVALID = 0, diff --git a/src/arch/x86/xop.zig b/src/arch/x86/xop.zig index 389d5af..72a9a1e 100644 --- a/src/arch/x86/xop.zig +++ b/src/arch/x86/xop.zig @@ -1,4 +1,4 @@ -pub const cs = @import("capstone-c"); +const cs = @import("capstone-c"); pub const Cc = enum(cs.x86_xop_cc) { INVALID = 0, diff --git a/src/arch/xcore/all.zig b/src/arch/xcore/all.zig index e0f0843..44042eb 100644 --- a/src/arch/xcore/all.zig +++ b/src/arch/xcore/all.zig @@ -1,4 +1,8 @@ -const Operand = @import("operand.zig").Operand; +pub const OpMem = @import("op_mem.zig").OpMem; +pub const OpType = @import("op_type.zig").OpType; +pub const Register = @import("register.zig").Register; +pub const Instruction = @import("instruction.zig").Instruction; +pub const Operand = @import("operand.zig").Operand; pub const Arch = extern struct { op_count: u8, diff --git a/src/impl.zig b/src/impl.zig index 49914d2..ca4969a 100644 --- a/src/impl.zig +++ b/src/impl.zig @@ -10,11 +10,17 @@ pub const Iter = iter.Iter; pub const IterManaged = iter.IterManaged; pub const Handle = cs.csh; -pub fn version(major: ?*c_int, minor: ?*c_int) err.CapstoneError!void { - const maj = major orelse return {}; - const min = minor orelse return {}; - - return err.toError(cs.cs_version(@ptrCast(maj), @ptrCast(min))) orelse return; +const SemanticVersion = @import("std").SemanticVersion; +pub fn version() SemanticVersion { + var major: c_uint = 0; + var minor: c_uint = 0; + + _ = cs.cs_version(@ptrCast(&major), @ptrCast(&minor)); + return SemanticVersion{ + .major = @intCast(major), + .minor = @intCast(minor), + .patch = 0, // Capstone does not provide patch version + }; } pub fn support(query: c_int) bool { @@ -166,7 +172,7 @@ test { @import("std").testing.refAllDecls(@import("setup.zig")); } -test "free" { +test "malloc and free" { var handle = try open(.X86, .@"16"); defer close(&handle) catch {}; const ins = try malloc(handle); diff --git a/src/iter.zig b/src/iter.zig index 99ea678..06ed4eb 100644 --- a/src/iter.zig +++ b/src/iter.zig @@ -13,7 +13,7 @@ pub const Iter = struct { insn: *Insn, // Consumes the iterator and goes to the next - pub fn next(self: *Iter) ?*Insn { + pub fn next(self: *Iter) ?*const Insn { if (cs.cs_disasm_iter(self.handle, @ptrCast(&self.code.ptr), @ptrCast(&self.code.len), &self.address, @ptrCast(self.insn))) { self.insn.normalizeStrings(); return self.insn; @@ -50,7 +50,7 @@ pub const IterManaged = struct { } // Consumes the iterator and goes to the next - pub fn next(self: *IterManaged) ?*Insn { + pub fn next(self: *IterManaged) ?*const Insn { return self.inner.next(); } From 42c10720075208ef112522903cfc39b521ba754b Mon Sep 17 00:00:00 2001 From: IamSanjid Date: Sun, 1 Jun 2025 05:31:41 +0600 Subject: [PATCH 23/24] IterUnmanaged, createInsn, dupeInsn, destroyInsn --- capstone.zig | 5 ++- src/ManagedHandle.zig | 85 +++++++++++++++++++++++++++++++++++++-- src/impl.zig | 93 +++++++++++++++++++++++++++++++++++++++---- src/iter.zig | 46 +++++++++++---------- 4 files changed, 196 insertions(+), 33 deletions(-) diff --git a/capstone.zig b/capstone.zig index 0c38e7b..b6c1ec1 100644 --- a/capstone.zig +++ b/capstone.zig @@ -1,5 +1,5 @@ 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; @@ -15,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; diff --git a/src/ManagedHandle.zig b/src/ManagedHandle.zig index 1bd0a86..62f3017 100644 --- a/src/ManagedHandle.zig +++ b/src/ManagedHandle.zig @@ -10,7 +10,7 @@ const impl = @import("impl.zig"); const iter = @import("iter.zig"); -native: impl.Handle, +const Allocator = std.mem.Allocator; // To avoid heap allocations... const tmp_storage = if (builtin.single_threaded) @@ -65,6 +65,9 @@ pub const Options = struct { }; }; +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); @@ -97,7 +100,7 @@ pub fn init(arch: enums.Arch, mode: enums.Mode, options: Options) !Self { try impl.option(handle, .MNEMONIC, @intFromPtr(options.mnemonic.ptr)); } - return Self{ .native = handle }; + 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). @@ -114,10 +117,41 @@ pub fn disasmAlloc(self: Self, allocator: std.mem.Allocator, code: []const u8, a } /// Uses global/threadlocal storage to avoid heap allocations for the single Insn struct. -pub fn disasmIter(self: Self, code: []const u8, address: u64) iter.Iter { +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 {}; @@ -141,3 +175,48 @@ test "with options" { 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); +} diff --git a/src/impl.zig b/src/impl.zig index ca4969a..d636af7 100644 --- a/src/impl.zig +++ b/src/impl.zig @@ -6,11 +6,13 @@ pub const setup = @import("setup.zig"); pub const enums = @import("enums.zig"); const iter = @import("iter.zig"); -pub const Iter = iter.Iter; +pub const IterUnmanaged = iter.IterUnmanaged; pub const IterManaged = iter.IterManaged; pub const Handle = cs.csh; +const Allocator = @import("std").mem.Allocator; const SemanticVersion = @import("std").SemanticVersion; + pub fn version() SemanticVersion { var major: c_uint = 0; var minor: c_uint = 0; @@ -96,21 +98,58 @@ pub fn malloc(handle: Handle) !*insn.Insn { return if (ins) |i| return i else return error.OutOfMemory; } -/// Same as the normal Variant, but does the allocation for you. -pub fn disasmIterManaged(handle: Handle, code: []const u8, address: u64) !IterManaged { - return IterManaged.init(handle, code, address); +/// Create an Insn object using the provided allocator. +/// The owner takes responsibility of the pointer. +/// Must be destroyed with `destroyInsn`. +pub fn createInsn(allocator: Allocator, detail: bool) !*insn.Insn { + const ins = try allocator.create(insn.Insn); + if (detail) { + ins.detail = try allocator.create(insn.Detail); + } else { + ins.detail = null; + } + return ins; +} + +/// 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(allocator: Allocator, ins: *const insn.Insn) !*insn.Insn { + var new_ins = try createInsn(allocator, ins.detail != null); + const new_detail = new_ins.detail; + new_ins.* = ins.*; + + if (ins.detail) |detail| { + new_detail.?.* = detail.*; + new_ins.detail = new_detail; + } + + return new_ins; +} + +/// Destroy an Insn object created with `createInsn`. +pub fn destroyInsn(allocator: Allocator, ins: *insn.Insn) void { + if (ins.detail) |detail| { + allocator.destroy(detail); + } + allocator.destroy(ins); +} + +/// Same as the normal Variant, but does the allocations using the provided allocator. +pub fn disasmIterManaged(allocator: Allocator, handle: Handle, code: []const u8, address: u64) !IterManaged { + return IterManaged.init(allocator, handle, code, address); } -/// Return an Iter object +/// Return an IterUnmanaged object /// Does not yet consume any element. -pub fn disasmIter(handle: Handle, code: []const u8, address: u64, ins: *insn.Insn) Iter { - return Iter{ +pub fn disasmIter(handle: Handle, code: []const u8, address: u64, ins: *insn.Insn) IterUnmanaged { + return IterUnmanaged{ .handle = handle, .code = code, .original_code = code, .original_address = address, .address = address, - .insn = ins, + .ins = ins, }; } @@ -182,3 +221,41 @@ test "malloc and free" { defer free(disass); try @import("std").testing.expectEqual(1, disass.len); } + +test "disasm iter managed" { + const testing = @import("std").testing; + const allocator = testing.allocator; + + var handle = try open(.X86, .@"64"); + defer close(&handle) catch {}; + + var dis_iter = try disasmIterManaged(allocator, handle, "\x75\x14", 0x1000); + defer dis_iter.deinit(); + + const ins = dis_iter.next().?; + try testing.expect(ins.detail != null); + try testing.expectEqualStrings("jne", ins.mnemonic[0..3]); +} + +test "create, dupe and destroy Insn" { + const testing = @import("std").testing; + const allocator = testing.allocator; + + const ins = try createInsn(allocator, true); + defer destroyInsn(allocator, ins); + + ins.id = 0x1234; + ins.address = 0x1000; + ins.size = 2; + + ins.detail.?.groups_count = 2; + + const duped_ins = try dupeInsn(allocator, ins); + defer 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); +} diff --git a/src/iter.zig b/src/iter.zig index 06ed4eb..8e94bf9 100644 --- a/src/iter.zig +++ b/src/iter.zig @@ -1,65 +1,69 @@ const cs = @import("capstone-c"); +const insn = @import("insn.zig"); -const Insn = @import("insn.zig").Insn; +const Allocator = @import("std").mem.Allocator; const Handle = cs.csh; /// The Iterator for traversing the disassembler -pub const Iter = struct { +pub const IterUnmanaged = struct { handle: Handle, code: []const u8, original_code: []const u8, original_address: u64, address: u64, - insn: *Insn, + ins: *insn.Insn, // Consumes the iterator and goes to the next - pub fn next(self: *Iter) ?*const Insn { - if (cs.cs_disasm_iter(self.handle, @ptrCast(&self.code.ptr), @ptrCast(&self.code.len), &self.address, @ptrCast(self.insn))) { - self.insn.normalizeStrings(); - return self.insn; + pub fn next(self: *IterUnmanaged) ?*const insn.Insn { + if (cs.cs_disasm_iter(self.handle, @ptrCast(&self.code.ptr), @ptrCast(&self.code.len), &self.address, @ptrCast(self.ins))) { + self.ins.normalizeStrings(); + return self.ins; } else { return null; } } - pub fn reset(self: *Iter) void { + pub fn reset(self: *IterUnmanaged) void { self.address = self.original_address; self.code = self.original_code; } }; -/// The Iterator for traversing the disassembler, but **allocates** space using capstone malloc. +/// The Iterator for traversing the disassembler, but **allocates** space using the provided `std.mem.Allocator`. pub const IterManaged = struct { - inner: Iter, - insn: *Insn, + allocator: Allocator, + unmanaged: IterUnmanaged, + ins: *insn.Insn, - pub fn init(handle: Handle, code: []const u8, address: u64) !IterManaged { - const insn_ptr: ?*Insn = @ptrCast(cs.cs_malloc(handle)); - const insn = if (insn_ptr) |i| i else return error.OutOfMemory; + pub fn init(allocator: Allocator, handle: Handle, code: []const u8, address: u64) !IterManaged { + const ins = try allocator.create(insn.Insn); + ins.detail = try allocator.create(insn.Detail); return .{ - .inner = Iter{ + .allocator = allocator, + .unmanaged = IterUnmanaged{ .handle = handle, .code = code, .original_code = code, .original_address = address, .address = address, - .insn = insn, + .ins = ins, }, - .insn = insn, + .ins = ins, }; } // Consumes the iterator and goes to the next - pub fn next(self: *IterManaged) ?*const Insn { - return self.inner.next(); + pub fn next(self: *IterManaged) ?*const insn.Insn { + return self.unmanaged.next(); } pub fn reset(self: *IterManaged) void { - self.inner.reset(); + self.unmanaged.reset(); } // Clean up the iter pub fn deinit(self: IterManaged) void { - cs.cs_free(@ptrCast(self.insn), 1); + self.allocator.destroy(self.ins.detail.?); + self.allocator.destroy(self.ins); } }; From ef38def5b0152ac90f121257af362f464130854d Mon Sep 17 00:00:00 2001 From: SoraTenshi Date: Sun, 1 Jun 2025 03:17:18 +0200 Subject: [PATCH 24/24] update ci to 0.14.1 --- .github/workflows/ci.yaml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index d3da778..52b0005 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -12,8 +12,10 @@ jobs: lint: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: goto-bus-stop/setup-zig@v2 + - uses: actions/checkout@v3 + - uses: mlugg/setup-zig@v2 + with: + version: 0.14.1 - run: zig fmt --check *.zig test: @@ -23,6 +25,8 @@ jobs: os: [ubuntu-latest, macos-latest] runs-on: ${{matrix.os}} steps: - - uses: actions/checkout@v2 - - uses: goto-bus-stop/setup-zig@v2 + - uses: actions/checkout@v3 + - uses: mlugg/setup-zig@v2 + with: + version: 0.14.1 - run: zig build test