A simple example with gcc and objdump
12 Jan 2015In today’s post, I want to present a dead-simple C program that we’ll compile into an object file and then use objdump to give us some assembly code. I’ll then take you through the generated assembly.
Using objdump
According to its manpage, objdump is used to dsplay information from object files. It has a whole host of different switches that you can supply to interrogate object files, but we’ll only have a very simple usage for it in this post.
I prefer Intel assembly syntax, so I’ll specify -M intel
. We want to disassemble the object file, so we’ll use -d
. It’s really helpful to also have the original source code intermixed with the assembly code, so we’ll turn that on with -S
.
Your command should look something like this
Simple example
The most basic program to look at is one that does nothing but return 0
back to the operating system.
Compiling this unit (ensuring to specify -g
to gcc for debug symbols) and then disassembling with objdump, we’re given back the following:
Whilst the whole block that gets dumped out is important, we’re really only worried about the inner implementation of the main
function call. The translation of this code is equally pretty simple.
Dissecting this code, we can see that the program first sets up the stack frame for the two parameters passed into main, argc
and argv
.
So, we save the previous rbp
to preserve its state.
And in accordance with the calling conventions for System V AMD64
The first six integer or pointer arguments are passed in registers RDI, RSI, RDX, RCX, R8, and R9, while XMM0, XMM1, XMM2, XMM3, XMM4, XMM5, XMM6 and XMM7 are used for floating point arguments.
Therefore, argc
being of type int
is therefore a DWORD
and is passed via edi
. argv
is a pointer and is a QWORD
; therefore it is passed using the 64 bit register rsi
.
Upon entry, we’re just filling up those spots in the stack.
Exiting we’re just setting our return value (which is always in the accumulator), restoring the pre-entry value that was in rbp
and returning to the caller.
Write another, more complex C program; disassemble it and see if you can follow along with the results.