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 init() InstructionFormatter { pub fn formatBytes(bytes: []const u8) []u8 {
var formatter: zydis.ZydisFormatter = undefined; return formatInstruction(disassembleInstruction(bytes));
const status = zydis.ZydisFormatterInit(&formatter, zydis.ZYDIS_FORMATTER_STYLE_ATT);
if (!zydis.ZYAN_SUCCESS(status)) @panic("Zydis formatter init failed");
return .{
.formatter = formatter,
};
} }
pub fn format( /// Format the given instruction into the buffer.
formatter: *const InstructionFormatter, /// This function is not threadsafe.
instruction: BundledInstruction, pub fn formatInstruction(instruction: BundledInstruction) []u8 {
buffer: []u8, // Static variable to initialize the formatter only once and have a valid address for the
) []u8 { // buffer.
const static = struct {
var initialized = false;
var formatter: zydis.ZydisFormatter = undefined;
var buffer: [256]u8 = undefined;
};
if (!static.initialized) {
const status = zydis.ZydisFormatterInit(&static.formatter, zydis.ZYDIS_FORMATTER_STYLE_ATT);
if (!zydis.ZYAN_SUCCESS(status)) @panic("Zydis formatter init failed");
}
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 {
// Static variable to initialize the decoder only once and have a valid address for the
// instruction and operands.
const static = struct {
var initialized = false;
var decoder: zydis.ZydisDecoder = undefined;
var instruction: zydis.ZydisDecodedInstruction = undefined;
var operands: [zydis.ZYDIS_MAX_OPERAND_COUNT]zydis.ZydisDecodedOperand = undefined;
};
if (!static.initialized) {
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,
}; };
/// Disassemble `bytes` and format them into the given buffer. Useful for error reporting or
/// debugging purposes.
/// This function should not be called in a tight loop as it's intentionally inefficient due tue
/// having a simple API.
pub fn formatBytes(bytes: []const u8, buffer: []u8) []u8 {
var iter = InstructionIterator.init(bytes);
const instr = iter.next() orelse return buffer[0..0];
const formatter = InstructionFormatter.init();
return formatter.format(instr, buffer);
} }