zydis icon indicating copy to clipboard operation
zydis copied to clipboard

Conditional writes for CPU flags

Open aswerw opened this issue 6 years ago • 6 comments

Some instructions will only conditionally modify a CPU flag. I think most instructions do so unconditionally.

Zydis exposes conditional reads and writes for operands.

Should it do the same for CPU flags?

Example: (conditional carry flag write)

rol eax, 0 ; Value of CF will be preserved
rol eax, 1 ; Value of CF will be modified

Here are a few others:

RCL/RCR/ROL/ROR--Rotate "If the masked count is 0, the flags are not affected. If the masked count is 1, then the OF flag is affected, otherwise (masked count is greater than 1) the OF flag is undefined. The CF flag is affected when the masked count is non-zero."

DAA/DAS "The CF and AF flags are set if the adjustment of the value results in a decimal [carry/borrow] in either digit of the result.."

CLI/STI "Either the IF flag or the VIF flag is [cleared/set] to [0/1]."

SAL/SAR/SHL/SHR--Shift "If the count is 0, the flags are not affected."

SHLD/SHRD "If the count operand is 0, the flags are not affected."

aswerw avatar Aug 09 '18 08:08 aswerw

Thanks for reporting. I'm aware of this problem and implementing a more advanced system for the flags is already on my todo-list (for the v3.0 release). Nevertheless some of these conditions are beyond the scope of a disassembler. CLI for example.:

If IOPL < 3 and either VME mode or PVI mode is active, CLI clears the VIF flag in the EFLAGS register, leaving IF unaffected.

There is no way to check IOPL/VME/PVI without making it somehow configurable by the user. And going this way, we would have to implement options for pretty much all CPU-modes, MSRs, system-context, ...

For now, zydis uses the "worst-case" action for these conditional flags e.g. UNDEFINED for RCL/... or MODIFIED for SAL/....

flobernd avatar Aug 09 '18 11:08 flobernd

Could you elaborate on how this would be useful for your project? I can't really think of a use case where the information of conditional flag writes would be more useful than a general write info unless the disassembler also tells you if it is actually written (which usually depends on a lot of context and thus is very hard to do).

athre0z avatar Aug 09 '18 12:08 athre0z

In my case it's for knowing whether or not an optimization is safe to do.

A poor example:

stc ; CF=1
ror eax, <imm8> ; Or some other instruction that conditionally writes to CF
jc <rel8>

STC can't be optimized out in this case, because ROR may not overwrite the CF.

If ROR was an instruction that unconditionally modified CF (without testing it as well) then STC could be removed, but there is no way to distinguish the two cases with Zydis.

aswerw avatar Aug 09 '18 13:08 aswerw

Allright. I can easily implement some logic for RCL/RCR/ROL/ROR, SAL/SAR/SHL/SHR, and SHLD/SHRD by adding an opcode-filter that matches the IMMEDIATE value of the instruction.

CLI/STI is beyond the scope of a disassembler for the reasons described above and DAA/DAS is a special case as well (as it requires context; e.g. the content of the AL register which is only available at runtime).

flobernd avatar Aug 09 '18 13:08 flobernd

Hmm, I see.

@flobernd I don't know if that would be such a good idea. Well, it could be done additionally, but it doesn't really address the core problem. Even if special handling for those imm=0 variants were added, there are still dynamic versions of the instructions (e.g. ROR r/m8, CL) where there is no certainty of a write being performed. I guess we'll just need a new flag access mode and do our best in fixing the tables. That will involve a lot of work, though.

athre0z avatar Aug 09 '18 13:08 athre0z

Reminder for myself: Let's for now always include the conditional flags. For the register form of the rotate instructions we can't do much anyways ...

flobernd avatar Nov 30 '20 07:11 flobernd