SPO 600 - Lab 2
In lab 2, I will be looking at how different compiler options would affect the executable. I will be using gcc in this lab, and this lab is running on x86_64 architecture.
First, let's start with a simple C hello world program:
Next, I'll compile it with the option: gcc -g -O0 -fno-builtin -o hello hello.c
(Note: -g enables the debugging option, -O0 stands for no optimization, and -fno-builtin stands for don't use built in function optimizations)
If I run the executable by entering ./hello, it should give me:
Hello World!
Using objdump --source hello, I can find the <main> section contains the code I wrote:
And using objdump -s hello, I can also find where the string is stored (It's stored in .rodata, or read-only data segment):
Right now I'll make a couple changes to see how the option would affect the executable.
1. Add the -static option
When I recompile it as hello1, I noticed the hello1 is much, much bigger than hello!
First, let's start with a simple C hello world program:
#include <stdio.h>
int main() {
printf("Hello World!\n");
}
Next, I'll compile it with the option: gcc -g -O0 -fno-builtin -o hello hello.c
(Note: -g enables the debugging option, -O0 stands for no optimization, and -fno-builtin stands for don't use built in function optimizations)
If I run the executable by entering ./hello, it should give me:
Hello World!
Using objdump --source hello, I can find the <main> section contains the code I wrote:
And using objdump -s hello, I can also find where the string is stored (It's stored in .rodata, or read-only data segment):
Right now I'll make a couple changes to see how the option would affect the executable.
1. Add the -static option
When I recompile it as hello1, I noticed the hello1 is much, much bigger than hello!
Yes, they do the same thing, but hello1 is 102.261 KB, while the original hello is only 1.325 KB. |
Also, in the hexdump of hello1 and the <main>, the function call for <printf> became <_IO_printf>. It is largely because of the -static option, as it actually copies all the functions into the assembly code.
5. Move the printf() inside a function called output()
When I recompile it as hello5 and open it with objdump, the
<main> would call <output> and prints “Hello world!”.
6. Replace -O0 with -O3
2. Remove
the -fno-builtin flag (i.e. no
built-in function optimization)
When I recompile
it as hello2 and , the file size is pretty much the same, but when I open it with objdump, I noticed in the <main>
section, the <printf@plt> is replaced <puts@plt>.
According to the GCC documentation, “Many of these functions are only optimized in certain cases; if
they are not optimized in a particular case, a call to the library function
will be emitted.” Although both printf() and puts() are built-in functions, the
former isn’t optimized for what I’m using it for. Since I’m not outputting any
variables, GCC simply chooses puts() for me.
3. Remove
the -g flag (i.e. No debugging
information)
When I recompile it as hello3, I noticed the file
is much smaller (1.021KB), since it no longer contains any debugging information.
4. Add 10 arguments to the printf() function
When I recompile it as hello4 and open it with objdump, I noticed the 10
arguments are showing in <main>, and they’re stored in the following
registers: %esi, %edx, %ecx, %r8d, %r9d (Note: they’re counting from back to
front)
Just in case you’re wondering where are the remaining 5,
they’re all stored in a stack. (That’s also why they’re using pushq instead of mov.)
|
5. Move the printf() inside a function called output()
Look closely at the callq in <main>, and you can see it's calling 4004d7, which is the register of <output>. |
6. Replace -O0 with -O3
When I recompile it as hello6 and open it with objdump, I noticed in the <main> section, the "mov $0x0,%eax" is replaced by "xor %eax,%eax", which is a more efficient way to set %eax to 0. It also uses sub instead of push.
Furthermore, hello6 runs faster than the original hello.
Comments
Post a Comment