|
|
...making Linux just a little more fun! Using the GNU Compiler Collection
Introduction to GCCThe GNU C compiler is an integral part of the GNU system and was initially written by Richard Stallman. At first it only compiled C code. Later a group of volunteers started maintaining it and GCC gained the ability to support different languages such a C++, Fortran, Ada and Java. It was then renamed to GNU Compiler Collection to signify this change. In this article we shall look at mainly the C language compiler. GCC is not only available on Linux but also on other Unix-like systems such as FreeBSD, NetBSD,OpenBSD as well as on Windows via Cygwin, MingW32 and Microsoft Services for Unix. It supports a wide variety of platforms such as the Intel x86 Architecture, AMD x86-64 ,Alpha and SPARC architectures. Due to this versatility of GCC, it is often used for cross compiling code for different architectures. Since the GCC source code is available and modular, it can easily be modified to emit binaries for obscure or new platforms, such as those used in embedded systems. Basic compilation optionsIf GCC is available on your system, you can give the following command to see with what options it has been compiled with. Command 1 - GCC specification and supported functionality$ gcc -v Reading specs from /usr/lib/gcc-lib/i586-suse-linux/3.3.3/specs Configured with: ../configure --enable-threads=posix --prefix=/usr \ --with-local-prefix=/usr/local --infodir=/usr/share/info \ --mandir=/usr/share/man --enable-languages=c,c++,f77,objc,java,ada \ --disable-checking --libdir=/usr/lib --enable-libgcj \ --with-gxx-include-dir=/usr/include/g++ --with-slibdir=/lib \ --with-system-zlib --enable-shared --enable-__cxa_atexit i586-suse-linux Thread model: posix gcc version 3.3.3 (SuSE Linux) This gives a lot of information about GCC. You can see that the POSIX threading model is supported by this version so you can compile multi-threaded applications with it. It can also compile code written in C,C++,Fortran-77, Objective C, Java and Ada. Notice that the C++ include path is also specified, and that Java code can be compiled to native binaries with libgcj. Let us write a small C program with a header file to see the various compilation options GCC supports. // helloworld.h #define COUNT 2 static char hello[] = "hello world";
// helloworld.c
#include <stdio.h>
#include "helloworld.h"
int main()
{
int i;
for(i = 0;i <= COUNT; i++)
{
printf("%s - %d\n",hello,i);
}
return 0;
}
To compile the helloworld program to an object file we can give the command Command 2 - Creating an Object File
$ gcc -v -c helloworld.c
...[output snipped]
/usr/lib/gcc-lib/i586-suse-linux/3.3.3/cc1 -quiet -v -D__GNUC__=3 -D__GNUC_MINOR__=3 \
-D__GNUC_PATCHLEVEL__=3 helloworld.c -quiet -dumpbase helloworld.c -auxbase helloworld \
-version -o /tmp/ccHmbDAJ.s
GNU C version 3.3.3 (SuSE Linux) (i586-suse-linux)
compiled by GNU C version 3.3.3 (SuSE Linux).
GGC heuristics: --param ggc-min-expand=42 --param ggc-min-heapsize=23825
#include "..." search starts here:
#include <...> search starts here:
/usr/local/include
/usr/lib/gcc-lib/i586-suse-linux/3.3.3/include
/usr/i586-suse-linux/include
/usr/include
End of search list.
/usr/lib/gcc-lib/i586-suse-linux/3.3.3/../../../../i586-suse-linux/bin/as -V -Qy \
-o helloworld.o /tmp/ccHmbDAJ.s
GNU assembler version 2.15.90.0.1.1 (i586-suse-linux) using BFD version 2.15.90.0.1.1
20040303 (SuSE Linux)
Command 3 - Creating an Executable File
$ gcc -v -o helloworld helloworld.c ...[output snipped] /usr/lib/gcc-lib/i586-suse-linux/3.3.3/collect2 --eh-frame-hdr -m elf_i386 -dynamic-linker \ /lib/ld-linux.so.2 -o helloworld /usr/lib/gcc-lib/i586-suse-linux/3.3.3/../../../crt1.o \ /usr/lib/gcc-lib/i586-suse-linux/3.3.3/../../../crti.o \ /usr/lib/gcc-lib/i586-suse-linux/3.3.3/crtbegin.o -L/usr/lib/gcc-lib/i586-suse-linux/3.3.3 \ -L/usr/lib/gcc-lib/i586-suse-linux/3.3.3/../../../../i586-suse-linux/lib \ -L/usr/lib/gcc-lib/i586-suse-linux/3.3.3/../../.. /tmp/ccUyu9EA.o -lgcc \ -lgcc_eh -lc -lgcc -lgcc_eh /usr/lib/gcc-lib/i586-suse-linux/3.3.3/crtend.o \ /usr/lib/gcc-lib/i586-suse-linux/3.3.3/../../../crtn.o From the above output, we can see that When creating a executable file an extra step is involved -
Linking. From the output of command 2 we can see that file is
dynamically linked with the libraries (note the usage of
The role of the PreprocessorThe preprocessor is an important part of the C compiler. All the
preprocessor directives start with a '#' (hash) sign. It processes
the different preprocessor directives such as $ gcc -E helloworld.c > helloworld.c.preprocessCommand 5 - Expanded Macros (#define) $ gcc -E helloworld.c -dM | sort | less Command 4 will produce a large preprocessed file with all the
included files and all the expanded macros. You can open the file
in your favorite editor and take a look at it. This the C source
the C compiler looks at. When I ran the above command on my desktop
machine it produced 455 lines of output excluding whitespace.
Command 5 shows all the Generating output in Assembly languageGCC converts the C code into assembly language before converting into binary code. In some instances you might want to look at the code generated or tweak it for performance reasons before finally converting into binary code. You can do it using the following command. $ gcc -S helloworld.c The output generated is as follows:
.file "helloworld.c"
.data
.type hello, @object
.size hello, 12
hello:
.string "hello world"
.section .rodata
.LC0:
.string "%s - %d\n"
.text
.globl main
.type main, @function
main:
pushl %ebp
movl %esp, %ebp
subl $8, %esp
andl $-16, %esp
movl $0, %eax
subl %eax, %esp
movl $0, -4(%ebp)
.L2:
cmpl $2, -4(%ebp)
jle .L5
jmp .L3
.L5:
subl $4, %esp
pushl -4(%ebp)
pushl $hello
pushl $.LC0
call printf
addl $16, %esp
leal -4(%ebp), %eax
incl (%eax)
jmp .L2
.L3:
movl $0, %eax
leave
ret
.size main, .-main
.section .note.GNU-stack,"",@progbits
.ident "GCC: (GNU) 3.3.3 (SuSE Linux)"
From the output above, we can see that Conformance and warning optionsGCC has it's own extensions to the C standard. These extensions
are used by many GNU programs as well as other software including
the Linux kernel. These extensions may not be available with other
compilers on other platforms. So if you want to write portable
code, you might want to use the Also it is always a good idea to turn some common warnings on,
using Generating Makefile dependencies
$ gcc -M helloworld.c helloworld.o: helloworld.c /usr/include/stdio.h /usr/include/features.h \ /usr/include/sys/cdefs.h /usr/include/gnu/stubs.h \ /usr/lib/gcc-lib/i586-suse-linux/3.3.3/include/stddef.h \ /usr/include/bits/types.h /usr/include/bits/wordsize.h \ /usr/include/bits/typesizes.h /usr/include/libio.h \ /usr/include/_G_config.h /usr/include/wchar.h /usr/include/bits/wchar.h \ /usr/include/gconv.h \ /usr/lib/gcc-lib/i586-suse-linux/3.3.3/include/stdarg.h \ /usr/include/bits/stdio_lim.h /usr/include/bits/sys_errlist.h \ helloworld.h This nifty little trick can save a lot of time when you are working against a stiff deadline. Using library codeIf you are developing a library for use by other programmers you
need to use the On most systems, the default behavior of gcc is to link
dynamically. This can create problems if you do not want to
distribute the shared library along with the executable. Also you
might be in a situation where the shared library you have used on
your system is not readily available. In such situation we can
statically link the executable so that the library code need not be
separately provided. But use this option with care as it will
increase the size of the executable by quite a bit. The command
option to statically link the output in gcc is (predictably)
ConclusionIn this article we have seen a small overview of how gcc can be used to generate binaries and the various stages the C code goes through before being converted into binary code. In the next part in this series we will look at how the code generated can be optimized for a particular platform as well as options to generate debug binaries for use with gdb.
|
Vinayak Hegde is currently working for Akamai Technologies Inc. He
first stumbled upon Linux in 1997 and has never looked back since. He
is interested in large-scale computer networks, distributed computing
systems and programming languages. In his non-existent free time he
likes trekking, listening to music and reading books. He also
maintains an intermittently updated