Appendix F Libraries

Libraries can be used to record interface information. A library containing information about the Standard C Library is used to enable checking of library calls. Program libraries can be created to enable fast checking of single modules in a large program.

Standard Libraries

In order to check calls to library functions, LCLint uses an annotated standard library. This contains more information about function interfaces then is available in the system header files since it uses annotations. Further, it contains only those functions documented in the ANSI Standard. Many systems include extra functions in their system libraries; programs that use these functions cannot be compiled on other systems that do not provide them. Certain types defined by the library are treated as abstract types (e.g., a program should not rely on how the FILE type is implemented). When checking source code, LCLint does include system headers according to include directive in the source code, but instead uses the library description of the standard library.

The LCLint distribution includes several different standard libraries: the ANSI standard library, the POSIX standard library , and an ad hoc UNIX library. Each library comes in two versions: the standard version and the strict version.

ANSI Library

The default behavior of LCLint is to use the ANSI standard library (loaded from ansi.lcd). This library is based on the standard library described in the ANSI C Standard. It includes functions and types added by Amendment 1 to the ANSI C Standard.

POSIX Library

The POSIX library is selected by the +posixlib flag. The POSIX library is based on the IEEE Std 1003.1-1990.

UNIX Library

The UNIX library is selected by the +unixlib flag. This library is an ad hoc attempt to capture additional functionality provided by many UNIX platforms. Unfortunately, UNIX systems vary widely and very few are consistent with the ANSI Standard.

The differences between the UNIX library and the POSIX library are:

Code checked using the UNIX library can probably be ported to some UNIX systems without difficulty. To enhance the likelihood that a program is portable, the POSIX library should be used instead.

Strict Libraries

Stricter versions of the libraries are used if the -ansi-strict, posix-strict-lib or unix-strct-lib flag is used. These libraries use a stricter interpretation of the library. They will detect more errors in some programs, but may to produce many spurious errors for typical code.

The differences between the standard libraries and the strict libraries are:

Generating the Standard Libraries

The standard libraries are generated from header files included in the LCLint distribution. Some libraries are generated from more than one header file. Since the POSIX library includes the ANSI library, the headers for the ANSI and POSIX libraries are combined to produce the POSIX library. Similarly, the UNIX library is composed of the ANSI, POSIX and UNIX headers. The header files include some sections that are conditionally selected by defining STRICT.

The commands to generate the standard libraries are:

lclint -nolib ansi.h -dump ansi 
lclint -nolib -DSTRICT ansi.h -dump ansistrict
lclint -nolib ansi.h posix.h -dump posix
lclint -nolib -DSTRICT ansi.h posix.h -dump posixstrict
lclint -nolib ansi.h posix.h unix.h -dump unix
lclint -nolib -DSTRICT ansi.h posix.h unix.h -dump unixstrict

User Libraries

To enable running LCLint on large systems, mechanisms are provided for creating libraries containing necessary information. This means source files can be checked independently, after a library has been created. The command line option -dump library stores information in the file library (the default extension, .lcd[27], is added). Then, -load library loads the library. The library contains interface information from the files checked when the library was created.

Header File Inclusion

The standard behavior of LCLint on encountering

   #include <X.h>
is to search for a file named X.h on the include search path (set using -I) and then the system base include path (usually /usr/include, default is set when LCLint is compiled). If X.h is the name of a header file in a loaded standard library (either ANSI or POSIX) and X.h is found in a directory that is a system directory (as set by the -sysdirs flag; the default is /usr/include), X.h will not be included if skip-ansi-headers or skip-posix-headers (depending on whether X.h is an ANSI or POSIX header file) is on (both are on by default). To force all headers to be included normally, use -skip-ansi-headers and -skip-posix-headers.

Sometimes headers in system directories contain non-standard syntax which LCLint is unable to parse. The +skip-sys-headers flag may be used to prevent any include file in a system directory from being included.

LCLint is fast enough that it can be run on medium-size (10,000 line) programs without performance concerns. It takes about one second to process a thousand source lines on a DEC Alpha. Libraries can be used to enable efficient checking of small modules in large programs. To further improve performance, header file inclusion can be optimized.

When processing a complete system in which many files include the same headers, a large fraction of processing time is wasted re-reading header files unnecessarily. If you are checking a 100-file program, and every file includes utils.h, LCLint will have to process utils.h 100 times (as would most C compilers). If the +singleinclude flag is used, each header file is processed only once. Single header file processing produces a significant efficiency improvement when checking large programs split into many files, but is only safe if the same header file included in different contexts always has the same meaning (i.e., it does not depend on preprocessor variable defined differently at different inclusion sites).

When processing a single file in a large system, a large fraction of the time is spent processing included header files. This can be avoided if the information in the header files is stored in a library instead. If +neverinclude is set, inclusion of files ending in .h is prevented. Files with different suffixes are included normally. To do this the header files must not include any expanded macros. That is, the header file must be processed with +allmacros, and there must be no /*@[email protected]*/ control comments in the header. Then, the +neverinclude flag may be used to prevent inclusion of header files. Alternately, non-function macros can be moved to a different file with a name that does not end in .h. Remember, that this file must be included directly from the .c file, since if it is included from a .h file indirectly, that .h file is ignored so the other file is never included.

These options can be used for significant performance improvements on large systems. The performance depends on how the code is structured, but checking a single module in a large program is several times faster if libraries and +neverinclude are used.

Next: Appendix G. Specifications