So it was that we wrote a simple Hello World program. And it was good. Then we compiled it:
gcc -g -O0 -fno-builtin hello.c -o hello
And it did compile. Now, the -g enables debugging information, the -O0 tells the compiler not to optimize (numbers greater than zero would have optimized the program: -O1 would do basic optimization, -O2 would do all the safe optimizations, and -O3 would perform aggressive optimization), and the -fno-builtin tells it not to use builtin function optimizations. So, we got an unoptimized binary ELF file with debugging information. Let’s examine it!
We were told to use objdump with the -f, -s, -d, or –source options in order to examine the file. We did. It was a good file.
Now, we were to use objdump to examine the changes that would occur for the following:
- Adding the compiler option -static:
The resulting binary file was much larger than the original. The cause of this was that the -static option brought in all the environment variables and libraries needed to run the program.
- Removing the compiler option -fno-builtinMost noticeable was that calls to the printf function in the ELF file were instead calls to the puts function. This is a function optimization based on the fact that the puts function is faster than printf. When printf is supposed just to output a string without any variables, the compiler calls puts instead, which skips past the whole hassle of formatting the output string and just puts it there.
- Removing the compiler option -gWithout debugging information, the resultant binary file was significantly smaller. The ELF did not have the .debug_ sections that the first file had.
- Adding additional arguments to the printf() function.The first few arguments are stored in the registers while the rest are stored on the stack.
- Moving the printf() call to a separate function called output().we saw that the <main> section was smaller and had only the call to output. The <output> section was identical to the original <main> section, that is, it stored the “Hello World” string in memory and called the printf() function.
- Removing -O0 and adding -O3.The binary file was even larger. We are optimizing for speed, then, not size. The disassembled code was quite different. The compiler performed several optimizations to make the code execute faster. For example, the unoptimized ELF uses
mov $0x0,%eaxto clear the register %eax, while the optimized file does