|
Guide Contents 1. Overview 2. Operation 3. Abstract Types |
4. Function Interfaces 5. Memory Management 6. Sharing 7. Value Constraints |
8. Macros 9. Naming Conventions 10. Other Checks Contact: lclint@cs.virginia.edu |
This guide is preserved to maintain old links, but has been replaced by the Splint Manual. |
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:
- In the UNIX library, free is declared with a non-null parameter. ANSI C specifies that free should handle the argument NULL, but several UNIX platforms crash if NULL is passed to free.
- Extra variables, constants and functions are included in the UNIX library. Some declarations are not part of the POSIX library, but are believed to be available on many UNIX systems. See lib/unix.h for a list of the UNIX-only declarations.
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:
- The standard libraries declare the printing functions (fprintf, printf, and sprintf) that may return error codes to return int or void. This prevents typical programs from leading to deluge of ignored return value errors, but may mean some relevant errors are not detected. In the strict libraries, they are declared to return int, so ignored return value errors will be reported (depending on other flag settings). Programs should check that this return value is non-negative.
- The standard libraries declare some parameters and return values to be alternate types (int or bool, or int or char). The ANSI standard specifies these types as int to be compatible with older versions of the library, but logically they make more sense as bool or char. In the strict libraries, the stronger type is used. The parameter to assert is int or bool in the standard library, and bool in the strict library. The parameter to the character functions isalnum, isalpha, iscntrl, isdigit, isgraph, islower, isprint, ispunct, isspace, isupper, isxdigit, tolower and toupper is char or int in the standard library and char in the strict library. The type of the return value of the character classification functions (all of the previous character functions except tolower and toupper) is bool or int in the standard library and bool in the strict library. The type of the first parameter to ungetc is char or int in the standard library and char in the strict library (EOF should not be passed to ungetc). The second parameter to strchr and strrchr is char or int in the standard library and char in the strict library.
- The global variables stdin, stdout and stderr are declared as unchecked variables (see Section 4.2.1) in the standard libraries. In the strict libraries, they are checked. The global variable errno is declared unchecked in the standard libraries, but declared checkedstrict in the strict libraries.
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 unixstrictUser 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 /*@notfunction@*/ 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
Contents
This guide is preserved to maintain old links, but has been replaced by the Splint Manual. | |||
|
Guide Contents 1. Overview 2. Operation 3. Abstract Types |
4. Function Interfaces 5. Memory Management 6. Sharing 7. Value Constraints |
8. Macros 9. Naming Conventions 10. Other Checks Contact: lclint@cs.virginia.edu |