Skip to content

Commit

Permalink
encode2: Fix RIP offset with VEX/EVEX+67/seg
Browse files Browse the repository at this point in the history
  • Loading branch information
aengelke committed Jul 10, 2024
1 parent e59a751 commit e19a52e
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 29 deletions.
45 changes: 45 additions & 0 deletions encode-test.inc
Original file line number Diff line number Diff line change
Expand Up @@ -1496,6 +1496,51 @@ TEST("\x6b\x05\xf9\xff\xff\xff\x02", IMUL32rmi, 0, FE_AX, FE_MEM(FE_IP, 0, FE_NO
TEST("\x66\x6b\x05\xf8\xff\xff\xff\x02", IMUL16rmi, 0, FE_AX, FE_MEM(FE_IP, 0, FE_NOREG, 0), 2);
TEST("\x69\x05\xf6\xff\xff\xff\x80\x00\x00\x00", IMUL32rmi, 0, FE_AX, FE_MEM(FE_IP, 0, FE_NOREG, 0), 0x80);
TEST("\x66\x69\x05\xf7\xff\xff\xff\x80\x00", IMUL16rmi, 0, FE_AX, FE_MEM(FE_IP, 0, FE_NOREG, 0), 0x80);
// Test RIP-relative addressing with various prefixes
TEST("\x01\x05\x0a\x00\x00\x00", ADD32mr, 0, FE_MEM(FE_IP, 0, FE_NOREG, 0x10), FE_AX);
TEST("\x44\x01\x05\x09\x00\x00\x00", ADD32mr, 0, FE_MEM(FE_IP, 0, FE_NOREG, 0x10), FE_R8);
TEST("\x67\x01\x05\x09\x00\x00\x00", ADD32mr, FE_ADDR32, FE_MEM(FE_IP, 0, FE_NOREG, 0x10), FE_AX);
TEST("\x67\x44\x01\x05\x08\x00\x00\x00", ADD32mr, FE_ADDR32, FE_MEM(FE_IP, 0, FE_NOREG, 0x10), FE_R8);
TEST("\x64\x01\x05\x09\x00\x00\x00", ADD32mr, FE_SEG(FE_FS), FE_MEM(FE_IP, 0, FE_NOREG, 0x10), FE_AX);
TEST("\x64\x44\x01\x05\x08\x00\x00\x00", ADD32mr, FE_SEG(FE_FS), FE_MEM(FE_IP, 0, FE_NOREG, 0x10), FE_R8);
TEST("\x64\x67\x01\x05\x08\x00\x00\x00", ADD32mr, FE_ADDR32|FE_SEG(FE_FS), FE_MEM(FE_IP, 0, FE_NOREG, 0x10), FE_AX);
TEST("\x64\x67\x44\x01\x05\x07\x00\x00\x00", ADD32mr, FE_ADDR32|FE_SEG(FE_FS), FE_MEM(FE_IP, 0, FE_NOREG, 0x10), FE_R8);
TEST("\x66\x01\x05\x09\x00\x00\x00", ADD16mr, 0, FE_MEM(FE_IP, 0, FE_NOREG, 0x10), FE_AX);
TEST("\x66\x44\x01\x05\x08\x00\x00\x00", ADD16mr, 0, FE_MEM(FE_IP, 0, FE_NOREG, 0x10), FE_R8);
TEST("\x67\x66\x01\x05\x08\x00\x00\x00", ADD16mr, FE_ADDR32, FE_MEM(FE_IP, 0, FE_NOREG, 0x10), FE_AX);
TEST("\x67\x66\x44\x01\x05\x07\x00\x00\x00", ADD16mr, FE_ADDR32, FE_MEM(FE_IP, 0, FE_NOREG, 0x10), FE_R8);
TEST("\x64\x66\x01\x05\x08\x00\x00\x00", ADD16mr, FE_SEG(FE_FS), FE_MEM(FE_IP, 0, FE_NOREG, 0x10), FE_AX);
TEST("\x64\x66\x44\x01\x05\x07\x00\x00\x00", ADD16mr, FE_SEG(FE_FS), FE_MEM(FE_IP, 0, FE_NOREG, 0x10), FE_R8);
TEST("\x64\x67\x66\x01\x05\x07\x00\x00\x00", ADD16mr, FE_ADDR32|FE_SEG(FE_FS), FE_MEM(FE_IP, 0, FE_NOREG, 0x10), FE_AX);
TEST("\x64\x67\x66\x44\x01\x05\x06\x00\x00\x00", ADD16mr, FE_ADDR32|FE_SEG(FE_FS), FE_MEM(FE_IP, 0, FE_NOREG, 0x10), FE_R8);
TEST("\xf0\x01\x05\x09\x00\x00\x00", LOCK_ADD32mr, 0, FE_MEM(FE_IP, 0, FE_NOREG, 0x10), FE_AX);
TEST("\xf0\x44\x01\x05\x08\x00\x00\x00", LOCK_ADD32mr, 0, FE_MEM(FE_IP, 0, FE_NOREG, 0x10), FE_R8);
TEST("\x67\xf0\x01\x05\x08\x00\x00\x00", LOCK_ADD32mr, FE_ADDR32, FE_MEM(FE_IP, 0, FE_NOREG, 0x10), FE_AX);
TEST("\x67\xf0\x44\x01\x05\x07\x00\x00\x00", LOCK_ADD32mr, FE_ADDR32, FE_MEM(FE_IP, 0, FE_NOREG, 0x10), FE_R8);
TEST("\x64\xf0\x01\x05\x08\x00\x00\x00", LOCK_ADD32mr, FE_SEG(FE_FS), FE_MEM(FE_IP, 0, FE_NOREG, 0x10), FE_AX);
TEST("\x64\xf0\x44\x01\x05\x07\x00\x00\x00", LOCK_ADD32mr, FE_SEG(FE_FS), FE_MEM(FE_IP, 0, FE_NOREG, 0x10), FE_R8);
TEST("\x64\x67\xf0\x01\x05\x07\x00\x00\x00", LOCK_ADD32mr, FE_ADDR32|FE_SEG(FE_FS), FE_MEM(FE_IP, 0, FE_NOREG, 0x10), FE_AX);
TEST("\x64\x67\xf0\x44\x01\x05\x06\x00\x00\x00", LOCK_ADD32mr, FE_ADDR32|FE_SEG(FE_FS), FE_MEM(FE_IP, 0, FE_NOREG, 0x10), FE_R8);
TEST("\xf0\x66\x01\x05\x08\x00\x00\x00", LOCK_ADD16mr, 0, FE_MEM(FE_IP, 0, FE_NOREG, 0x10), FE_AX);
TEST("\xf0\x66\x44\x01\x05\x07\x00\x00\x00", LOCK_ADD16mr, 0, FE_MEM(FE_IP, 0, FE_NOREG, 0x10), FE_R8);
TEST("\x67\xf0\x66\x01\x05\x07\x00\x00\x00", LOCK_ADD16mr, FE_ADDR32, FE_MEM(FE_IP, 0, FE_NOREG, 0x10), FE_AX);
TEST("\x67\xf0\x66\x44\x01\x05\x06\x00\x00\x00", LOCK_ADD16mr, FE_ADDR32, FE_MEM(FE_IP, 0, FE_NOREG, 0x10), FE_R8);
TEST("\x64\xf0\x66\x01\x05\x07\x00\x00\x00", LOCK_ADD16mr, FE_SEG(FE_FS), FE_MEM(FE_IP, 0, FE_NOREG, 0x10), FE_AX);
TEST("\x64\xf0\x66\x44\x01\x05\x06\x00\x00\x00", LOCK_ADD16mr, FE_SEG(FE_FS), FE_MEM(FE_IP, 0, FE_NOREG, 0x10), FE_R8);
TEST("\x64\x67\xf0\x66\x01\x05\x06\x00\x00\x00", LOCK_ADD16mr, FE_ADDR32|FE_SEG(FE_FS), FE_MEM(FE_IP, 0, FE_NOREG, 0x10), FE_AX);
TEST("\x64\x67\xf0\x66\x44\x01\x05\x05\x00\x00\x00", LOCK_ADD16mr, FE_ADDR32|FE_SEG(FE_FS), FE_MEM(FE_IP, 0, FE_NOREG, 0x10), FE_R8);
TEST("\xc5\xf9\x6e\x05\x08\x00\x00\x00", VMOVDrm, 0, FE_XMM0, FE_MEM(FE_IP, 0, FE_NOREG, 0x10));
TEST("\xc4\xe1\xf9\x6e\x05\x07\x00\x00\x00", VMOVQ_G2Xrm, 0, FE_XMM0, FE_MEM(FE_IP, 0, FE_NOREG, 0x10));
TEST("\x67\xc5\xf9\x6e\x05\x07\x00\x00\x00", VMOVDrm, FE_ADDR32, FE_XMM0, FE_MEM(FE_IP, 0, FE_NOREG, 0x10));
TEST("\x67\xc4\xe1\xf9\x6e\x05\x06\x00\x00\x00", VMOVQ_G2Xrm, FE_ADDR32, FE_XMM0, FE_MEM(FE_IP, 0, FE_NOREG, 0x10));
TEST("\x64\xc5\xf9\x6e\x05\x07\x00\x00\x00", VMOVDrm, FE_SEG(FE_FS), FE_XMM0, FE_MEM(FE_IP, 0, FE_NOREG, 0x10));
TEST("\x64\xc4\xe1\xf9\x6e\x05\x06\x00\x00\x00", VMOVQ_G2Xrm, FE_SEG(FE_FS), FE_XMM0, FE_MEM(FE_IP, 0, FE_NOREG, 0x10));
TEST("\x64\x67\xc5\xf9\x6e\x05\x06\x00\x00\x00", VMOVDrm, FE_ADDR32|FE_SEG(FE_FS), FE_XMM0, FE_MEM(FE_IP, 0, FE_NOREG, 0x10));
TEST("\x64\x67\xc4\xe1\xf9\x6e\x05\x05\x00\x00\x00", VMOVQ_G2Xrm, FE_ADDR32|FE_SEG(FE_FS), FE_XMM0, FE_MEM(FE_IP, 0, FE_NOREG, 0x10));
TEST("\x62\xe1\x7d\x08\x6e\x05\x06\x00\x00\x00", VMOVDrm, 0, FE_XMM16, FE_MEM(FE_IP, 0, FE_NOREG, 0x10));
TEST("\x67\x62\xe1\x7d\x08\x6e\x05\x05\x00\x00\x00", VMOVDrm, FE_ADDR32, FE_XMM16, FE_MEM(FE_IP, 0, FE_NOREG, 0x10));
TEST("\x64\x62\xe1\x7d\x08\x6e\x05\x05\x00\x00\x00", VMOVDrm, FE_SEG(FE_FS), FE_XMM16, FE_MEM(FE_IP, 0, FE_NOREG, 0x10));
TEST("\x64\x67\x62\xe1\x7d\x08\x6e\x05\x04\x00\x00\x00", VMOVDrm, FE_ADDR32|FE_SEG(FE_FS), FE_XMM16, FE_MEM(FE_IP, 0, FE_NOREG, 0x10));

TEST("\x01\x00", ADD32mr, 0, FE_MEM(FE_AX, 0, FE_NOREG, 0), FE_AX);
TEST("\x01\x01", ADD32mr, 0, FE_MEM(FE_CX, 0, FE_NOREG, 0), FE_AX);
Expand Down
54 changes: 27 additions & 27 deletions encode2.c
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ enc_imm(uint8_t* buf, uint64_t imm, unsigned immsz) {
}

static int
enc_mem_common(uint8_t* buf, unsigned bufidx, FeMem op0, uint64_t op1,
unsigned immsz, unsigned sibidx, unsigned disp8scale) {
enc_mem_common(uint8_t* buf, unsigned ripoff, FeMem op0, uint64_t op1,
unsigned sibidx, unsigned disp8scale) {
int mod = 0, reg = op1 & 7, rm;
unsigned sib = 0x20;
bool withsib = false;
Expand All @@ -68,17 +68,17 @@ enc_mem_common(uint8_t* buf, unsigned bufidx, FeMem op0, uint64_t op1,
}

if (UNLIKELY(op0.base.idx == op_reg_idx(FE_NOREG))) {
buf[bufidx] = (reg << 3) | 4;
buf[bufidx+1] = sib | 5;
enc_imm(buf + bufidx + 2, off, 4);
*buf++ = (reg << 3) | 4;
*buf++ = sib | 5;
enc_imm(buf, off, 4);
return 6;
} else if (UNLIKELY(op0.base.idx == FE_IP.idx)) {
if (withsib)
return 0;
// Adjust offset, caller doesn't know instruction length.
off -= bufidx + 5 + immsz;
buf[bufidx] = (reg << 3) | 5;
enc_imm(buf + bufidx + 1, off, 4);
off -= ripoff + 5;
*buf++ = (reg << 3) | 5;
enc_imm(buf, off, 4);
return 5;
}

Expand All @@ -102,20 +102,20 @@ enc_mem_common(uint8_t* buf, unsigned bufidx, FeMem op0, uint64_t op1,
// Always write four bytes of displacement. The buffer is always large
// enough, and we truncate by returning a smaller "written bytes" count.
if (withsib || rm == 4) {
buf[bufidx] = mod | (reg << 3) | 4;
buf[bufidx+1] = sib | rm;
enc_imm(buf + bufidx + 2, off, 4);
*buf++ = mod | (reg << 3) | 4;
*buf++ = sib | rm;
enc_imm(buf, off, 4);
return 2 + dispsz;
} else {
buf[bufidx] = mod | (reg << 3) | rm;
enc_imm(buf + bufidx + 1, off, 4);
*buf++ = mod | (reg << 3) | rm;
enc_imm(buf, off, 4);
return 1 + dispsz;
}
}

static int
enc_mem(uint8_t* buf, unsigned bufidx, FeMem op0, uint64_t op1, unsigned immsz,
unsigned forcesib, unsigned disp8scale) {
enc_mem(uint8_t* buf, unsigned ripoff, FeMem op0, uint64_t op1, bool forcesib,
unsigned disp8scale) {
if ((op_reg_idx(op0.idx) != op_reg_idx(FE_NOREG)) != !!op0.scale)
return 0;
unsigned sibidx = forcesib ? 4 : 8;
Expand All @@ -124,17 +124,17 @@ enc_mem(uint8_t* buf, unsigned bufidx, FeMem op0, uint64_t op1, unsigned immsz,
return 0;
sibidx = op_reg_idx(op0.idx) & 7;
}
return enc_mem_common(buf, bufidx, op0, op1, immsz, sibidx, disp8scale);
return enc_mem_common(buf, ripoff, op0, op1, sibidx, disp8scale);
}

static int
enc_mem_vsib(uint8_t* buf, unsigned bufidx, FeMemV op0, uint64_t op1,
unsigned immsz, unsigned forcesib, unsigned disp8scale) {
enc_mem_vsib(uint8_t* buf, unsigned ripoff, FeMemV op0, uint64_t op1,
bool forcesib, unsigned disp8scale) {
(void) forcesib;
if (!op0.scale)
return 0;
FeMem mem = FE_MEM(op0.base, op0.scale, FE_NOREG, op0.off);
return enc_mem_common(buf, bufidx, mem, op1, immsz, op_reg_idx(op0.idx) & 7,
return enc_mem_common(buf, ripoff, mem, op1, op_reg_idx(op0.idx) & 7,
disp8scale);
}

Expand Down Expand Up @@ -190,17 +190,17 @@ enc_vex_reg(uint8_t* buf, unsigned opcode, uint64_t rm, uint64_t reg,

static int
enc_vex_mem(uint8_t* buf, unsigned opcode, FeMem rm, uint64_t reg,
uint64_t vvvv, unsigned immsz, bool forcesib, unsigned disp8scale) {
uint64_t vvvv, unsigned ripoff, bool forcesib, unsigned disp8scale) {
unsigned off = enc_vex_common(buf, opcode, op_reg_idx(rm.base), op_reg_idx(rm.idx), reg, vvvv);
unsigned memoff = enc_mem(buf, off, rm, reg, immsz, forcesib, disp8scale);
unsigned memoff = enc_mem(buf + off, ripoff + off, rm, reg, forcesib, disp8scale);
return off && memoff ? off + memoff : 0;
}

static int
enc_vex_vsib(uint8_t* buf, unsigned opcode, FeMemV rm, uint64_t reg,
uint64_t vvvv, unsigned immsz, bool forcesib, unsigned disp8scale) {
uint64_t vvvv, unsigned ripoff, bool forcesib, unsigned disp8scale) {
unsigned off = enc_vex_common(buf, opcode, op_reg_idx(rm.base), op_reg_idx(rm.idx), reg, vvvv);
unsigned memoff = enc_mem_vsib(buf, off, rm, reg, immsz, forcesib, disp8scale);
unsigned memoff = enc_mem_vsib(buf + off, ripoff + off, rm, reg, forcesib, disp8scale);
return off && memoff ? off + memoff : 0;
}

Expand Down Expand Up @@ -267,7 +267,7 @@ enc_evex_xmm(uint8_t* buf, unsigned opcode, unsigned rm,

static int
enc_evex_mem(uint8_t* buf, unsigned opcode, FeMem rm, uint64_t reg,
uint64_t vvvv, unsigned immsz, bool forcesib, unsigned disp8scale) {
uint64_t vvvv, unsigned ripoff, bool forcesib, unsigned disp8scale) {
unsigned off;
if (!((op_reg_idx(rm.base) | op_reg_idx(rm.idx) | reg | vvvv) & 0x10) &&
(opcode & FE_OPC_VEX_DOWNGRADE_VEX)) {
Expand All @@ -276,20 +276,20 @@ enc_evex_mem(uint8_t* buf, unsigned opcode, FeMem rm, uint64_t reg,
} else {
off = enc_evex_common(buf, opcode, op_reg_idx(rm.base), op_reg_idx(rm.idx), reg, vvvv);
}
unsigned memoff = enc_mem(buf, off, rm, reg, immsz, forcesib, disp8scale);
unsigned memoff = enc_mem(buf + off, ripoff + off, rm, reg, forcesib, disp8scale);
return off && memoff ? off + memoff : 0;
}

static int
enc_evex_vsib(uint8_t* buf, unsigned opcode, FeMemV rm, uint64_t reg,
uint64_t vvvv, unsigned immsz, bool forcesib, unsigned disp8scale) {
uint64_t vvvv, unsigned ripoff, bool forcesib, unsigned disp8scale) {
(void) vvvv;
// EVEX VSIB requires non-zero mask operand
if (!(opcode & 0x7)) return 0;
// EVEX.X4 is encoded in EVEX.V4
unsigned idx = op_reg_idx(rm.idx);
unsigned off = enc_evex_common(buf, opcode, op_reg_idx(rm.base), idx & 0x0f, reg, idx & 0x10);
unsigned memoff = enc_mem_vsib(buf, off, rm, reg, immsz, forcesib, disp8scale);
unsigned memoff = enc_mem_vsib(buf + off, ripoff + off, rm, reg, forcesib, disp8scale);
return off && memoff ? off + memoff : 0;
}

Expand Down
5 changes: 3 additions & 2 deletions parseinstrs.py
Original file line number Diff line number Diff line change
Expand Up @@ -1115,7 +1115,7 @@ def encode2_gen_legacy(variant: EncodeVariant, opsize: int, supports_high_regs:
assert "VSIB" not in desc.flags
assert opcode.modrm[2] is None
modrm = f"op{flags.modrm_idx^3}"
code += f" unsigned memoff = enc_mem(buf, idx, {modrm}, {modreg}, {imm_size_expr}, 0, 0);\n"
code += f" unsigned memoff = enc_mem(buf+idx, idx+{imm_size_expr}, {modrm}, {modreg}, 0, 0);\n"
code += f" if (!memoff) return 0;\n idx += memoff;\n"
else:
if flags.modrm_idx:
Expand Down Expand Up @@ -1172,7 +1172,8 @@ def encode2_gen_vex(variant: EncodeVariant, imm_expr: str, imm_size_expr: str, h
assert opcode.modrm[2] in (None, 4)
forcesib = 1 if opcode.modrm[2] == 4 else 0 # AMX
modrm = f"op{flags.modrm_idx^3}"
helperargs = (f"{modrm}, {modreg}, {vexop}, {imm_size_expr}, " +
ripoff = imm_size_expr + ("" if not has_idx else "+idx")
helperargs = (f"{modrm}, {modreg}, {vexop}, {ripoff}, " +
f"{forcesib}, {variant.evexdisp8scale}")
else:
if flags.modrm_idx:
Expand Down

0 comments on commit e19a52e

Please sign in to comment.