oreilly.comSafari Books Online.Conferences.


Getting Familiar with GCC Parameters
Pages: 1, 2, 3, 4

Options Controlling Compilation Stages

For learning purposes, sometimes you want to know how your source code is transformed into an executable. Fortunately, gcc provides you options to stop at any processing stage. Recall that gcc has several stages to be accomplished--for example, linking. The options are:

  • -c stops at assembly phase but skips linking. The result is an object code.
  • -E stops after preprocessing stage. All preprocessing directives are expanded so you only see plain code.
  • -S stops after compilation. It leaves you with assembler code

-c is mostly used when you have multiple source files and combine them to create the final executable. So, instead of:

$ gcc -o final-binary test1.c test2.c

it would be better to split them as:

$ gcc -c -o test1.o test1.c
$ gcc -c -o test2.o test2.c

and then:

$ gcc -o final-binary ./test1.o ./test1.o

You probably notice the same sequence if you build the program using a Makefile. The advantage of using -c is clear: you only need to recompile the changed source files. The only phase that has to be redone is linking all the object files, and that greatly saves time, especially in large projects. An obvious example of this is the Linux kernel.

-E is useful if you want to see how your code really looks after macros, definitions, and such are expanded. Take Listing 3 as an example.


#define A 2
#define B 4
#define calculate(a,b) a*a + b*b

void plain_dummy()
    printf("Just a dummy\n");

static inline justtest()

int main(int argc, char *argv[])
#ifdef TEST
    printf("%d\n", calculate(A,B));
    return 0;

Listing 3. Code contains #define and #ifdef

We compile it like this:

$ gcc -E -o listing2.e listing2.c

Notice that we don't pass any -D parameters, which means TEST is undefined. So what do we have in the preprocessed file?

void plain_dummy()
    printf("Just a dummy\n");

static inline justtest()

int main(int argc, char *argv[])
    printf("%d\n", 2*2 + 4*4);
    return 0;

Where is the call to justtest() inside main()? Nowhere. TEST is undefined--that's why the code is eliminated. You can also see that the calculate() macro is already expanded into multiplication and addition of constant numbers. In final executable form, this number will be replaced with the operation result. As you see, -E is quite handy to double-check the correctness of directives.

Notice that plain_dummy() is still there even though it is never called. No surprise since no compilation happens here, therefore dead code elimination doesn't happen at this stage. stdio.h is also expanded but it isn't shown in the above listing.

I found an interesting application of -E as an HTML authoring tool [11]. In short, it helps you to adopt common programming practices such as code modularization and macros into the HTML world--something that cannot be done using plain HTML coding.

-S gives you assembly code, much like what you see with objdump -d/-D. However, with -S, you still see directives and symbol names, which makes it easier to study the code. For example, a call like printf("%d\n", 20) could be transformed into:

.section        .rodata.str1.1,"aMS",@progbits,1
        .string "%d\n"
        movl    $20, 4(%esp)
        movl    $.LC0, (%esp)
        call    printf

You can see that format string %d is placed in a read-only data section (.rodata). Also, you can confirm that arguments are pushed to the stack from right to left, with the format string at the top of the stack.


gcc gives us many useful options to make our code into whatever we like. By understanding what these options really do, we can make the program faster and slimmer. However, do not depend entirely on them: you should pay more attention to writing efficient and well-structured code.


I would like to thank the communities in the OFTC chat room (#kernelnewbies and #gcc) and #osdev (Freenode) for their valuable ideas.


  1. Wikipedia's article about Preprocessing
  2. Wikipedia's article about Compilation
  3. Wikipedia's article about Assembler
  4. Wikipedia's article about Linker
  5. An example of code reordering using gcc
  6. Frame pointer omission (FPO) optimization and consequences when debugging, Part 1 and Part 2
  7. Explanation of DWARF
  8. Explanation of stabs
  9. Explanation of COFF
  10. Explanation of XCOFF (a COFF variant)
  11. Using a C preprocessor as an HTML authoring tool
  12. gcc online documentation
  13. AMD Athlon Processor x86 Code Optimization Guide

Mulyadi Santosa is a freelance writer who lives in Indonesia.

Return to

Sponsored by: