On slide 20 of the 3rd compilers handout,
the x86 implementation of the FST
Jargon VM instruction is shown to be:
movl 4(%esp), %edx // 4 bytes, 1 word, after header
movl %edx, (%esp) // replace “a” with “v1” at top of stack
and there is a helpful diagram too:
I am now convinced that the first of these two instructions is
incorrect, since it is missing a level of indirection, and instead of
taking the value v1
from the heap, the value below a
on the stack
would be taken, as shown in the (poorly) modified diagram below.
Since I sometimes find manuals (and generally human languages) confusing
when talking about things like this, I took a more practical approach
and wrote an example program that demonstrates the situation. This can
be found in poc.asm
. It is written in the Intel syntax, since I am
more comfortable with that, however, after compilation I used objdump
to acquire a dissassembly listing in both flavours, which can be seen
below. This step can be easily reproduced using the att
and intel
targets in the attached Makefile.
Intel syntax:
8048410: 68 aa aa aa aa push 0xaaaaaaaa
8048415: 68 9c 96 04 08 push 0x804969c
804841a: 8b 54 24 04 mov edx,DWORD PTR [esp+0x4]
804841e: 52 push edx
804841f: 68 a0 96 04 08 push 0x80496a0
8048424: e8 b7 fe ff ff call 80482e0 <printf@plt>
8048429: 83 c4 10 add esp,0x10
804842c: b8 00 00 00 00 mov eax,0x0
8048431: c3 ret
8048432: 66 90 xchg ax,ax
8048434: 66 90 xchg ax,ax
8048436: 66 90 xchg ax,ax
8048438: 66 90 xchg ax,ax
804843a: 66 90 xchg ax,ax
804843c: 66 90 xchg ax,ax
804843e: 66 90 xchg ax,ax
GAS syntax:
8048410: 68 aa aa aa aa push $0xaaaaaaaa
8048415: 68 9c 96 04 08 push $0x804969c
804841a: 8b 54 24 04 mov 0x4(%esp),%edx
804841e: 52 push %edx
804841f: 68 a0 96 04 08 push $0x80496a0
8048424: e8 b7 fe ff ff call 80482e0 <printf@plt>
8048429: 83 c4 10 add $0x10,%esp
804842c: b8 00 00 00 00 mov $0x0,%eax
8048431: c3 ret
8048432: 66 90 xchg %ax,%ax
8048434: 66 90 xchg %ax,%ax
8048436: 66 90 xchg %ax,%ax
8048438: 66 90 xchg %ax,%ax
804843a: 66 90 xchg %ax,%ax
804843c: 66 90 xchg %ax,%ax
804843e: 66 90 xchg %ax,%ax
Note that the instruction at address 0x0804841a is exactly the disputed
instruction from below. (The slides use the mnemonic movl
, but
objdump
does not output a prefix when the operand size can be
unambiguously determined from the context, as it is the case here. I did
not find a truly authoritative source on this, but Wikipedia says
If the suffix is not specified, and there are no memory operands for the instruction, GAS infers the operand size from the size of the destination register operand (the final operand).,
and TLDP says
However, gas does not require strict AT&T syntax, so the suffix is optional when size can be guessed from register operands, and else defaults to 32-bit (with a warning).
, plus operand size should not matter anyway.)
This proof of concept code first pushes a dummy value (0xAAAAAAAA) on
the stack, then pushes a 'heap address', where two dummy values
(0xBBBBBBBB, and 0xCCCCCCCC) are stored (these correspond to the header
and the first/left value of a
from the example in the notes).
After assembling, and linking with the C library (make poc.x
), running
poc.x
outputs: result is: AAAAAAAA
, verifying that indeed the
incorrect value is loaded, as in the second diagram above.
Debugging in GDB (with peda installed) nicely demonstrates what is happening inside the binary.
gdb-peda$ start
[----------------------------------registers-----------------------------------]
EAX: 0xf7fa7de0 --> 0xffffdd9c --> 0xffffdf41 ("COLORFGBG=15;default")
EBX: 0x0
ECX: 0x8fd793fa
EDX: 0xffffdd24 --> 0x0
ESI: 0x1
EDI: 0xf7fa6000 --> 0x1b5db0
EBP: 0x0
ESP: 0xffffdcfc --> 0xf7e08497 (<__libc_start_main+247>: add esp,0x10)
EIP: 0x8048410 (<main>: push 0xaaaaaaaa)
EFLAGS: 0x296 (carry PARITY ADJUST zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x804840b <frame_dummy+43>: xchg ax,ax
0x804840d <frame_dummy+45>: xchg ax,ax
0x804840f <frame_dummy+47>: nop
=> 0x8048410 <main>: push 0xaaaaaaaa
0x8048415 <main+5>: push 0x804969c
0x804841a <main+10>: mov edx,DWORD PTR [esp+0x4]
0x804841e <main+14>: push edx
0x804841f <main+15>: push 0x80496a0
[------------------------------------stack-------------------------------------]
0000| 0xffffdcfc --> 0xf7e08497 (<__libc_start_main+247>: add esp,0x10)
0004| 0xffffdd00 --> 0x1
0008| 0xffffdd04 --> 0xffffdd94 --> 0xffffdf0b ("/home/gabor/cambridge/work/1b/compilers/gas_asm/poc.x")
0012| 0xffffdd08 --> 0xffffdd9c --> 0xffffdf41 ("COLORFGBG=15;default")
0016| 0xffffdd0c --> 0x0
0020| 0xffffdd10 --> 0x0
0024| 0xffffdd14 --> 0x0
0028| 0xffffdd18 --> 0xf7fa6000 --> 0x1b5db0
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Temporary breakpoint 1, 0x08048410 in main ()
gdb-peda$ ni
[----------------------------------registers-----------------------------------]
EAX: 0xf7fa7de0 --> 0xffffdd9c --> 0xffffdf41 ("COLORFGBG=15;default")
EBX: 0x0
ECX: 0x8fd793fa
EDX: 0xffffdd24 --> 0x0
ESI: 0x1
EDI: 0xf7fa6000 --> 0x1b5db0
EBP: 0x0
ESP: 0xffffdcf8 --> 0xaaaaaaaa
EIP: 0x8048415 (<main+5>: push 0x804969c)
EFLAGS: 0x296 (carry PARITY ADJUST zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x804840d <frame_dummy+45>: xchg ax,ax
0x804840f <frame_dummy+47>: nop
0x8048410 <main>: push 0xaaaaaaaa
=> 0x8048415 <main+5>: push 0x804969c
0x804841a <main+10>: mov edx,DWORD PTR [esp+0x4]
0x804841e <main+14>: push edx
0x804841f <main+15>: push 0x80496a0
0x8048424 <main+20>: call 0x80482e0 <printf@plt>
[------------------------------------stack-------------------------------------]
0000| 0xffffdcf8 --> 0xaaaaaaaa
0004| 0xffffdcfc --> 0xf7e08497 (<__libc_start_main+247>: add esp,0x10)
0008| 0xffffdd00 --> 0x1
0012| 0xffffdd04 --> 0xffffdd94 --> 0xffffdf0b ("/home/gabor/cambridge/work/1b/compilers/gas_asm/poc.x")
0016| 0xffffdd08 --> 0xffffdd9c --> 0xffffdf41 ("COLORFGBG=15;default")
0020| 0xffffdd0c --> 0x0
0024| 0xffffdd10 --> 0x0
0028| 0xffffdd14 --> 0x0
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x08048415 in main ()
gdb-peda$ ni
[----------------------------------registers-----------------------------------]
EAX: 0xf7fa7de0 --> 0xffffdd9c --> 0xffffdf41 ("COLORFGBG=15;default")
EBX: 0x0
ECX: 0x8fd793fa
EDX: 0xffffdd24 --> 0x0
ESI: 0x1
EDI: 0xf7fa6000 --> 0x1b5db0
EBP: 0x0
ESP: 0xffffdcf4 --> 0x804969c --> 0xbbbbbbbb
EIP: 0x804841a (<main+10>: mov edx,DWORD PTR [esp+0x4])
EFLAGS: 0x296 (carry PARITY ADJUST zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x804840f <frame_dummy+47>: nop
0x8048410 <main>: push 0xaaaaaaaa
0x8048415 <main+5>: push 0x804969c
=> 0x804841a <main+10>: mov edx,DWORD PTR [esp+0x4]
0x804841e <main+14>: push edx
0x804841f <main+15>: push 0x80496a0
0x8048424 <main+20>: call 0x80482e0 <printf@plt>
0x8048429 <main+25>: add esp,0x10
[------------------------------------stack-------------------------------------]
0000| 0xffffdcf4 --> 0x804969c --> 0xbbbbbbbb
0004| 0xffffdcf8 --> 0xaaaaaaaa
0008| 0xffffdcfc --> 0xf7e08497 (<__libc_start_main+247>: add esp,0x10)
0012| 0xffffdd00 --> 0x1
0016| 0xffffdd04 --> 0xffffdd94 --> 0xffffdf0b ("/home/gabor/cambridge/work/1b/compilers/gas_asm/poc.x")
0020| 0xffffdd08 --> 0xffffdd9c --> 0xffffdf41 ("COLORFGBG=15;default")
0024| 0xffffdd0c --> 0x0
0028| 0xffffdd10 --> 0x0
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x0804841a in main ()
gdb-peda$ ni
[----------------------------------registers-----------------------------------]
EAX: 0xf7fa7de0 --> 0xffffdd9c --> 0xffffdf41 ("COLORFGBG=15;default")
EBX: 0x0
ECX: 0x8fd793fa
EDX: 0xaaaaaaaa
ESI: 0x1
EDI: 0xf7fa6000 --> 0x1b5db0
EBP: 0x0
ESP: 0xffffdcf4 --> 0x804969c --> 0xbbbbbbbb
EIP: 0x804841e (<main+14>: push edx)
EFLAGS: 0x296 (carry PARITY ADJUST zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x8048410 <main>: push 0xaaaaaaaa
0x8048415 <main+5>: push 0x804969c
0x804841a <main+10>: mov edx,DWORD PTR [esp+0x4]
=> 0x804841e <main+14>: push edx
0x804841f <main+15>: push 0x80496a0
0x8048424 <main+20>: call 0x80482e0 <printf@plt>
0x8048429 <main+25>: add esp,0x10
0x804842c <main+28>: mov eax,0x0
[------------------------------------stack-------------------------------------]
0000| 0xffffdcf4 --> 0x804969c --> 0xbbbbbbbb
0004| 0xffffdcf8 --> 0xaaaaaaaa
0008| 0xffffdcfc --> 0xf7e08497 (<__libc_start_main+247>: add esp,0x10)
0012| 0xffffdd00 --> 0x1
0016| 0xffffdd04 --> 0xffffdd94 --> 0xffffdf0b ("/home/gabor/cambridge/work/1b/compilers/gas_asm/poc.x")
0020| 0xffffdd08 --> 0xffffdd9c --> 0xffffdf41 ("COLORFGBG=15;default")
0024| 0xffffdd0c --> 0x0
0028| 0xffffdd10 --> 0x0
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x0804841e in main ()
gdb-peda$ ni
[----------------------------------registers-----------------------------------]
EAX: 0xf7fa7de0 --> 0xffffdd9c --> 0xffffdf41 ("COLORFGBG=15;default")
EBX: 0x0
ECX: 0x8fd793fa
EDX: 0xaaaaaaaa
ESI: 0x1
EDI: 0xf7fa6000 --> 0x1b5db0
EBP: 0x0
ESP: 0xffffdcf0 --> 0xaaaaaaaa
EIP: 0x804841f (<main+15>: push 0x80496a0)
EFLAGS: 0x296 (carry PARITY ADJUST zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x8048415 <main+5>: push 0x804969c
0x804841a <main+10>: mov edx,DWORD PTR [esp+0x4]
0x804841e <main+14>: push edx
=> 0x804841f <main+15>: push 0x80496a0
0x8048424 <main+20>: call 0x80482e0 <printf@plt>
0x8048429 <main+25>: add esp,0x10
0x804842c <main+28>: mov eax,0x0
0x8048431 <main+33>: ret
[------------------------------------stack-------------------------------------]
0000| 0xffffdcf0 --> 0xaaaaaaaa
0004| 0xffffdcf4 --> 0x804969c --> 0xbbbbbbbb
0008| 0xffffdcf8 --> 0xaaaaaaaa
0012| 0xffffdcfc --> 0xf7e08497 (<__libc_start_main+247>: add esp,0x10)
0016| 0xffffdd00 --> 0x1
0020| 0xffffdd04 --> 0xffffdd94 --> 0xffffdf0b ("/home/gabor/cambridge/work/1b/compilers/gas_asm/poc.x")
0024| 0xffffdd08 --> 0xffffdd9c --> 0xffffdf41 ("COLORFGBG=15;default")
0028| 0xffffdd0c --> 0x0
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x0804841f in main ()
gdb-peda$ ni
[----------------------------------registers-----------------------------------]
EAX: 0xf7fa7de0 --> 0xffffdd9c --> 0xffffdf41 ("COLORFGBG=15;default")
EBX: 0x0
ECX: 0x8fd793fa
EDX: 0xaaaaaaaa
ESI: 0x1
EDI: 0xf7fa6000 --> 0x1b5db0
EBP: 0x0
ESP: 0xffffdcec --> 0x80496a0 ("result is: %08X\n")
EIP: 0x8048424 (<main+20>: call 0x80482e0 <printf@plt>)
EFLAGS: 0x296 (carry PARITY ADJUST zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x804841a <main+10>: mov edx,DWORD PTR [esp+0x4]
0x804841e <main+14>: push edx
0x804841f <main+15>: push 0x80496a0
=> 0x8048424 <main+20>: call 0x80482e0 <printf@plt>
0x8048429 <main+25>: add esp,0x10
0x804842c <main+28>: mov eax,0x0
0x8048431 <main+33>: ret
0x8048432 <main+34>: xchg ax,ax
Guessed arguments:
arg[0]: 0x80496a0 ("result is: %08X\n")
arg[1]: 0xaaaaaaaa
arg[2]: 0x804969c --> 0xbbbbbbbb
arg[3]: 0xaaaaaaaa
[------------------------------------stack-------------------------------------]
0000| 0xffffdcec --> 0x80496a0 ("result is: %08X\n")
0004| 0xffffdcf0 --> 0xaaaaaaaa
0008| 0xffffdcf4 --> 0x804969c --> 0xbbbbbbbb
0012| 0xffffdcf8 --> 0xaaaaaaaa
0016| 0xffffdcfc --> 0xf7e08497 (<__libc_start_main+247>: add esp,0x10)
0020| 0xffffdd00 --> 0x1
0024| 0xffffdd04 --> 0xffffdd94 --> 0xffffdf0b ("/home/gabor/cambridge/work/1b/compilers/gas_asm/poc.x")
0028| 0xffffdd08 --> 0xffffdd9c --> 0xffffdf41 ("COLORFGBG=15;default")
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x08048424 in main ()