You could single step this yourself in gdb to see what it did. Set up gdb to show you register changed by the last single-step (e.g. layout reg
, see the bottom of the x86 tag wiki).
Since %r12
needs to be a valid pointer for the source operand for the ADD, put this in foo.S
:
.globl _start
_start:
mov %rsp, %r12 # added this instruction: r12 is now a valid pointer to stack memory, since we copy the stack pointer into it
add -0x4(%r12), %eax
# cmp %eax, %r12 # operand-size mismatch is an error
cmp %eax, %r12d # 32-bit compare
cmp %rax, %r12 # 64-bit compare. upper 32 of RAX is zero from writing EAX in the add instruction
# your program will segfault here because we don't make an exit() system call, and instead keep executing whatever bytes are next in memory.
Assemble it with gcc -g -nostdlib foo.S
to make a static binary. _start is the default entry point.
Run gdb ./a.out
:
(gdb) layout reg
(gdb) b _start
(gdb) r
(gdb) si # step instruction,
# repeat as necessary and watch gdb highlight changed registers.
I like set disassembly-flavor intel
instead of AT&T syntax, but if you like (or want/need to learn AT&T syntax), then don't do that.
Hint, CMP doesn't modify either of its operands, and the ADD only uses R12 as the addressing mode to load 4 bytes of source data from.
The final value of EAX depends on what was in memory.