simplify one-off disassembly and formatting

This commit is contained in:
2025-11-21 23:58:17 +01:00
parent 16bc59de41
commit 393289b154

View File

@@ -3,6 +3,7 @@ const mem = std.mem;
const zydis = @import("zydis").zydis; const zydis = @import("zydis").zydis;
const log = std.log.scoped(.disassembler); const log = std.log.scoped(.disassembler);
const assert = std.debug.assert;
pub const InstructionIterator = struct { pub const InstructionIterator = struct {
decoder: zydis.ZydisDecoder, decoder: zydis.ZydisDecoder,
@@ -86,49 +87,72 @@ pub const BundledInstruction = struct {
operands: []const zydis.ZydisDecodedOperand, operands: []const zydis.ZydisDecodedOperand,
}; };
pub const InstructionFormatter = struct { /// Disassemble `bytes` and format them into the given buffer. Useful for error reporting or
formatter: zydis.ZydisFormatter, /// debugging purposes.
/// This function is not threadsafe.
pub fn formatBytes(bytes: []const u8) []u8 {
return formatInstruction(disassembleInstruction(bytes));
}
pub fn init() InstructionFormatter { /// Format the given instruction into the buffer.
/// This function is not threadsafe.
pub fn formatInstruction(instruction: BundledInstruction) []u8 {
// Static variable to initialize the formatter only once and have a valid address for the
// buffer.
const static = struct {
var initialized = false;
var formatter: zydis.ZydisFormatter = undefined; var formatter: zydis.ZydisFormatter = undefined;
const status = zydis.ZydisFormatterInit(&formatter, zydis.ZYDIS_FORMATTER_STYLE_ATT); var buffer: [256]u8 = undefined;
if (!zydis.ZYAN_SUCCESS(status)) @panic("Zydis formatter init failed");
return .{
.formatter = formatter,
}; };
if (!static.initialized) {
const status = zydis.ZydisFormatterInit(&static.formatter, zydis.ZYDIS_FORMATTER_STYLE_ATT);
if (!zydis.ZYAN_SUCCESS(status)) @panic("Zydis formatter init failed");
} }
pub fn format(
formatter: *const InstructionFormatter,
instruction: BundledInstruction,
buffer: []u8,
) []u8 {
const status = zydis.ZydisFormatterFormatInstruction( const status = zydis.ZydisFormatterFormatInstruction(
&formatter.formatter, &static.formatter,
instruction.instruction, instruction.instruction,
instruction.operands.ptr, instruction.operands.ptr,
instruction.instruction.operand_count_visible, instruction.instruction.operand_count_visible,
buffer.ptr, &static.buffer,
buffer.len, static.buffer.len,
instruction.address, instruction.address,
null, null,
); );
if (!zydis.ZYAN_SUCCESS(status)) { assert(zydis.ZYAN_SUCCESS(status)); // TODO: handle
@panic("wow"); return mem.sliceTo(&static.buffer, 0);
} }
return mem.sliceTo(buffer, 0);
} /// Disassemble the first instruction at bytes.
}; /// This function is not threadsafe.
pub fn disassembleInstruction(bytes: []const u8) BundledInstruction {
/// Disassemble `bytes` and format them into the given buffer. Useful for error reporting or // Static variable to initialize the decoder only once and have a valid address for the
/// debugging purposes. // instruction and operands.
/// This function should not be called in a tight loop as it's intentionally inefficient due tue const static = struct {
/// having a simple API. var initialized = false;
pub fn formatBytes(bytes: []const u8, buffer: []u8) []u8 { var decoder: zydis.ZydisDecoder = undefined;
var iter = InstructionIterator.init(bytes); var instruction: zydis.ZydisDecodedInstruction = undefined;
var operands: [zydis.ZYDIS_MAX_OPERAND_COUNT]zydis.ZydisDecodedOperand = undefined;
const instr = iter.next() orelse return buffer[0..0]; };
const formatter = InstructionFormatter.init(); if (!static.initialized) {
return formatter.format(instr, buffer); const status = zydis.ZydisDecoderInit(
&static.decoder,
zydis.ZYDIS_MACHINE_MODE_LONG_64,
zydis.ZYDIS_STACK_WIDTH_64,
);
if (!zydis.ZYAN_SUCCESS(status)) @panic("Zydis decoder init failed");
static.initialized = true;
}
const status = zydis.ZydisDecoderDecodeFull(
&static.decoder,
bytes.ptr,
bytes.len,
&static.instruction,
&static.operands,
);
assert(zydis.ZYAN_SUCCESS(status)); // TODO: handle
return .{
.address = @intFromPtr(bytes.ptr),
.instruction = &static.instruction,
.operands = &static.operands,
};
} }