- Real-Time Embedded - http://www.rt-embedded.com/blog -
Using the GNU Compiler Collection (gcc) – Part 2
Posted By Hai Shalom On March 11, 2010 @ 1:46 PM In Infrastructure | No Comments
This article presents additional flags you can specify to the compiler in order to customize the binary ouput and customize the behavior of the compiler.
As I mentioned in part 1 , we need to configure the compiler with more options, according to our requirements or needs. In this part, I will show the most common and useful directives and configurations in order to complete the compilation process as we require. There are many other flags which can be used for application specific requirements. These flags are documented in the GCC online documents site (see resources).
In most cases, we need to include header files which are not provided by the system. Such header files could be from other modules or libraries that we want to use. In this case, we are going to specify the –I directive. This directive may contain a long list of include directories. In case we want to specify a secondary location (another include directory that if a primary and a secondary locations have the same file, the compiler will take the one from the primary), we use the –idirafter directive.
Here’s an example:
|# gcc –c mysource.c –o myobject.o –I/usr/project/include –idirafter /usr/project/sec/include|
Debug symbols are required in case we want to use a debugger. These symbols can be used by GDB, a hardware debugger or one of the binutils for information extraction. Debug symbols make the object bigger due to the extra debug information which is embedded inside the object. However, when creating a final filesystem image for the target, all the executables are usually stripped from this information, which makes their size to be equivalent to their original non-debug size. The following example shows how to instruct the compiler to add debug symbols:
|# gcc –c mysource.c –o myobject.o –g -ggdb|
If you are a good programmer, you would like the compiler to warn you about everything, because almost every warning has a potential to be a real bug, and fixing them when they are young is the easiest and cheapest. Fixing something which already runs in the field is extremely expensive, not to mention the bad reputation. The default warning level used by gcc reports only syntax errors, but it won’t report other potential issues. Here are some directives to instruct the gcc regarding warnings:
My recommendation is to add the –Wall flag to the compilation process and fix all outstanding issues. The –w flag is dangerous because it will inhibit the compiler from reporting any issue (including real bugs). The –Werror flag is like a police officer mode. Once your code is stable and clean from warnings, you can enable this flag. Any changes that you or your team will do will not compile in case a new warning was introduced. This will enforce code quality (in terms of warnings only), but will make the programmers life hard, because it will fail the compilation for every nonsense. The gcc manual displays many warning types that can be disabled or enabled manually. There is no actual use for turning on or off an explicit warning, however, an example for this could be a case where you don’t want to see a specific warning type on a 3rd party code that you can’t modify.
What do we do in case there is an #ifdef which disables a required piece of code? What do we do in case the code expects a value in a macro and no one specifies it? How can we do that without changing the code? The answer is, by using the –D and
–U directives. The first defines a macro and the latter undefines a macro. Here are some examples:
|# gcc –c mysource.c –o myobject.o –DMAX_ARRAY_SIZE=16|
This will be useful in case our program defines an array, but doesn’t know of what size. The outsize definition will define the size and the code will support 16 places in the devices array:
|unsigned int devices[ MAX_ARRAY_SIZE ];|
In this example, we change the way the program works:
|# gcc –c mysource.c –o myobject.o –DENABLE_USB_DEVICE|
The code supports USB device, but by default this option is off. We use the command line definition to enable this code.
|int setup_devices( void )
int ret = 0;#ifdef ENABLE_USB_DEVICE
ret = usb_init();
#endif ret |= eth_init(); return ret;
Believe it or not, but the compiler’s objective is to minimize the work and memory consumption on the host, and it doesn’t care about the target! This is absurd, because we really don’t care about the host machine, which is a strong and powerful PC or server, but we do care about our resource limited target platform. For this reason, we must specify the optimization level we want to use on our target. Here are the optimizations levels:
When you debug something, you usually edit a file and want to rebuild and test. In this case, you’ll want that the system will compile only the file you changed, and nothing else, because building the whole project usually takes a lot of time. That’s why we have dependency files. Dependency files (usually with the extension of “.d”) contain a recursive list all the files that a source file includes. As you know, in case one of them is changed, there is a need to recompile this source file. Dependency files are used by Makefiles in order to determine whether a source file needs to be recompiled or not. This helps to speed up the build process of a project starting from the second time it is built, by skipping work which is not required. The generated dependency files are not used by the gcc, but only generated by the gcc. There are a few different flags which generate dependency files. My recommendation is to use the –MMD flag which generates the list of header files (without system headers which are not supposed to be changed). The following example will generate mysource.d:
|# gcc –c mysource.c –o myobject.o –MMD|
Specifying library search directories
Similarly to the include directory specification, you may want to tell the linker where to find all the libraries that your program requires. This is done by the –L flag. See the example in the next section.
When your program is using external functionality which is provided by a library, you must specify the library’s name in the final link command line. This is done by the –l flag, and the library’s core name. When I say “core name”, I mean that you need to specify the library name without lib prefix and the extension. Here’s an example of linking with a library called libmylib.so or libmylib.a. The linker will link with the latter in case both exist.
|# gcc myobj1.o myobj2.o myobj3.o –o mybigprog –L/usr/lib -lmylib|
In case you need to link with an archive (a static library), you can also specify its name in full path. When multiple archives are used for the link, and in case there are cross dependencies between them, you must direct the linker to keep all the archive open for function resolving until all are resolved. Otherwise, you’ll get link errors for unresolved functions, even though you specified the library’s name correctly. This is done by defining a group:
|# gcc myobj1.o myobj2.o myobj3.o –o mybigprog –L/usr/lib –start-group –lmylib1 –lmylib2 –lmylib3 –end-group|
It’s the best practice to surround your list of libraries (either static or shared) with the group directive in all cases.
You can instruct gcc to send commands directly to the linker. These commands are not understood by the compiler itself, and passed as they are. The way it is done is using the –Wl,command directive. Usually, you don’t have to use this directive. However, it can be used to further optimize the output. This will be described in an advanced article.
Map file is a detailed text file which contains lists of:
The map file is important and could help you locate and isolate crashes. The map file is created by sending a command to the linker as follows:
|# gcc myobj1.o myobj2.o myobj3.o –o mybigprog -Wl,-Map,myprog.map|
|Check out the ads, there could be something that may interest you there. The ads revenue helps me to pay for the domain and storage.|
Article printed from Real-Time Embedded: http://www.rt-embedded.com/blog
URL to article: http://www.rt-embedded.com/blog/archives/using-gcc-part-2/
URLs in this post:
 part 1: http://www.rt-embedded.com/blog/archives/using-gcc-part-1/
 http://gcc.gnu.org/onlinedocs/: http://gcc.gnu.org/onlinedocs/
Copyright © 2010 Real-Time Embedded. All rights reserved.