Noeud:GCC Internals, Noeud « Next »:Integrated Languages, Noeud « Previous »:GCC Commands, Noeud « Up »:The GNU Compiler Collection
In practice, even a simple compilation produces half a page of text when ran
with verbose output to explain what is happening at each stage of the compile.
Thus, it is necessary to demystify this process and explain the inner workings
of gcc
when ran with a simple compilation. With this
knowledge, you'll have a firmer understanding of how to examine and walk
through detailed compilations, making (and tweaking) changes if need be.
Throughout this chapter the different parts of the compiler have been unveiled; first the preprocessor, then the compiler, and assembler, and so on. Each of these components is a spearate process; each has its own functionality and responsibility, which we'll now encounter with an example of a full compile with verbose output.
There are a number of processes to make note of when invoking
gcc
with a C source file; the preprocessor cpp
;
the C compiler, cc1
; the GNU assembler, as
; and
the GNU linker, ld
. Externally it is not necessary to know
about these separate processes - gcc
will take care of this
without you needing to know. However, gcc
supports various
command line switches to enable the control of which parts of the
compilation process are used. Although it may appear convenient to gloss
over these details, knowing about them will make your awareness of how
gcc
works on a relativley low level much stronger, and if
errors occur you'll be more prepared to deal with them.
Consider the following listing of a sample compilation:
$ gcc main.c -v Reading specs from /usr/lib/gcc-lib/i486-suse-linux/2.95.2/specs gcc version 2.95.2 19991024 (release) /usr/lib/gcc-lib/i486-suse-linux/2.95.2/cpp -lang-c -v -D__GNUC__=2 -D__GNUC_MINOR__=95 -D__ELF__ -Dunix -D__i386__ -Dlinux -D__ELF__ -D__unix__ -D__i386__ -D__linux__ -D__unix -D__linux -Asystem(posix) -Acpu(i386) -Amachine(i386) -Di386 -D__i386 -D__i386__ -Di486 -D__i486 -D__i486__ main.c /tmp/ccxswtDi.i GNU CPP version 2.95.2 19991024 (release) (i386 Linux/ELF) #include "..." search starts here: #include <...> search starts here: /usr/local/include /usr/lib/gcc-lib/i486-suse-linux/2.95.2/include /usr/include End of search list. The following default directories have been omitted from the search path: /usr/include/g++ /usr/lib/gcc-lib/i486-suse-linux/2.95.2/../../../../ i486-suse-linux/include End of omitted list. /usr/lib/gcc-lib/i486-suse-linux/2.95.2/cc1 /tmp/ccxswtDi.i -quiet -dumpbase main.c -version -o /tmp/cc0pQ7AA.s GNU C version 2.95.2 19991024 (release) (i486-suse-linux) compiled by GNU C version 2.95.2 19991024 (release). /usr/i486-suse-linux/bin/as -V -Qy -o /tmp/ccwiNRyM.o /tmp/cc0pQ7AA.s GNU assembler version 2.9.5 (i486-suse-linux) using BFD version 2.9.5.0.24 /usr/lib/gcc-lib/i486-suse-linux/2.95.2/collect2 -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 /usr/lib/crt1.o /usr/lib/crti.o /usr/lib/gcc-lib/i486-suse-linux/2.95.2/crtbegin.o -L/usr/lib/gcc-lib/i486-suse-linux/2.95.2 -L/usr/i486-suse-linux/lib /tmp/ccwiNRyM.o -lgcc -lc -lgcc /usr/lib/gcc-lib/i486-suse-linux/2.95.2/crtend.o /usr/lib/crtn.o $
Don't be intimidated! Although the output may appear daunting at first, the process when broken into logical divisions becomes much easier to understand. Since we've already discussed the different phases of compilation, all we really need to do is pick out which sections of the compilation are dealing with specific phases. It couldn't be easier; looking at the compilation, there are a number of points to make:
specs
file
The first thing that is read is the specs
file. It's purpose is
to define rules for compilation. It is extremely unlikely that you'll
need to tweak the specs
file, and you should avoid it unless
necessary. And if you do know how to tweak it, chances are you don't
need to read this section...
cpp
, the C preprocessor
The section
/usr/lib/gcc-lib/i486-suse-linux/2.95.2/cpp -lang-c -v -D__GNUC__=2 -D__GNUC_MINOR__=95 -D__ELF__ -Dunix -D__i386__ -Dlinux -D__ELF__ -D__unix__ -D__i386__ -D__linux__ -D__unix -D__linux -Asystem(posix) -Acpu(i386) -Amachine(i386) -Di386 -D__i386 -D__i386__ -Di486 -D__i486 -D__i486__ main.c /tmp/ccxswtDi.i
which calls the preprocessor, cpp
, with a host of preprocessor
options, passing main.c
through the preprocessor and creating a
file named /tmp/ccxswtDi.i
. Notice that CPP passes
-lang-c
as one of its options; it's saying "the following
source file is a C file". Calling any of the integrated languages
will ensure that the -lang-...
command will be called,
providing that gcc
recognises the file extension for that
source file.
The preprocessed file is placed in a temporary file named
/tmp/ccxswtDi.i
; this will be used by the compiler to generate assembly
code. Note here that each -D
instance is defining a constant (see
Defining Constants); there are also a number of architectural flags
(-A
). There's no real need to have to worry about these flags - they
are dealt with for you, and you shouldn't have to bother with them.
cc1
, the C compiler
With /tmp/ccxswtDi.i
now created, it is passed to the C compiler;
it's output is to produce a file named /tmp/cc0pQ7AA.s
which will
contain the assembly code:
/usr/lib/gcc-lib/i486-suse-linux/2.95.2/cc1 /tmp/ccxswtDi.i -quiet -dumpbase main.c -version -o /tmp/cc0pQ7AA.s
It creates as its output file /tmp/cc0pQ7AA.s
, the assembly file.
as
, the GNU assembler
/tmp/cc0pQ7AA.s
is now passed to the assembler to produce an object
file, named /tmp/ccwiNRyM.o
:
/usr/i486-suse-linux/bin/as -V -Qy -o /tmp/ccwiNRyM.o /tmp/cc0pQ7AA.s
collect2
collect2
is a utility to arrange calling of initialisation
functions at start time. It links the program and looks for these
functions, creating a table of them in a temporary .c
file, and
then links the program a second time but including the new
file. ld
is called from collect2
at the end of this process.
- NOTE
- Each of these processes is called with a number of flags that you'll not find in the
gcc
man pages. The reason for this is quite simple: they're flags specific to that process, and are nothing to do withgcc
. In fact you can find out exactly how to use these flags by calling the process involved with--help
, for example, when I invoke
cc1 --help
I get the full listing of the commands available for
cc1
. Although generally unnecessary to know these flags, it is helpful to know how to find out about the options available, which you may need to tweak and change.
Although gcc
handles these details for you, it is worth getting a
fuller grasp on what happens when gcc
is invoked; the separate
components enable you to monitor and watch each of the separate phases of
compilation, such that (if necessary) you can cut out the text from a compile,
change the flags and then paste the text back in and run it again with
different flags.
The example compilation just shown was produced from a C source file; in
practice, there is relatively little difference between this and, for example,
the compilation of a Fortran source file compiled using g77
(g77
is the GNU Fortran compiler). The most obvious
difference is what is used to compile the source file; cc1
for
C sources, f771
for Fortran sources, jc1
for Java
sources etc..