To the best of my knowledge, x86-64 requires the stack to be 16-byte aligned before a call, while gcc with -m32
doesn't require this for main.
I have the following testing code:
.data
intfmt: .string "int: %d\n"
testint: .int 20
.text
.globl main
main:
mov %esp, %ebp
push testint
push $intfmt
call printf
mov %ebp, %esp
ret
Build with as --32 test.S -o test.o && gcc -m32 test.o -o test
. I am aware that syscall write exists, but to my knowledge it cannot print ints and floats the way printf can.
After entering main, a 4 byte return address is on the stack. Then interpreting this code naively, the two push calls each put 4 bytes on the stack, so call needs another 4 byte value pushed to be aligned.
Here is the objdump of the binary generated by gas and gcc:
0000053d <main>:
53d: 89 e5 mov %esp,%ebp
53f: ff 35 1d 20 00 00 pushl 0x201d
545: 68 14 20 00 00 push $0x2014
54a: e8 fc ff ff ff call 54b <main+0xe>
54f: 89 ec mov %ebp,%esp
551: c3 ret
552: 66 90 xchg %ax,%ax
554: 66 90 xchg %ax,%ax
556: 66 90 xchg %ax,%ax
558: 66 90 xchg %ax,%ax
55a: 66 90 xchg %ax,%ax
55c: 66 90 xchg %ax,%ax
55e: 66 90 xchg %ax,%ax
I am very confused about the push instructions generated.
- If two 4 byte values are pushed, how is alignment achieved?
- Why is 0x2014 pushed instead of 0x14? What is 0x201d?
- What does
call 54b
even achieve? Output ofhd
matchesobjdump
. Why is this different in gdb? Is this the dynamic linker?
B+>│0x5655553d <main> mov %esp,%ebp │
│0x5655553f <main+2> pushl 0x5655701d │
│0x56555545 <main+8> push $0x56557014 │
│0x5655554a <main+13> call 0xf7e222d0 <printf> │
│0x5655554f <main+18> mov %ebp,%esp │
│0x56555551 <main+20> ret
Resources on what goes on when a binary is actually executed are appreciated, since I don't know what's actually going on and the tutorials I've read don't cover it. I'm in the process of reading through How programs get run: ELF binaries.
mov %esp, %ebp
without saving / restoring the caller's%ebp
is bad and could easily lead to a segfault after main returns. – Peter Cordesgcc -O1 -fverbose-asm -S
to get assembler code. Read the relevant x86 ABI specification – Basile Starynkevitch-O1
will get rid of. – qwr-O1
. Notice that the assembler is generated bygcc
from somefoo.c
source code intofoo.s
aftergcc -fverbose-asm -S foo.c
(and you could add-O1
). I did mention C code (not assembler) to understand what kind of assembler codegcc
is generating – Basile Starynkevitchas
to create object file andgcc
to create executable, no C source at all? – qwr