Instructions
x86-64XCHG
Atomically exchange two operands. Harmless between registers — but with a memory operand it carries an implicit LOCK, making it a foundational synchronization primitive.
XCHG a, b swaps the contents of its two operands. At least one must be a
register; the other can be a register or memory.
xchg eax, ebx ; swap the two registers
xchg [rdi], eax ; swap eax with the memory at [rdi] (atomic — see below)The implicit LOCK
This is the detail that matters for reverse engineering: when one operand is
memory, XCHG behaves as if it had a LOCK prefix even if none is
written. The read-modify-write is atomic with respect to other cores.
That makes xchg reg, [mem] the simplest atomic swap — the classic
implementation of std::atomic::exchange, a test-and-set spinlock acquire, and
similar primitives:
; acquire a spinlock: put 1 into the lock, learn the old value
mov eax, 1
xchg eax, [lock] ; atomically: old = *lock; *lock = 1
test eax, eax ; was it already held?
jnz spin ; yes → keep spinningRegister-to-register xchg has no such locking and is just a swap.
Reverse-engineering notes
xchg reg, [mem](or anylock-prefixed RMW) is a strong signal you're inside locking / lock-free concurrency code: spinlocks, mutex fast paths, reference-count swaps.xchg eax, eax(encoded0x90) isNOP— they share an opcode. A disassembler will shownop, but now you know why.- Compilers rarely use register
xchgto swap variables; threemovs through a temporary are usually faster on modern cores. A registerxchgtherefore often points to hand-written assembly or a deliberate size optimisation. - Don't confuse it with
CMPXCHG(compare-and-swap), the other workhorse of lock-free code —CMPXCHGswaps only if the destination still equals a comparand, and needs an explicitlockto be atomic.
See also: MOV · NOP · PUSH / POP.