I\'m primarily interested in popular and widely used compilers, such as gcc. But if things are done differently with different compilers, I\'d like to know that, too.
According to chapter 2 of Introduction to Reverse Engineering Software (by Mike Perry and Nasko Oskov), both gcc and cl.exe (the back end compiler for MSVC++) have the -S switch you can use to output the assembly that each compiler produces.
You can also run gcc in verbose mode (gcc -v
) to get a list of commands that it executes to see what it's doing behind the scenes.
Java compilers compile to java byte code (binary format) and then run this using a virtual machine (jvm).
Whilst this may seem slow it - it can be faster because the JVM can take advantage of later CPU instructions and new optimizations. A C++ compiler won't do this - you have to target the instruction set at compile time.
Compilers, in general, parse the source code into an Abstract Syntax Tree (an AST), then into some intermediate language. Only then, usually after some optimizations, they emit the target language.
About gcc, it can compile to a wide variety of targets. I don't know if for x86 it compiles to assembly first, but I did give you some insight onto compilers - and you asked for that too.
There are many phases of compilation. In abstract, there is the front end that reads the source code, breaks it up into tokens and finally into a parse tree.
The back end is responsible for first generating a sequential code like three address code eg:
code:
x = y + z + w
into:
reg1 = y + z
x = reg1 + w
Then optimizing it, translating it into assembly and finally into machine language. All steps are layered carefully so that when needed, one of them can be replaced
Some of the above answers confused me because in some answers GCC(GNU Compiler Collection) is mentioned as a single tool but it's a suite of tools like GNU Assembler(also known as GAS), linker, compiler and debugger which are used together to produce an executable. And yes, GCC doesn't directly converts the C source file to machine code.
It does that in 4 steps:
In most multi-pass compilers assembly language is generated during the code generation steps. This allows you to write the lexer, syntax and semantic phases once and then generate executable code using a single assembler back end. this is used a lot in cross compilers such a C compilers that generates for a range of different cpu's.
Just about every compiler has some form of this wheter its an implicit or explicity step.