|
|
|
|
@@ -536,59 +536,62 @@ pub const Instruction = struct {
|
|
|
|
|
}
|
|
|
|
|
try encoder.disp32(sib.disp);
|
|
|
|
|
},
|
|
|
|
|
.reg => |base| if (base.class() == .segment) {
|
|
|
|
|
// TODO audit this wrt SIB
|
|
|
|
|
try encoder.modRm_SIBDisp0(operand_enc);
|
|
|
|
|
if (mem.scaleIndex()) |si| {
|
|
|
|
|
const scale = math.log2_int(u4, si.scale);
|
|
|
|
|
try encoder.sib_scaleIndexDisp32(scale, si.index.lowEnc());
|
|
|
|
|
} else {
|
|
|
|
|
try encoder.sib_disp32();
|
|
|
|
|
}
|
|
|
|
|
try encoder.disp32(sib.disp);
|
|
|
|
|
} else {
|
|
|
|
|
assert(base.class() == .general_purpose);
|
|
|
|
|
const dst = base.lowEnc();
|
|
|
|
|
const src = operand_enc;
|
|
|
|
|
if (dst == 4 or mem.scaleIndex() != null) {
|
|
|
|
|
if (sib.disp == 0 and dst != 5) {
|
|
|
|
|
try encoder.modRm_SIBDisp0(src);
|
|
|
|
|
if (mem.scaleIndex()) |si| {
|
|
|
|
|
const scale = math.log2_int(u4, si.scale);
|
|
|
|
|
try encoder.sib_scaleIndexBase(scale, si.index.lowEnc(), dst);
|
|
|
|
|
} else {
|
|
|
|
|
try encoder.sib_base(dst);
|
|
|
|
|
}
|
|
|
|
|
} else if (math.cast(i8, sib.disp)) |_| {
|
|
|
|
|
try encoder.modRm_SIBDisp8(src);
|
|
|
|
|
if (mem.scaleIndex()) |si| {
|
|
|
|
|
const scale = math.log2_int(u4, si.scale);
|
|
|
|
|
try encoder.sib_scaleIndexBaseDisp8(scale, si.index.lowEnc(), dst);
|
|
|
|
|
} else {
|
|
|
|
|
try encoder.sib_baseDisp8(dst);
|
|
|
|
|
}
|
|
|
|
|
try encoder.disp8(@as(i8, @truncate(sib.disp)));
|
|
|
|
|
.reg => |base| switch (base.class()) {
|
|
|
|
|
.segment => {
|
|
|
|
|
// TODO audit this wrt SIB
|
|
|
|
|
try encoder.modRm_SIBDisp0(operand_enc);
|
|
|
|
|
if (mem.scaleIndex()) |si| {
|
|
|
|
|
const scale = math.log2_int(u4, si.scale);
|
|
|
|
|
try encoder.sib_scaleIndexDisp32(scale, si.index.lowEnc());
|
|
|
|
|
} else {
|
|
|
|
|
try encoder.modRm_SIBDisp32(src);
|
|
|
|
|
if (mem.scaleIndex()) |si| {
|
|
|
|
|
const scale = math.log2_int(u4, si.scale);
|
|
|
|
|
try encoder.sib_scaleIndexBaseDisp32(scale, si.index.lowEnc(), dst);
|
|
|
|
|
try encoder.sib_disp32();
|
|
|
|
|
}
|
|
|
|
|
try encoder.disp32(sib.disp);
|
|
|
|
|
},
|
|
|
|
|
.general_purpose => {
|
|
|
|
|
const dst = base.lowEnc();
|
|
|
|
|
const src = operand_enc;
|
|
|
|
|
if (dst == 4 or mem.scaleIndex() != null) {
|
|
|
|
|
if (sib.disp == 0 and dst != 5) {
|
|
|
|
|
try encoder.modRm_SIBDisp0(src);
|
|
|
|
|
if (mem.scaleIndex()) |si| {
|
|
|
|
|
const scale = math.log2_int(u4, si.scale);
|
|
|
|
|
try encoder.sib_scaleIndexBase(scale, si.index.lowEnc(), dst);
|
|
|
|
|
} else {
|
|
|
|
|
try encoder.sib_base(dst);
|
|
|
|
|
}
|
|
|
|
|
} else if (math.cast(i8, sib.disp)) |_| {
|
|
|
|
|
try encoder.modRm_SIBDisp8(src);
|
|
|
|
|
if (mem.scaleIndex()) |si| {
|
|
|
|
|
const scale = math.log2_int(u4, si.scale);
|
|
|
|
|
try encoder.sib_scaleIndexBaseDisp8(scale, si.index.lowEnc(), dst);
|
|
|
|
|
} else {
|
|
|
|
|
try encoder.sib_baseDisp8(dst);
|
|
|
|
|
}
|
|
|
|
|
try encoder.disp8(@as(i8, @truncate(sib.disp)));
|
|
|
|
|
} else {
|
|
|
|
|
try encoder.sib_baseDisp32(dst);
|
|
|
|
|
try encoder.modRm_SIBDisp32(src);
|
|
|
|
|
if (mem.scaleIndex()) |si| {
|
|
|
|
|
const scale = math.log2_int(u4, si.scale);
|
|
|
|
|
try encoder.sib_scaleIndexBaseDisp32(scale, si.index.lowEnc(), dst);
|
|
|
|
|
} else {
|
|
|
|
|
try encoder.sib_baseDisp32(dst);
|
|
|
|
|
}
|
|
|
|
|
try encoder.disp32(sib.disp);
|
|
|
|
|
}
|
|
|
|
|
try encoder.disp32(sib.disp);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (sib.disp == 0 and dst != 5) {
|
|
|
|
|
try encoder.modRm_indirectDisp0(src, dst);
|
|
|
|
|
} else if (math.cast(i8, sib.disp)) |_| {
|
|
|
|
|
try encoder.modRm_indirectDisp8(src, dst);
|
|
|
|
|
try encoder.disp8(@as(i8, @truncate(sib.disp)));
|
|
|
|
|
} else {
|
|
|
|
|
try encoder.modRm_indirectDisp32(src, dst);
|
|
|
|
|
try encoder.disp32(sib.disp);
|
|
|
|
|
if (sib.disp == 0 and dst != 5) {
|
|
|
|
|
try encoder.modRm_indirectDisp0(src, dst);
|
|
|
|
|
} else if (math.cast(i8, sib.disp)) |_| {
|
|
|
|
|
try encoder.modRm_indirectDisp8(src, dst);
|
|
|
|
|
try encoder.disp8(@as(i8, @truncate(sib.disp)));
|
|
|
|
|
} else {
|
|
|
|
|
try encoder.modRm_indirectDisp32(src, dst);
|
|
|
|
|
try encoder.disp32(sib.disp);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
else => unreachable,
|
|
|
|
|
},
|
|
|
|
|
.frame => if (@TypeOf(encoder).options.allow_frame_locs) {
|
|
|
|
|
try encoder.modRm_indirectDisp32(operand_enc, undefined);
|
|
|
|
|
@@ -1101,7 +1104,7 @@ const TestEncode = struct {
|
|
|
|
|
var stream = std.io.fixedBufferStream(&enc.buffer);
|
|
|
|
|
var count_writer = std.io.countingWriter(stream.writer());
|
|
|
|
|
const inst = try Instruction.new(.none, mnemonic, ops);
|
|
|
|
|
try inst.encode(count_writer.writer());
|
|
|
|
|
try inst.encode(count_writer.writer(), .{});
|
|
|
|
|
enc.index = count_writer.bytes_written;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -1118,7 +1121,7 @@ test "encode" {
|
|
|
|
|
.{ .reg = .rbx },
|
|
|
|
|
.{ .imm = Immediate.u(4) },
|
|
|
|
|
});
|
|
|
|
|
try inst.encode(buf.writer());
|
|
|
|
|
try inst.encode(buf.writer(), .{});
|
|
|
|
|
try testing.expectEqualSlices(u8, &.{ 0x48, 0xc7, 0xc3, 0x4, 0x0, 0x0, 0x0 }, buf.items);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -1181,7 +1184,7 @@ test "lower MI encoding" {
|
|
|
|
|
try expectEqualHexStrings("\x49\xC7\xC4\x00\x10\x00\x00", enc.code(), "mov r12, 0x1000");
|
|
|
|
|
|
|
|
|
|
try enc.encode(.mov, &.{
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.byte, .{ .base = .r12 }) },
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.byte, .{ .base = .{ .reg = .r12 } }) },
|
|
|
|
|
.{ .imm = Immediate.u(0x10) },
|
|
|
|
|
});
|
|
|
|
|
try expectEqualHexStrings("\x41\xC6\x04\x24\x10", enc.code(), "mov BYTE PTR [r12], 0x10");
|
|
|
|
|
@@ -1205,7 +1208,7 @@ test "lower MI encoding" {
|
|
|
|
|
try expectEqualHexStrings("\x48\xc7\xc0\x10\x00\x00\x00", enc.code(), "mov rax, 0x10");
|
|
|
|
|
|
|
|
|
|
try enc.encode(.mov, &.{
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.dword, .{ .base = .r11 }) },
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.dword, .{ .base = .{ .reg = .r11 } }) },
|
|
|
|
|
.{ .imm = Immediate.u(0x10) },
|
|
|
|
|
});
|
|
|
|
|
try expectEqualHexStrings("\x41\xc7\x03\x10\x00\x00\x00", enc.code(), "mov DWORD PTR [r11], 0x10");
|
|
|
|
|
@@ -1221,26 +1224,26 @@ test "lower MI encoding" {
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
try enc.encode(.mov, &.{
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.qword, .{ .base = .rbp, .disp = -8 }) },
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .rbp }, .disp = -8 }) },
|
|
|
|
|
.{ .imm = Immediate.u(0x10) },
|
|
|
|
|
});
|
|
|
|
|
try expectEqualHexStrings("\x48\xc7\x45\xf8\x10\x00\x00\x00", enc.code(), "mov QWORD PTR [rbp - 8], 0x10");
|
|
|
|
|
|
|
|
|
|
try enc.encode(.mov, &.{
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.word, .{ .base = .rbp, .disp = -2 }) },
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.word, .{ .base = .{ .reg = .rbp }, .disp = -2 }) },
|
|
|
|
|
.{ .imm = Immediate.s(-16) },
|
|
|
|
|
});
|
|
|
|
|
try expectEqualHexStrings("\x66\xC7\x45\xFE\xF0\xFF", enc.code(), "mov WORD PTR [rbp - 2], -16");
|
|
|
|
|
|
|
|
|
|
try enc.encode(.mov, &.{
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.byte, .{ .base = .rbp, .disp = -1 }) },
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.byte, .{ .base = .{ .reg = .rbp }, .disp = -1 }) },
|
|
|
|
|
.{ .imm = Immediate.u(0x10) },
|
|
|
|
|
});
|
|
|
|
|
try expectEqualHexStrings("\xC6\x45\xFF\x10", enc.code(), "mov BYTE PTR [rbp - 1], 0x10");
|
|
|
|
|
|
|
|
|
|
try enc.encode(.mov, &.{
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.qword, .{
|
|
|
|
|
.base = .ds,
|
|
|
|
|
.base = .{ .reg = .ds },
|
|
|
|
|
.disp = 0x10000000,
|
|
|
|
|
.scale_index = .{ .scale = 2, .index = .rcx },
|
|
|
|
|
}) },
|
|
|
|
|
@@ -1253,7 +1256,7 @@ test "lower MI encoding" {
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
try enc.encode(.adc, &.{
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.byte, .{ .base = .rbp, .disp = -0x10 }) },
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.byte, .{ .base = .{ .reg = .rbp }, .disp = -0x10 }) },
|
|
|
|
|
.{ .imm = Immediate.u(0x10) },
|
|
|
|
|
});
|
|
|
|
|
try expectEqualHexStrings("\x80\x55\xF0\x10", enc.code(), "adc BYTE PTR [rbp - 0x10], 0x10");
|
|
|
|
|
@@ -1271,7 +1274,7 @@ test "lower MI encoding" {
|
|
|
|
|
try expectEqualHexStrings("\x48\x83\xD0\x10", enc.code(), "adc rax, 0x10");
|
|
|
|
|
|
|
|
|
|
try enc.encode(.add, &.{
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.dword, .{ .base = .rdx, .disp = -8 }) },
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.dword, .{ .base = .{ .reg = .rdx }, .disp = -8 }) },
|
|
|
|
|
.{ .imm = Immediate.u(0x10) },
|
|
|
|
|
});
|
|
|
|
|
try expectEqualHexStrings("\x83\x42\xF8\x10", enc.code(), "add DWORD PTR [rdx - 8], 0x10");
|
|
|
|
|
@@ -1283,13 +1286,13 @@ test "lower MI encoding" {
|
|
|
|
|
try expectEqualHexStrings("\x48\x83\xC0\x10", enc.code(), "add rax, 0x10");
|
|
|
|
|
|
|
|
|
|
try enc.encode(.add, &.{
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.qword, .{ .base = .rbp, .disp = -0x10 }) },
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .rbp }, .disp = -0x10 }) },
|
|
|
|
|
.{ .imm = Immediate.s(-0x10) },
|
|
|
|
|
});
|
|
|
|
|
try expectEqualHexStrings("\x48\x83\x45\xF0\xF0", enc.code(), "add QWORD PTR [rbp - 0x10], -0x10");
|
|
|
|
|
|
|
|
|
|
try enc.encode(.@"and", &.{
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.dword, .{ .base = .ds, .disp = 0x10000000 }) },
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.dword, .{ .base = .{ .reg = .ds }, .disp = 0x10000000 }) },
|
|
|
|
|
.{ .imm = Immediate.u(0x10) },
|
|
|
|
|
});
|
|
|
|
|
try expectEqualHexStrings(
|
|
|
|
|
@@ -1299,7 +1302,7 @@ test "lower MI encoding" {
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
try enc.encode(.@"and", &.{
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.dword, .{ .base = .es, .disp = 0x10000000 }) },
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.dword, .{ .base = .{ .reg = .es }, .disp = 0x10000000 }) },
|
|
|
|
|
.{ .imm = Immediate.u(0x10) },
|
|
|
|
|
});
|
|
|
|
|
try expectEqualHexStrings(
|
|
|
|
|
@@ -1309,7 +1312,7 @@ test "lower MI encoding" {
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
try enc.encode(.@"and", &.{
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.dword, .{ .base = .r12, .disp = 0x10000000 }) },
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.dword, .{ .base = .{ .reg = .r12 }, .disp = 0x10000000 }) },
|
|
|
|
|
.{ .imm = Immediate.u(0x10) },
|
|
|
|
|
});
|
|
|
|
|
try expectEqualHexStrings(
|
|
|
|
|
@@ -1319,7 +1322,7 @@ test "lower MI encoding" {
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
try enc.encode(.sub, &.{
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.dword, .{ .base = .r11, .disp = 0x10000000 }) },
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.dword, .{ .base = .{ .reg = .r11 }, .disp = 0x10000000 }) },
|
|
|
|
|
.{ .imm = Immediate.u(0x10) },
|
|
|
|
|
});
|
|
|
|
|
try expectEqualHexStrings(
|
|
|
|
|
@@ -1334,26 +1337,26 @@ test "lower RM encoding" {
|
|
|
|
|
|
|
|
|
|
try enc.encode(.mov, &.{
|
|
|
|
|
.{ .reg = .rax },
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.qword, .{ .base = .r11 }) },
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .r11 } }) },
|
|
|
|
|
});
|
|
|
|
|
try expectEqualHexStrings("\x49\x8b\x03", enc.code(), "mov rax, QWORD PTR [r11]");
|
|
|
|
|
|
|
|
|
|
try enc.encode(.mov, &.{
|
|
|
|
|
.{ .reg = .rbx },
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.qword, .{ .base = .ds, .disp = 0x10 }) },
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .ds }, .disp = 0x10 }) },
|
|
|
|
|
});
|
|
|
|
|
try expectEqualHexStrings("\x48\x8B\x1C\x25\x10\x00\x00\x00", enc.code(), "mov rbx, QWORD PTR ds:0x10");
|
|
|
|
|
|
|
|
|
|
try enc.encode(.mov, &.{
|
|
|
|
|
.{ .reg = .rax },
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.qword, .{ .base = .rbp, .disp = -4 }) },
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .rbp }, .disp = -4 }) },
|
|
|
|
|
});
|
|
|
|
|
try expectEqualHexStrings("\x48\x8B\x45\xFC", enc.code(), "mov rax, QWORD PTR [rbp - 4]");
|
|
|
|
|
|
|
|
|
|
try enc.encode(.mov, &.{
|
|
|
|
|
.{ .reg = .rax },
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.qword, .{
|
|
|
|
|
.base = .rbp,
|
|
|
|
|
.base = .{ .reg = .rbp },
|
|
|
|
|
.scale_index = .{ .scale = 1, .index = .rcx },
|
|
|
|
|
.disp = -8,
|
|
|
|
|
}) },
|
|
|
|
|
@@ -1363,7 +1366,7 @@ test "lower RM encoding" {
|
|
|
|
|
try enc.encode(.mov, &.{
|
|
|
|
|
.{ .reg = .eax },
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.dword, .{
|
|
|
|
|
.base = .rbp,
|
|
|
|
|
.base = .{ .reg = .rbp },
|
|
|
|
|
.scale_index = .{ .scale = 4, .index = .rdx },
|
|
|
|
|
.disp = -4,
|
|
|
|
|
}) },
|
|
|
|
|
@@ -1373,7 +1376,7 @@ test "lower RM encoding" {
|
|
|
|
|
try enc.encode(.mov, &.{
|
|
|
|
|
.{ .reg = .rax },
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.qword, .{
|
|
|
|
|
.base = .rbp,
|
|
|
|
|
.base = .{ .reg = .rbp },
|
|
|
|
|
.scale_index = .{ .scale = 8, .index = .rcx },
|
|
|
|
|
.disp = -8,
|
|
|
|
|
}) },
|
|
|
|
|
@@ -1383,7 +1386,7 @@ test "lower RM encoding" {
|
|
|
|
|
try enc.encode(.mov, &.{
|
|
|
|
|
.{ .reg = .r8b },
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.byte, .{
|
|
|
|
|
.base = .rsi,
|
|
|
|
|
.base = .{ .reg = .rsi },
|
|
|
|
|
.scale_index = .{ .scale = 1, .index = .rcx },
|
|
|
|
|
.disp = -24,
|
|
|
|
|
}) },
|
|
|
|
|
@@ -1398,10 +1401,10 @@ test "lower RM encoding" {
|
|
|
|
|
try expectEqualHexStrings("\x48\x8C\xC8", enc.code(), "mov rax, cs");
|
|
|
|
|
|
|
|
|
|
try enc.encode(.mov, &.{
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.qword, .{ .base = .rbp, .disp = -16 }) },
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.word, .{ .base = .{ .reg = .rbp }, .disp = -16 }) },
|
|
|
|
|
.{ .reg = .fs },
|
|
|
|
|
});
|
|
|
|
|
try expectEqualHexStrings("\x48\x8C\x65\xF0", enc.code(), "mov QWORD PTR [rbp - 16], fs");
|
|
|
|
|
try expectEqualHexStrings("\x8C\x65\xF0", enc.code(), "mov WORD PTR [rbp - 16], fs");
|
|
|
|
|
|
|
|
|
|
try enc.encode(.mov, &.{
|
|
|
|
|
.{ .reg = .r12w },
|
|
|
|
|
@@ -1409,12 +1412,6 @@ test "lower RM encoding" {
|
|
|
|
|
});
|
|
|
|
|
try expectEqualHexStrings("\x66\x41\x8C\xCC", enc.code(), "mov r12w, cs");
|
|
|
|
|
|
|
|
|
|
try enc.encode(.mov, &.{
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.word, .{ .base = .rbp, .disp = -16 }) },
|
|
|
|
|
.{ .reg = .fs },
|
|
|
|
|
});
|
|
|
|
|
try expectEqualHexStrings("\x66\x8C\x65\xF0", enc.code(), "mov WORD PTR [rbp - 16], fs");
|
|
|
|
|
|
|
|
|
|
try enc.encode(.movsx, &.{
|
|
|
|
|
.{ .reg = .eax },
|
|
|
|
|
.{ .reg = .bx },
|
|
|
|
|
@@ -1435,7 +1432,7 @@ test "lower RM encoding" {
|
|
|
|
|
|
|
|
|
|
try enc.encode(.movsx, &.{
|
|
|
|
|
.{ .reg = .eax },
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.word, .{ .base = .rbp }) },
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.word, .{ .base = .{ .reg = .rbp } }) },
|
|
|
|
|
});
|
|
|
|
|
try expectEqualHexStrings("\x0F\xBF\x45\x00", enc.code(), "movsx eax, BYTE PTR [rbp]");
|
|
|
|
|
|
|
|
|
|
@@ -1496,7 +1493,7 @@ test "lower RM encoding" {
|
|
|
|
|
try enc.encode(.lea, &.{
|
|
|
|
|
.{ .reg = .rsi },
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.qword, .{
|
|
|
|
|
.base = .rbp,
|
|
|
|
|
.base = .{ .reg = .rbp },
|
|
|
|
|
.scale_index = .{ .scale = 1, .index = .rcx },
|
|
|
|
|
}) },
|
|
|
|
|
});
|
|
|
|
|
@@ -1504,31 +1501,31 @@ test "lower RM encoding" {
|
|
|
|
|
|
|
|
|
|
try enc.encode(.add, &.{
|
|
|
|
|
.{ .reg = .r11 },
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.qword, .{ .base = .ds, .disp = 0x10000000 }) },
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .ds }, .disp = 0x10000000 }) },
|
|
|
|
|
});
|
|
|
|
|
try expectEqualHexStrings("\x4C\x03\x1C\x25\x00\x00\x00\x10", enc.code(), "add r11, QWORD PTR ds:0x10000000");
|
|
|
|
|
|
|
|
|
|
try enc.encode(.add, &.{
|
|
|
|
|
.{ .reg = .r12b },
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.byte, .{ .base = .ds, .disp = 0x10000000 }) },
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.byte, .{ .base = .{ .reg = .ds }, .disp = 0x10000000 }) },
|
|
|
|
|
});
|
|
|
|
|
try expectEqualHexStrings("\x44\x02\x24\x25\x00\x00\x00\x10", enc.code(), "add r11b, BYTE PTR ds:0x10000000");
|
|
|
|
|
|
|
|
|
|
try enc.encode(.add, &.{
|
|
|
|
|
.{ .reg = .r12b },
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.byte, .{ .base = .fs, .disp = 0x10000000 }) },
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.byte, .{ .base = .{ .reg = .fs }, .disp = 0x10000000 }) },
|
|
|
|
|
});
|
|
|
|
|
try expectEqualHexStrings("\x64\x44\x02\x24\x25\x00\x00\x00\x10", enc.code(), "add r11b, BYTE PTR fs:0x10000000");
|
|
|
|
|
|
|
|
|
|
try enc.encode(.sub, &.{
|
|
|
|
|
.{ .reg = .r11 },
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.qword, .{ .base = .r13, .disp = 0x10000000 }) },
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .r13 }, .disp = 0x10000000 }) },
|
|
|
|
|
});
|
|
|
|
|
try expectEqualHexStrings("\x4D\x2B\x9D\x00\x00\x00\x10", enc.code(), "sub r11, QWORD PTR [r13 + 0x10000000]");
|
|
|
|
|
|
|
|
|
|
try enc.encode(.sub, &.{
|
|
|
|
|
.{ .reg = .r11 },
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.qword, .{ .base = .r12, .disp = 0x10000000 }) },
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .r12 }, .disp = 0x10000000 }) },
|
|
|
|
|
});
|
|
|
|
|
try expectEqualHexStrings("\x4D\x2B\x9C\x24\x00\x00\x00\x10", enc.code(), "sub r11, QWORD PTR [r12 + 0x10000000]");
|
|
|
|
|
|
|
|
|
|
@@ -1562,7 +1559,7 @@ test "lower RMI encoding" {
|
|
|
|
|
|
|
|
|
|
try enc.encode(.imul, &.{
|
|
|
|
|
.{ .reg = .bx },
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.word, .{ .base = .rbp, .disp = -16 }) },
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.word, .{ .base = .{ .reg = .rbp }, .disp = -16 }) },
|
|
|
|
|
.{ .imm = Immediate.s(-1024) },
|
|
|
|
|
});
|
|
|
|
|
try expectEqualHexStrings(
|
|
|
|
|
@@ -1573,7 +1570,7 @@ test "lower RMI encoding" {
|
|
|
|
|
|
|
|
|
|
try enc.encode(.imul, &.{
|
|
|
|
|
.{ .reg = .bx },
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.word, .{ .base = .rbp, .disp = -16 }) },
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.word, .{ .base = .{ .reg = .rbp }, .disp = -16 }) },
|
|
|
|
|
.{ .imm = Immediate.u(1024) },
|
|
|
|
|
});
|
|
|
|
|
try expectEqualHexStrings(
|
|
|
|
|
@@ -1593,7 +1590,7 @@ test "lower MR encoding" {
|
|
|
|
|
try expectEqualHexStrings("\x48\x89\xD8", enc.code(), "mov rax, rbx");
|
|
|
|
|
|
|
|
|
|
try enc.encode(.mov, &.{
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.qword, .{ .base = .rbp, .disp = -4 }) },
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .rbp }, .disp = -4 }) },
|
|
|
|
|
.{ .reg = .r11 },
|
|
|
|
|
});
|
|
|
|
|
try expectEqualHexStrings("\x4c\x89\x5d\xfc", enc.code(), "mov QWORD PTR [rbp - 4], r11");
|
|
|
|
|
@@ -1606,7 +1603,7 @@ test "lower MR encoding" {
|
|
|
|
|
|
|
|
|
|
try enc.encode(.mov, &.{
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.qword, .{
|
|
|
|
|
.base = .r11,
|
|
|
|
|
.base = .{ .reg = .r11 },
|
|
|
|
|
.scale_index = .{ .scale = 2, .index = .r12 },
|
|
|
|
|
.disp = 0x10,
|
|
|
|
|
}) },
|
|
|
|
|
@@ -1622,7 +1619,7 @@ test "lower MR encoding" {
|
|
|
|
|
|
|
|
|
|
try enc.encode(.mov, &.{
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.byte, .{
|
|
|
|
|
.base = .r11,
|
|
|
|
|
.base = .{ .reg = .r11 },
|
|
|
|
|
.scale_index = .{ .scale = 2, .index = .r12 },
|
|
|
|
|
.disp = 0x10,
|
|
|
|
|
}) },
|
|
|
|
|
@@ -1631,25 +1628,25 @@ test "lower MR encoding" {
|
|
|
|
|
try expectEqualHexStrings("\x47\x88\x6C\x63\x10", enc.code(), "mov BYTE PTR [r11 + 2 * r12 + 0x10], r13b");
|
|
|
|
|
|
|
|
|
|
try enc.encode(.add, &.{
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.byte, .{ .base = .ds, .disp = 0x10000000 }) },
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.byte, .{ .base = .{ .reg = .ds }, .disp = 0x10000000 }) },
|
|
|
|
|
.{ .reg = .r12b },
|
|
|
|
|
});
|
|
|
|
|
try expectEqualHexStrings("\x44\x00\x24\x25\x00\x00\x00\x10", enc.code(), "add BYTE PTR ds:0x10000000, r12b");
|
|
|
|
|
|
|
|
|
|
try enc.encode(.add, &.{
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.dword, .{ .base = .ds, .disp = 0x10000000 }) },
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.dword, .{ .base = .{ .reg = .ds }, .disp = 0x10000000 }) },
|
|
|
|
|
.{ .reg = .r12d },
|
|
|
|
|
});
|
|
|
|
|
try expectEqualHexStrings("\x44\x01\x24\x25\x00\x00\x00\x10", enc.code(), "add DWORD PTR [ds:0x10000000], r12d");
|
|
|
|
|
|
|
|
|
|
try enc.encode(.add, &.{
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.dword, .{ .base = .gs, .disp = 0x10000000 }) },
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.dword, .{ .base = .{ .reg = .gs }, .disp = 0x10000000 }) },
|
|
|
|
|
.{ .reg = .r12d },
|
|
|
|
|
});
|
|
|
|
|
try expectEqualHexStrings("\x65\x44\x01\x24\x25\x00\x00\x00\x10", enc.code(), "add DWORD PTR [gs:0x10000000], r12d");
|
|
|
|
|
|
|
|
|
|
try enc.encode(.sub, &.{
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.qword, .{ .base = .r11, .disp = 0x10000000 }) },
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .r11 }, .disp = 0x10000000 }) },
|
|
|
|
|
.{ .reg = .r12 },
|
|
|
|
|
});
|
|
|
|
|
try expectEqualHexStrings("\x4D\x29\xA3\x00\x00\x00\x10", enc.code(), "sub QWORD PTR [r11 + 0x10000000], r12");
|
|
|
|
|
@@ -1664,13 +1661,13 @@ test "lower M encoding" {
|
|
|
|
|
try expectEqualHexStrings("\x41\xFF\xD4", enc.code(), "call r12");
|
|
|
|
|
|
|
|
|
|
try enc.encode(.call, &.{
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.qword, .{ .base = .r12 }) },
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .r12 } }) },
|
|
|
|
|
});
|
|
|
|
|
try expectEqualHexStrings("\x41\xFF\x14\x24", enc.code(), "call QWORD PTR [r12]");
|
|
|
|
|
|
|
|
|
|
try enc.encode(.call, &.{
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.qword, .{
|
|
|
|
|
.base = null,
|
|
|
|
|
.base = .none,
|
|
|
|
|
.scale_index = .{ .index = .r11, .scale = 2 },
|
|
|
|
|
}) },
|
|
|
|
|
});
|
|
|
|
|
@@ -1678,14 +1675,14 @@ test "lower M encoding" {
|
|
|
|
|
|
|
|
|
|
try enc.encode(.call, &.{
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.qword, .{
|
|
|
|
|
.base = null,
|
|
|
|
|
.base = .none,
|
|
|
|
|
.scale_index = .{ .index = .r12, .scale = 2 },
|
|
|
|
|
}) },
|
|
|
|
|
});
|
|
|
|
|
try expectEqualHexStrings("\x42\xFF\x14\x65\x00\x00\x00\x00", enc.code(), "call QWORD PTR [r12 * 2]");
|
|
|
|
|
|
|
|
|
|
try enc.encode(.call, &.{
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.qword, .{ .base = .gs }) },
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .gs } }) },
|
|
|
|
|
});
|
|
|
|
|
try expectEqualHexStrings("\x65\xFF\x14\x25\x00\x00\x00\x00", enc.code(), "call gs:0x0");
|
|
|
|
|
|
|
|
|
|
@@ -1695,12 +1692,12 @@ test "lower M encoding" {
|
|
|
|
|
try expectEqualHexStrings("\xE8\x00\x00\x00\x00", enc.code(), "call 0x0");
|
|
|
|
|
|
|
|
|
|
try enc.encode(.push, &.{
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.qword, .{ .base = .rbp }) },
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .rbp } }) },
|
|
|
|
|
});
|
|
|
|
|
try expectEqualHexStrings("\xFF\x75\x00", enc.code(), "push QWORD PTR [rbp]");
|
|
|
|
|
|
|
|
|
|
try enc.encode(.push, &.{
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.word, .{ .base = .rbp }) },
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.word, .{ .base = .{ .reg = .rbp } }) },
|
|
|
|
|
});
|
|
|
|
|
try expectEqualHexStrings("\x66\xFF\x75\x00", enc.code(), "push QWORD PTR [rbp]");
|
|
|
|
|
|
|
|
|
|
@@ -1913,7 +1910,7 @@ fn cannotEncode(mnemonic: Instruction.Mnemonic, ops: []const Instruction.Operand
|
|
|
|
|
|
|
|
|
|
test "cannot encode" {
|
|
|
|
|
try cannotEncode(.@"test", &.{
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.byte, .{ .base = .r12 }) },
|
|
|
|
|
.{ .mem = Instruction.Memory.sib(.byte, .{ .base = .{ .reg = .r12 } }) },
|
|
|
|
|
.{ .reg = .ah },
|
|
|
|
|
});
|
|
|
|
|
try cannotEncode(.@"test", &.{
|
|
|
|
|
@@ -2093,7 +2090,7 @@ const Assembler = struct {
|
|
|
|
|
pub fn assemble(as: *Assembler, writer: anytype) !void {
|
|
|
|
|
while (try as.next()) |parsed_inst| {
|
|
|
|
|
const inst = try Instruction.new(.none, parsed_inst.mnemonic, &parsed_inst.ops);
|
|
|
|
|
try inst.encode(writer);
|
|
|
|
|
try inst.encode(writer, .{});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -2262,26 +2259,26 @@ const Assembler = struct {
|
|
|
|
|
|
|
|
|
|
// Supported rules and orderings.
|
|
|
|
|
const rules = .{
|
|
|
|
|
.{ .open_br, .base, .close_br }, // [ base ]
|
|
|
|
|
.{ .open_br, .base, .plus, .disp, .close_br }, // [ base + disp ]
|
|
|
|
|
.{ .open_br, .base, .minus, .disp, .close_br }, // [ base - disp ]
|
|
|
|
|
.{ .open_br, .disp, .plus, .base, .close_br }, // [ disp + base ]
|
|
|
|
|
.{ .open_br, .base, .plus, .index, .close_br }, // [ base + index ]
|
|
|
|
|
.{ .open_br, .base, .plus, .index, .star, .scale, .close_br }, // [ base + index * scale ]
|
|
|
|
|
.{ .open_br, .index, .star, .scale, .plus, .base, .close_br }, // [ index * scale + base ]
|
|
|
|
|
.{ .open_br, .base, .plus, .index, .star, .scale, .plus, .disp, .close_br }, // [ base + index * scale + disp ]
|
|
|
|
|
.{ .open_br, .base, .plus, .index, .star, .scale, .minus, .disp, .close_br }, // [ base + index * scale - disp ]
|
|
|
|
|
.{ .open_br, .index, .star, .scale, .plus, .base, .plus, .disp, .close_br }, // [ index * scale + base + disp ]
|
|
|
|
|
.{ .open_br, .index, .star, .scale, .plus, .base, .minus, .disp, .close_br }, // [ index * scale + base - disp ]
|
|
|
|
|
.{ .open_br, .disp, .plus, .index, .star, .scale, .plus, .base, .close_br }, // [ disp + index * scale + base ]
|
|
|
|
|
.{ .open_br, .disp, .plus, .base, .plus, .index, .star, .scale, .close_br }, // [ disp + base + index * scale ]
|
|
|
|
|
.{ .open_br, .base, .plus, .disp, .plus, .index, .star, .scale, .close_br }, // [ base + disp + index * scale ]
|
|
|
|
|
.{ .open_br, .base, .minus, .disp, .plus, .index, .star, .scale, .close_br }, // [ base - disp + index * scale ]
|
|
|
|
|
.{ .open_br, .base, .plus, .disp, .plus, .scale, .star, .index, .close_br }, // [ base + disp + scale * index ]
|
|
|
|
|
.{ .open_br, .base, .minus, .disp, .plus, .scale, .star, .index, .close_br }, // [ base - disp + scale * index ]
|
|
|
|
|
.{ .open_br, .general_purpose, .close_br }, // [ general_purpose ]
|
|
|
|
|
.{ .open_br, .general_purpose, .plus, .disp, .close_br }, // [ general_purpose + disp ]
|
|
|
|
|
.{ .open_br, .general_purpose, .minus, .disp, .close_br }, // [ general_purpose - disp ]
|
|
|
|
|
.{ .open_br, .disp, .plus, .general_purpose, .close_br }, // [ disp + general_purpose ]
|
|
|
|
|
.{ .open_br, .general_purpose, .plus, .index, .close_br }, // [ general_purpose + index ]
|
|
|
|
|
.{ .open_br, .general_purpose, .plus, .index, .star, .scale, .close_br }, // [ general_purpose + index * scale ]
|
|
|
|
|
.{ .open_br, .index, .star, .scale, .plus, .general_purpose, .close_br }, // [ index * scale + general_purpose ]
|
|
|
|
|
.{ .open_br, .general_purpose, .plus, .index, .star, .scale, .plus, .disp, .close_br }, // [ general_purpose + index * scale + disp ]
|
|
|
|
|
.{ .open_br, .general_purpose, .plus, .index, .star, .scale, .minus, .disp, .close_br }, // [ general_purpose + index * scale - disp ]
|
|
|
|
|
.{ .open_br, .index, .star, .scale, .plus, .general_purpose, .plus, .disp, .close_br }, // [ index * scale + general_purpose + disp ]
|
|
|
|
|
.{ .open_br, .index, .star, .scale, .plus, .general_purpose, .minus, .disp, .close_br }, // [ index * scale + general_purpose - disp ]
|
|
|
|
|
.{ .open_br, .disp, .plus, .index, .star, .scale, .plus, .general_purpose, .close_br }, // [ disp + index * scale + general_purpose ]
|
|
|
|
|
.{ .open_br, .disp, .plus, .general_purpose, .plus, .index, .star, .scale, .close_br }, // [ disp + general_purpose + index * scale ]
|
|
|
|
|
.{ .open_br, .general_purpose, .plus, .disp, .plus, .index, .star, .scale, .close_br }, // [ general_purpose + disp + index * scale ]
|
|
|
|
|
.{ .open_br, .general_purpose, .minus, .disp, .plus, .index, .star, .scale, .close_br }, // [ general_purpose - disp + index * scale ]
|
|
|
|
|
.{ .open_br, .general_purpose, .plus, .disp, .plus, .scale, .star, .index, .close_br }, // [ general_purpose + disp + scale * index ]
|
|
|
|
|
.{ .open_br, .general_purpose, .minus, .disp, .plus, .scale, .star, .index, .close_br }, // [ general_purpose - disp + scale * index ]
|
|
|
|
|
.{ .open_br, .rip, .plus, .disp, .close_br }, // [ rip + disp ]
|
|
|
|
|
.{ .open_br, .rip, .minus, .disp, .close_br }, // [ rig - disp ]
|
|
|
|
|
.{ .base, .colon, .disp }, // seg:disp
|
|
|
|
|
.{ .segment, .colon, .disp }, // seg:disp
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const pos = as.it.pos;
|
|
|
|
|
@@ -2301,7 +2298,7 @@ const Assembler = struct {
|
|
|
|
|
return Instruction.Memory.moffs(base, offset);
|
|
|
|
|
}
|
|
|
|
|
return Instruction.Memory.sib(ptr_size orelse .qword, .{
|
|
|
|
|
.base = base,
|
|
|
|
|
.base = .{ .reg = base },
|
|
|
|
|
.scale_index = res.scale_index,
|
|
|
|
|
.disp = res.disp orelse 0,
|
|
|
|
|
});
|
|
|
|
|
@@ -2323,7 +2320,7 @@ const Assembler = struct {
|
|
|
|
|
offset: ?u64 = null,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
fn parseMemoryRule(as: *Assembler, rule: anytype) ParseError!Instruction.MemoryParseResult {
|
|
|
|
|
fn parseMemoryRule(as: *Assembler, rule: anytype) ParseError!MemoryParseResult {
|
|
|
|
|
var res: MemoryParseResult = .{};
|
|
|
|
|
inline for (rule, 0..) |cond, i| {
|
|
|
|
|
if (@typeInfo(@TypeOf(cond)) != .EnumLiteral) {
|
|
|
|
|
@@ -2333,9 +2330,11 @@ const Assembler = struct {
|
|
|
|
|
.open_br, .close_br, .plus, .minus, .star, .colon => {
|
|
|
|
|
_ = try as.expect(cond);
|
|
|
|
|
},
|
|
|
|
|
.base => {
|
|
|
|
|
.general_purpose, .segment => {
|
|
|
|
|
const tok = try as.expect(.string);
|
|
|
|
|
res.base = registerFromString(as.source(tok)) orelse return error.InvalidMemoryOperand;
|
|
|
|
|
const base = registerFromString(as.source(tok)) orelse return error.InvalidMemoryOperand;
|
|
|
|
|
if (base.class() != cond) return error.InvalidMemoryOperand;
|
|
|
|
|
res.base = base;
|
|
|
|
|
},
|
|
|
|
|
.rip => {
|
|
|
|
|
const tok = try as.expect(.string);
|
|
|
|
|
@@ -2529,8 +2528,8 @@ test "assemble" {
|
|
|
|
|
0xF3, 0x45, 0x0F, 0x10, 0xF9,
|
|
|
|
|
0xF2, 0x44, 0x0F, 0x10, 0x45, 0xF0,
|
|
|
|
|
0xF2, 0x0F, 0x11, 0x45, 0xF8,
|
|
|
|
|
0xF3, 0x44, 0x0F, 0x7E, 0x45, 0xF0,
|
|
|
|
|
0x66, 0x44, 0x0F, 0xD6, 0x45, 0xF0,
|
|
|
|
|
0x66, 0x4C, 0x0F, 0x6E, 0x45, 0xF0,
|
|
|
|
|
0x66, 0x4C, 0x0F, 0x7E, 0x45, 0xF0,
|
|
|
|
|
0x66, 0x0F, 0x2E, 0x45, 0xF0,
|
|
|
|
|
0xDD, 0x4D, 0xF0,
|
|
|
|
|
0xDF, 0x0D, 0x20, 0x00, 0x00, 0x00,
|
|
|
|
|
|