• No results found

General Compilation Problems

In document Self Service Linux pdf (Page 150-155)

Explained

121Compiling

4.4 C OMPILING THE L INUX K ERNEL

4.4.4 General Compilation Problems

When compiling your own code, compilation errors are often very easy to resolve. When downloading source from reputable locations on the Internet, resolving compilation errors can be very difficult. It can be especially difficult if the source that has been downloaded has been officially released and tested thoroughly. The instant thought is that it is a user error and you must be doing something wrong. This isn’t always the case though, as I’ll attempt to point out.

Reiterating what was just stated, compilation errors are generally due to one or a combination of the following reasons:

1. Environment/setup errors or differences 2. Compiler version differences or bugs 3. User error

4. Code error

4.4.4.1 Environment/Setup Errors or Differences There are an infinite number of ways to configure Linux system environments, so the chances for a setup or environment error are fairly high. Further adding to the possibility for problems is a correctly set up environment—but one that differs from the environment in which the source code was written. This is a very real problem, especially with Linux, simply because things change so quickly and are constantly evolving.

Some examples of environment errors or differences that could easily lead to compilation problems are

missing or outdated system include files

outdated or differing glibc libraries

insufficient disk space

Many modern software packages include scripts generated by the GNU autoconf package, which will automatically configure the Makefile(s) and source code based on the system’s environment. The use of autoconf will immediately flag any differences or problems it finds with header files or libraries before compilation even begins, which greatly simplifies the problem determination

process. Sample autoconf output for the gcc 3.3.2 source is shown here. The output can be quite lengthy, so a large chunk in the middle has been cut out (<<…>>) to show the beginning and end output.

linux> ./configure 2>&1 | tee conf.out Configuring for a i686-pc-linux-gnu host.

Created “Makefile” in /home/dbehman/gcc-3.3.2 using “mt-frag” Configuring libiberty...

creating cache ../config.cache

checking whether to enable maintainer-specific portions of Makefiles... no

checking for makeinfo... no checking for perl... perl

checking host system type... i686-pc-linux-gnu checking build system type... i686-pc-linux-gnu checking for ar... ar

checking for ranlib... ranlib checking for gcc... gcc

checking whether we are using GNU C... yes checking whether gcc accepts -g... yes

checking whether gcc and cc understand -c and -o together... yes <<…>>

checking size of short... (cached) 2 checking size of int... (cached) 4 checking size of long... (cached) 4 checking size of long long... (cached) 8

checking byte ordering... (cached) little-endian updating cache ../config.cache

creating ./config.status creating Makefile

creating install-defs.sh creating config.h

This isn’t a foolproof method, though, and compilation problems could still

occur even after a successful configuration. If running configure with or without

a series of command line parameters is documented as one of the first steps for compiling and installing the software, then it’s highly likely that autoconf is being used and your compilation experience has a greater chance of being problem-free.

4.4.4.2 Compiler Version Differences or Bugs A compilation failure due to a compiler version difference or a bug can be very tricky to diagnose. For version differences, the good news is that the GNU Compiler Collection (GCC) is the most commonly used set of compilers on Linux systems, so the scope of determining differences is much smaller than other systems. There are, however, a growing number of alternative compilers available for the various

architectures on which Linux runs, so using a compiler other than GCC increases the chances of a compile failure. As GCC is almost always available on a Linux system as well as additional compilers, a good first step in diagnosing a compile failure with a different compiler is to re-attempt compilation with GCC. If GCC compiles without error, you’ve identified a compiler difference. It doesn’t necessarily mean that either of the compilers is wrong or has a bug. It could simply mean that the compilers interpret the programming standard differently.

Version differences within GCC can easily result in compile failures. The following example illustrates how the same code compiles clean, compiles with a warning, and fails with a compile error when using different versions of GCC. The source code is:

#include <stdio.h>

static const char msg[] = “This is a string which spans a couple of lines to demonstrates differences between gcc 2.96, gcc 3.2, and gcc 3.3"; int main( void ) {

printf( “%s\n”, msg ); return 0;

}

Compiling and running this code with gcc 2.96 produces the following:

penguin> gcc -v

Reading specs from /usr/lib/gcc-lib/i386-suse-linux/2.95.3/specs gcc version 2.95.3 20010315 (SuSE) penguin> gcc multiline.c penguin> ./a.out This is a string which spans a couple of lines to demonstrates differences between gcc 2.96, gcc 3.2, and gcc 3.3

The compilation was successful with no warnings, and running the resulting executable displays the desired message.

Compiling with gcc 3.2 produces the following:

penguin> gcc -v

Reading specs from /usr/lib64/gcc-lib/x86_64-suse-linux/3.2.2/specs Configured with: ../configure —enable-threads=posix —prefix=/usr — with-local-prefix=/usr/local —infodir=/usr/share/info —mandir=/usr/ share/man —libdir=/usr/lib64 —enable-

languages=c,c++,f77,objc,java,ada —enable-libgcj —with-gxx-include- dir=/usr/include/g++ —with-slibdir=/lib —with-system-zlib —enable- shared —enable-__cxa_atexit x86_64-suse-linux

Thread model: posix

gcc version 3.2.2 (SuSE Linux) penguin> gcc multiline.c

multiline.c:3:27: warning: multi-line string literals are deprecated penguin> ./a.out This is a string which spans a couple of lines to demonstrates differences between gcc 2.96, gcc 3.2, and gcc 3.3

As the warning message states, multi-line string literals have been deprecated, but given this is just a warning, the compilation completes, and running the program produces our desired output.

Compiling the source with gcc 3.3 produces this:

penguin> /opt/gcc33/bin/gcc -v

Reading specs from /opt/gcc33/lib64/gcc-lib/x86_64-suse-linux/3.3/

➥specs

Configured with: ../configure —enable-threads=posix —prefix=/opt/ gcc33 —with-local-prefix=/usr/local —infodir=/opt/gcc33/share/

➥info —mandir=/opt/gcc33/share/man —libdir=/opt/gcc33/lib64 —enable-

➥languages=c,c++,f77,objc,java,ada —disable-checking —enable-libgcj

➥—with-gxx-include-dir=/opt/gcc33/include/g++ —with-slibdir=/lib64

➥—with-system-zlib —enable-shared —enable-__cxa_atexit x86_64-suse-

➥linux

Thread model: posix

gcc version 3.3 20030312 (prerelease) (SuSE Linux) penguin> /opt/gcc33/bin/gcc multiline.c

multiline.c:3:27: missing terminating “ character multiline.c:4: error: parse error before “which” multiline.c:9:11: missing terminating “ character

Clearly, the error is due to the string spanning multiple lines. If gcc 3.3 is the compiler version to be used, the only solution is to fix the code as shown in this updated script:

#include <stdio.h>

static const char msg[] = “This is a string\n” “which spans a\n”

“couple of lines\n”

“to demonstrates differences\n” “between gcc 2.96,\n”

“gcc 3.2,\n” “and gcc 3.3”; int main( void ) {

printf( “%s\n”, msg ); return 0;

}

The point here is that code with strings spanning multiple lines certainly existed when gcc 2.96 was the most current version. If that code doesn’t get updated by its author(s) and users attempt to compile it with a newer version of gcc, they will get compile errors directly related to a compiler version difference. Some C purists could argue that the first version of the sample code is incorrect and should not have been used in the first place. However, the fact remains that at one time the compiler allowed it without warning; therefore, it will be used by many programmers. In fact, there were several instances in kernel code, mostly drivers, which had multi-line strings that have since been fixed. Compiler bugs are certainly another very real possibility for compilation errors. A compiler is a piece of software written in a high-level language as well, so it is by no means exempt from the same kinds of bugs that exist in programs being compiled. As with all unexpected compilation errors and compiler behavior, the best way to determine the cause of the problem is to eliminate anything nonessential and set up a minimalist scenario; this generally means making a standalone test program that is made up of a very small number of source files and functions. The test program should do nothing but clearly demonstrate the problem. When this is achieved, this test program can be sent to the parties supporting the compiler. In the case of gcc, this would generally be the distribution’s support team, but it could be gcc developers at

gnu.org directly.

4.4.4.3 User Error Compilation failures due to user error are extremely ccommon and could be the result of the user incorrectly doing almost anything. Some examples include

Incorrectly expanding the source archive

Incorrectly setting required flags, options, or environment variables

Executing make incorrectly

Using insufficient permissions

Downloading incorrect or insufficient packages

The list is endless…

Generally, software packages come with documents in the highest level directory of the archive with a name to catch your attention. INSTALL, README, or COMPILE are examples. These files usually contain excellent documentation and instructions for building the software package quickly and easily. If the instructions are followed along with a little care and common knowledge, building a software package should be an error free experience. 4.4.4.4 Code Error Compilation failures due to code errors or bugs are the simplest way for a compilation to fail. In general, a source package gets thoroughly tested before it is made available. Testing cannot occur without compilation, so a very high percentage of compilation bugs, if not all, are flushed out during the testing phase. However, because of a huge variety of environments and compilation needs, real compile-time bugs can slip through the cracks to the end user. When this happens, the user can attempt to fix the problem on his own, but often correspondence with the author is required. A word of caution, though: The reason for discussing this section last is to stress the importance of ensuring that a compilation failure isn’t due to any of the causes just listed before assuming it is a code error. Reporting a problem as a bug to an author when it is actually user error, for example, is frustrating for everyone, so it is important to be sure of the diagnosis.

It’s also very important to note that a compilation failure could very easily be due to a combination of code error and any of the other causes mentioned such as compiler version differences. Using the example of the string spanning multiple lines, even though gcc 2.96 happily compiled it without warning, this doesn’t necessarily mean that it is 100% “correct” code.

In document Self Service Linux pdf (Page 150-155)