Skip to content

Commit 0b97b66

Browse files
authored
amd64: Fix 32b cmpxchg unmodified register writeback (#63)
32b register writes are zero-extended. In case a register should not be modified by cmpxchg, preserve the entire 64b value. Discovered via QEMU differential tests.
1 parent 54fca3e commit 0b97b66

File tree

1 file changed

+19
-4
lines changed

1 file changed

+19
-4
lines changed

priv/guest_amd64_toIR.c

+19-4
Original file line numberDiff line numberDiff line change
@@ -8677,10 +8677,25 @@ ULong dis_cmpxchg_G_E ( /*OUT*/Bool* ok,
86778677
assign( acc, getIRegRAX(size) );
86788678
setFlags_DEP1_DEP2(Iop_Sub8, acc, dest, ty);
86798679
assign( cond, mk_amd64g_calculate_condition(AMD64CondZ) );
8680-
assign( dest2, IRExpr_ITE(mkexpr(cond), mkexpr(src), mkexpr(dest)) );
8681-
assign( acc2, IRExpr_ITE(mkexpr(cond), mkexpr(acc), mkexpr(dest)) );
8682-
putIRegRAX(size, mkexpr(acc2));
8683-
putIRegE(size, pfx, rm, mkexpr(dest2));
8680+
8681+
if (size == 4) {
8682+
/* Preserve upper 32b when register should be unmodified. */
8683+
IRTemp acc64 = newTemp(Ity_I64);
8684+
IRTemp dest64 = newTemp(Ity_I64);
8685+
assign( dest64, IRExpr_ITE(mkexpr(cond),
8686+
unop(Iop_32Uto64, mkexpr(src)),
8687+
getIRegE(8, pfx, rm)) );
8688+
assign( acc64, IRExpr_ITE(mkexpr(cond),
8689+
getIRegRAX(8),
8690+
unop(Iop_32Uto64, mkexpr(dest))) );
8691+
putIRegRAX(8, mkexpr(acc64));
8692+
putIRegE(8, pfx, rm, mkexpr(dest64));
8693+
} else {
8694+
assign( dest2, IRExpr_ITE(mkexpr(cond), mkexpr(src), mkexpr(dest)) );
8695+
assign( acc2, IRExpr_ITE(mkexpr(cond), mkexpr(acc), mkexpr(dest)) );
8696+
putIRegRAX(size, mkexpr(acc2));
8697+
putIRegE(size, pfx, rm, mkexpr(dest2));
8698+
}
86848699
DIP("cmpxchg%c %s,%s\n", nameISize(size),
86858700
nameIRegG(size,pfx,rm),
86868701
nameIRegE(size,pfx,rm) );

0 commit comments

Comments
 (0)