views:

262

answers:

2

I have this code :

section .data
Foos:
  mov ecx,7
  mov edx,5
L:
  inc edx
  sub ecx,1
  setZ al ; set al to 1 if zero flag, otherwise al=0
  shl al,1
  mov byte[L1+1],al
L1:
  jmp L
  lmp L
  mov eax,edx
  ret

The question is what will be in eax at the end of the code? I don't know why the answer is 12?

+3  A: 

This looks like self-modifying code.

It loops 7 times and when ecx reaches zero, it replaces the jmp L statement with a different JMP instruction which jumps to the statement after the second JMP.

Foos:
  mov ecx,7
  mov edx,5
L:
  inc edx
  sub ecx,1
  setZ al ; set al to 1 if zero flag, otherwise al=0
  shl al,1   ; multiply by 2, so al=2 when ecx reaches zero
  mov byte[L1+1],al
L1:
  jmp L
  jmp L
  mov eax,edx
  ret

The magic is in the mov byte[L1+1]. When al=0, it replaces the first JMP with a JMP to the next statement, which is again JMP L, so it loops. When al=2, it replaces the first JMP to skip 2 bytes, so it skips the second jump, so the loop ends. At the end edx is 12 (5 + 7)

Why is that?

The JMP instructions used here are short jumps. They are coded as 2 bytes: EB followed by a single byte representing the relative number of bytes to jump. The mov instruction replaces the second byte by either 0 (jump to next instruction) or 2 (jump 2 bytes ahead).

Philippe Leybaert
yes but how it replace the jump and where???
nisnis84
thanks a lot!!!!!
nisnis84
I think most of the modern x86s won't see the instruction update. By the time the "move byte[L1+1]" is executed, the code at L1 has been prefetched by the CPU into its instruction cache, and the update isn't seen by that cache. This happens because it isn't worth the effort on the part of the CPU designers to make the interactions between self-modifying code and instruction caching work. There are instructions to force a refetch of the instruction stream after such an update, but this code doesn't use them. So this code loops forever ==> edx never has a value at the ret instruction.
Ira Baxter
Actually a modern CPU most definitely will see the instruction update. When the mov byte[L1+1] retires, it will force a flush of the cache and the prefetcher and restart the instruction. This has a huge performance impact, but it works. It wouldn't do to have undefined behavior in legal code, would it?
Nathan Fellman
Sorry for the tone of the last comment. I can't see how to edit it, though. I just read some comments on reddit, and I was in a foul mood.
Nathan Fellman
+1  A: 

This code is self modifying and missing a memory fence instructions. What happens depends on the processor stepping and how full the instruction cache is at the point when the code is executed which can change if this section of code is preempted by an ISR, a page fault occurs, or any number of other things.