Incorrect CPU Flags Set After imul rax, rax, 0 Instruction Execution
Hello Unicorn Team,
I've encountered an issue where executing the instruction:
imul rax, rax, 0
produces incorrect CPU flags in Unicorn's emulation compared to real hardware behavior.
What’s the difference?
From: mojtabafalleh @.> Sent: Sunday, July 13, 2025 3:13:38 AM To: unicorn-engine/unicorn @.> Cc: Subscribed @.***> Subject: [unicorn-engine/unicorn] Incorrect CPU Flags Set After imul rax, rax, 0 Instruction Execution (Issue #2212)
[https://avatars.githubusercontent.com/u/28828665?s=20&v=4]mojtabafalleh created an issue (unicorn-engine/unicorn#2212)https://github.com/unicorn-engine/unicorn/issues/2212
Hello Unicorn Team,
I've encountered an issue where executing the instruction:
imul rax, rax, 0 produces incorrect CPU flags in Unicorn's emulation compared to real hardware behavior.
— Reply to this email directly, view it on GitHubhttps://github.com/unicorn-engine/unicorn/issues/2212, or unsubscribehttps://github.com/notifications/unsubscribe-auth/AHJULO6CRAUXLOV5PAQ26N33IFM6FAVCNFSM6AAAAACBMBZ5VGVHI2DSMVQWIX3LMV43ASLTON2WKOZTGIZDKNRYHA4TMNQ. You are receiving this because you are subscribed to this thread.Message ID: @.***>
real cpu = 0x206 unicorn = 0x46
How to reproduce?
https://github.com/mojtabafalleh/anti_emulaion_unicorn for test in unicorn : https://sogen.dev/
On x86 (according to https://www.felixcloutier.com/x86/imul), imul should have SF, ZF, AF, and PF flags left undefined. Only CF and OF are defined.
On my AMD Ryzen 7, I get 0x202, IF = 1 (0x200) and 0x2 of reserved. Which is what you would expect from a good citizen following the spec, there's no carry and no overflow for such multiplication (0x1234567 * 0), and IF is set since we run the program in user-mode on top of the Windows kernel which is responsible for enabling interrupt.
I guess you have an Intel cpu since we have different result ? They may either set PF = 1 (since it's undefined, it's allowed to take any value), or you may have the cpu in a state with PF = 1 before imul is reached. So you might see PF = 1 or you might not — the spec doesn’t guarantee it.
Anyway, with IF = 1, PF = 1, and 0x2 of reserved, this add up to 0x206 exactly like your example.
Now in Unicorn, we have PF = 1 and ZF = 1, 0x2 reserved, so we end up with 0x46 (I dumped the qemu flags in function below).
The function that compute flags for multiplication in cc_template_helper.h :
/* NOTE: we compute the flags like the P4. On olders CPUs, only OF and
CF are modified and it is slower to do that. Note as well that we
don't truncate SRC1 for computing carry to DATA_TYPE. */
static int glue(compute_all_mul, SUFFIX)(DATA_TYPE dst, target_long src1)
{
int cf, pf, af, zf, sf, of;
cf = (src1 != 0);
pf = parity_table[(uint8_t)dst];
af = 0; /* undefined */
zf = (dst == 0) * CC_Z;
sf = lshift(dst, 8 - DATA_BITS) & CC_S;
of = cf * CC_O;
return cf | pf | af | zf | sf | of;
}
If you don't use sti (enable interrupt) before imul is reached, then IF will be 0. If you enable interrupt, then you get 0x246 which is correct, but still it would be better to get something closer to AMD like 0x202 which map perfectly to my AMD cpu. That would mean no flags computed except CF and OF. To get 0x206 like your cpu, you would have to compute PF only (and also have interrupt enabled). Unicorn doesn't provide a kernel which setup everything for you before-hand, it's up to you to emulate the environment. 0x46 on all desktop OS is a red flag since that mean your code run bare-metal. It should be at last 0x202 for a user-mode program.
So tdlr, if it's below 0x202 there's an emulator behind the scene.
In qemu 5.0.1 they choose to set AF = 0 and let the others flags being computed normally. In general, from what I saw, there's a tendency to re-use such flags helper across group of opcode. And even today, the function didn't changed that much from 5.0.1 https://gitlab.com/qemu-project/qemu/-/blob/master/target/i386/tcg/cc_helper_template.h.inc#L212