x64 jump instructions have eip as modified register
| Questions | Answers |
|---|---|
| Capstone module affected | x64 |
| Source of Capstone | pip install capstone==6.0.0a4 |
| Version/git commit | 6.0.0 alpha 4 |
Upon disassembling different jump instructions in x64 mode, I saw that the "registers written" value have eip as the register modified, instead of rip.
Actual behavior
cstool -d x64 "e9 d0 ff ff ff"
0 e9 d0 ff ff ff jmp 0xffffffffffffffd5
ID: 172 (jmp)
Prefix:0x00 0x00 0x00 0x00
Opcode:0xe9 0x00 0x00 0x00
rex: 0x0
addr_size: 8
modrm: 0x0
disp: 0x0
sib: 0x0
imm_count: 1
imms[1]: 0xffffffffffffffd5
op_count: 1
operands[0].type: IMM = 0xffffffffffffffd5
operands[0].size: 8
Registers modified: eip
Groups: branch_relative jump
The same is true for je,ja,jle,jbe, etc.
Expected behavior
Register write is rip, not eip
The problem is that X86_JMP_4 shares the same entry on x32 and x64:
{
X86_JMP_4, X86_INS_JMP, 0,
#ifndef CAPSTONE_DIET
{ 0 }, { X86_REG_EIP, 0 }, { X86_GRP_BRANCH_RELATIVE, 0 }, 1, 0
#endif
},
That is a tricky one to fix (a pontential won't fix bug). The implicit read/writes are collected in x86_reg_access(). But it only passes a cs_insn to it, not the currently active mode (16/32/64bit). And since the jump instructions share the same ID for 32 and 64bit we can't really determine effectively if it is eip or rip.
Changing the signature of x86_reg_access to pass the mode doesn't work, because it is an API change.
We could add some kind of reg_access_fixup() function.
But is it really worth it? It adds some constant runtime, even if users don't really need it. And adding a CS_OPT_FIXUP_X86_INDIRECT_REG_ACCESS seems over-engineered.
Or would you argue otherwise?
I just ran into this, as well, but for the conditional jumps (e.g., jz). I agree it's not a straightforward change and might be best examined during the migration to Zydis. That said, xed, zydis, and bddisasm all report rip for these in 64-bit mode, so it's something to consider carefully.
EDIT
I just noticed that the conditional jump reports rflags instead of eflags which is correct. Yet, the entry has just eflags:
{
X86_JE_1, X86_INS_JE, 0,
#ifndef CAPSTONE_DIET
{ X86_REG_EFLAGS, 0 }, { X86_REG_EIP, 0 }, { X86_GRP_BRANCH_RELATIVE, 0 }, 1, 0
#endif
},
./cstool -dar x64 "0x7410"
0 74 10 je 0x12
ID: 260 (je)
Prefix:0x00 0x00 0x00 0x00
Opcode:0x74 0x00 0x00 0x00
rex: 0x0
addr_size: 8
modrm: 0x0
disp: 0x0
sib: 0x0
imm_count: 1
imms[1]: 0x12
op_count: 1
operands[0].type: IMM = 0x12
operands[0].size: 8
Registers read: rflags <-----
Registers modified: eip <-----
EFLAGS: TEST_ZF
Groups: branch_relative jump
I just ran into this, as well, but for the conditional jumps (e.g., jz). I agree it's not a straightforward change and might be best examined during the migration to Zydis. That said, xed, zydis, and bddisasm all report
ripfor these in 64-bit mode, so it's something to consider carefully.EDIT I just noticed that the conditional jump reports
rflagsinstead ofeflagswhich is correct. Yet, the entry has justeflags:{ X86_JE_1, X86_INS_JE, 0, #ifndef CAPSTONE_DIET { X86_REG_EFLAGS, 0 }, { X86_REG_EIP, 0 }, { X86_GRP_BRANCH_RELATIVE, 0 }, 1, 0 #endif },
./cstool -dar x64 "0x7410" 0 74 10 je 0x12 ID: 260 (je) Prefix:0x00 0x00 0x00 0x00 Opcode:0x74 0x00 0x00 0x00 rex: 0x0 addr_size: 8 modrm: 0x0 disp: 0x0 sib: 0x0 imm_count: 1 imms[1]: 0x12 op_count: 1 operands[0].type: IMM = 0x12 operands[0].size: 8 Registers read: rflags <----- Registers modified: eip <----- EFLAGS: TEST_ZF Groups: branch_relative jump
This one is handled by changing the register name:
if (reg == X86_REG_EFLAGS) {
if (ud->mode & CS_MODE_32)
return "eflags";
if (ud->mode & CS_MODE_64)
return "rflags";
}
which means you still get the X86_REG_EFLAGS, only the name printed has changed.