GNU/Linux Application Programming (Programming Series)
The GNU compiler takes a number of different stages in the process of building an object. These stages can be filtered down to four: preprocessing, compiling, assembling, and linking (see Figure 4.1).
The preprocessing, compiling, and assembling stages are commonly collected together into one phase, but they re shown as independent here to illustrate some of the capabilities of GCC. Table 4.1 identifies the input files and files that result.
In the preprocessing stage, the source file ( *.c ) is preprocessed with the include files ( .h headers). At this stage, directives such as #ifdef , #include , and #define are resolved. The result is an intermediate file. Usually, this file isn t externally generated at all, but we show it here for completeness. With the source file now preprocessed, it can be compiled into assembly in the compiling stage ( *.s ). The assembly file is then converted into machine instructions in the assembling stage, resulting in an object file ( *.o ). Finally, the machine code is linked together ( potentially with other machine code objects or object libraries) into an executable binary.
Stage | Input | Output | GCC Example |
---|---|---|---|
Preprocessing | *.c | *.i | gcc -E |
Compiling | *.i | *.s | GCC -S test.i -o test.s |
Assembling | *.s | *.o | GCC -c test.s -o test.o |
Linking | *.o | * | GCC test.o -o test |
That s enough preliminaries . Let s now dig into GCC and see the variety of ways it can be used. We ll first look at a number of patterns that illustrate GCC in use, and then we ll explore some of the most useful options of GCC. This includes options for debugging, enabling various warnings, and optimizations. We ll then investigate a number of GNU tools that are related to GCC.
Patterns for GCC (Compile, Compile and Link)
The simplest example from which to begin is the compilation of a C source file to an image. In this example, the entire source necessary is contained within the single file, so we use GCC as follows :
$ GCC test.c -o test
Here we compile the
$ GCC -c test.c
By default, the resulting object is named test.o , but we could force the output of the object to newtest.o , as:
$ GCC -c test.c -o newtest.o
Most programs we ll develop will involve more than one file. GCC handles this easily on the command line as:
$ GCC -o image first.c second.c third.c
Here we compile three source files ( first.c , second.c , and third.c ) and link them together into the executable named image .
Note | In all examples where an executable will result, all C programs require a function called main . This is the main entry point for the program and should appear once in all the files to be compiled and linked together. When simply compiling a source file to an object, the link phase is not yet performed, and therefore the main function is not necessary. |
Useful Options
In many cases, we ll keep our header files in a directory that s separate from where we keep our source files. Consider an example where our source is kept in a subdirectory called ./src , and at the same level is a directory where our include files are kept, ./inc . We can tell GCC that the headers are provided here (while compiling within the ./src subdirectory as:
$ gcc test.c -I../inc -o test
We could specify numerous include subdirectories using multiple -I specs :
$ gcc test.c -I../inc -I../../inc2 -o test
Here we specify another include subdirectory called inc2 that is two directories up from our current directory.
For configuration of software, we can specify symbolic constants on the compile line. For example, defining a symbolic constant in source or header as
#define TEST_CONFIGURATION
could be just as easily defined on the command line using the -D option as:
$ gcc -DTEST_CONFIGURATION test.c -o test
The advantage to specifying this on the command line is that we need not modify any source to change its behavior (as specified by the symbolic constant).
One final useful option provides us with the means to emit a source and assembly interspersed listing. Consider the following command line:
$ gcc -c -g -Wa,-ahl,-L test.c
Most interesting in this command is the -Wa option, which passes the subsequent options to the assembler stage to intersperse the C source with assembly.
Compiler Warnings
While the GCC compiler will abort the compilation process if an error is detected, the discovery of warnings indicates potential problems that should be fixed, though it might still result in a working executable. GCC provides a very rich warning system, but it must be enabled to take advantage of the full spectrum of warnings that can be detected .
The most common use of GCC for finding common warnings is the -Wall option. This turns on all warnings of a given type, which consists of the most generally encountered issues in applications. Its use is this:
$ gcc -Wall test.c -o test
Option | Purpose |
---|---|
unused-function | Warn of undefined but declared static function. |
unused-label | Warn of declared but unused label. |
unused-parameter | Warn of unused function argument. |
unused-variable | Warn of unused locally declared variable |
unused-value | Warn of computed but unused value. |
format | Verify that the format strings of printf and so on have valid arguments based upon the types specified in the format string. |
implicit-int | Warn when a declaration doesn t specify a type. |
implicit-function- | Warn of a function being used prior to its declaration. _declaration |
char-subscripts | Warn if an array is subscripted by a char (common error considering that the type is signed). |
missing-braces | Warn if an aggregate initializer is not fully bracketed. |
parentheses | Warn of omissions of ()s if they could be ambiguous. |
return-type | Warn of function declarations that default to int or functions that lack a return, which note a return type. |
sequence-point | Warn of code elements that are suspicious (such as a[i] = c[i++]; ). |
switch | In switch statements that lack a default, warn of missing cases that would be present in the switch argument. |
strict-aliasing | Use strictest rules for aliasing of variables (such as trying to alias a void* to a double ). |
unknown-pragmas | Warn of #pragma directives that are not recognized. |
uninitialized | Warn of variables that are used but not initialized (enabled only with -O2 optimization level). |
A synonym for -Wall is ”all-warnings . Table 4.2 lists the plethora of warning options that are enabled within -Wall .
Note that most options also have a negative form, so that they can be disabled (if on by default or covered by an aggregate option such as -Wall ). For example, if we wanted to enable -Wall but disable the unused warning set, we could specify this as follows:
$ gcc -Wall -Wno-unused test.c -o test
Option | Purpose |
---|---|
cast-align | Warn whenever a pointer is cast and the required alignment is increased. |
sign-compare | Warn if a signed vs. unsigned compare could yield an incorrect result. |
missing- prototypes | Warn if a global function is used without a previous prototype definition. |
packed | Warn if a structure is provided with the packed attribute and no packing occurs. |
padded | Warn if a structure is padded to align it (resulting in a larger structure). |
unreachable-code | Warn if code is found that can never be executed. |
inline | Warn if a function marked as inline could not be inlined. |
disabled-optimization | Warn that the optimizer was not able to perform a given optimization (required too much time or resources to perform). |
Numerous other warnings can be enabled outside of -Wall . Table 4.3 provides a list of some of the more useful options and their descriptions.
One final warning option that can be very useful is -Werror . This option specifies that instead of simply issuing a warning if one is detected, the compiler will instead treat all warnings as errors and abort the compilation process. This can be very useful to ensure the highest quality code and is therefore recommended.
Категории