2    The Compiler System

This chapter contains information on the following topics:

The compiler system is responsible for converting source code into an executable program. This can involve several steps:

These steps can be performed by separate preprocessing, compiling, and linking commands, or they can be performed in a single operation, with the compiler system calling each tool at the appropriate time during the compilation.

Other tools in the compiler system help debug the program after it has been compiled and linked, examine the object files that are produced, create libraries of routines, or analyze the run-time performance of the program.

Table 2-1 summarizes the tools in the compiler system and points to the chapter or section where they are described in this and other documents.

Table 2-1:  Compiler System Functions

Task Tools Where Documented
Compile, link, and load programs; build shared libraries Compiler drivers, link editor, dynamic loader This chapter, Chapter 4, cc(1), c89(1), as(1), ld(1), loader(5), Assembly Language Programmer's Guide, Compaq C Language Reference Manual
Debug programs Symbolic debugger (dbx and ladebug) and Third Degree Chapter 5, Chapter 6, dbx(1), third(5), ladebug(1), Ladebug Debugger Manual
Profile programs Profiler, call graph profiler Chapter 8, prof(1), gprof(1), pixie(5), atom(1), hiprof(5), atomtools(5)
Optimize programs Optimizer, postlink optimizer This chapter, Chapter 10, cc(1), third(5)
Examine object files nm, file, size, dis, odump, and stdump tools This chapter, nm(1), file(1), size(1), dis(1), odump(1), stdump(1), Programming Support Tools
Produce necessary libraries Archiver (ar), linker (ld) command This chapter, Chapter 4, ar(1), ld(1)

2.1    Compiler System Components

Figure 2-1 shows the relationship between the major components of the compiler system and their primary inputs and outputs.

Figure 2-1:  Compiling a Program

Compiler system commands, sometimes called driver programs, invoke the components of the compiler system. Each language has its own set of compiler commands and options.

The cc command invokes the C compiler. In the Tru64 UNIX programming environment, a single cc compiler command can perform multiple actions, including the following:

Table 2-2:  File Suffixes and Associated Files

Suffix File
.a Archive library
.c C source code
.i The driver assumes that the source code was processed by the C preprocessor and that the source code is that of the processing driver; for example, %  cc  -c  source.i. The file, source.i, is assumed to contain C source code.
.o Object file.
.s Assembly source code.
.so Shared object (shared library).

2.2    Data Types in the Tru64 UNIX Environment

The following sections describe how data items are represented on the Tru64 UNIX system.

Note

The default memory access size on a Tru64 UNIX system is 8 bytes (quadword). This means that when two or more threads of execution are concurrently modifying adjacent memory locations, those locations must be quadword aligned to protect the individual modifications from being erroneously overwritten. Errors can occur, for example, if separate data items stored within a single quadword of a composite data structure are being concurrently modified.

For details on the problems that non-quadword alignment can cause and the various situations in which the problems can occur, see the Granularity Considerations section in the Guide to the POSIX Threads Library.

2.2.1    Data Type Sizes

The Tru64 UNIX system is little-endian; that is, the address of a multibyte integer is the address of its least significant byte and the more significant bytes are at higher addresses. The C compiler supports only little-endian byte ordering. The following table gives the sizes of supported data types:

Data Type Size, in Bits
char 8
short 16
int 32
long 64
long long 64
float 32 (IEEE single)
double 64 (IEEE double)
pointer 64 [Footnote 1]
long double 128

2.2.2    Floating-Point Range and Processing

The C compiler supports IEEE single-precision (32-bit float) and double-precision (64-bit double) floating-point data, as defined by the IEEE Standard for Binary Floating-Point Arithmetic (ANSI/IEEE Std 754-1985).

Floating-point numbers have the following ranges:

Tru64 UNIX provides the basic floating-point number formats, operations (add, subtract, multiply, divide, square root, remainder, and compare), and conversions defined in the standard. You can obtain full IEEE-compliant trapping behavior (including NaN [not-a-number]) by specifying a compilation option, or by specifying a fast mode when IEEE-style traps are not required. You can also select, at compile time, the rounding mode applied to the results of IEEE operations. See cc(1) for information on the options that support IEEE floating-point processing.

A user program can control the delivery of floating-point traps to a thread by calling ieee_set_fp_control(), or dynamically set the IEEE rounding mode by calling write_rnd(). See ieee(3) for more information on how to handle IEEE floating-point exceptions.

2.2.3    Structure Alignment

The C compiler aligns structure members on natural boundaries by default. That is, the components of a structure are laid out in memory in the order in which they are declared. The first component has the same address as the entire structure. Each additional component follows its predecessor on the next natural boundary for the component type.

For example, the following structure is aligned as shown in Figure 2-2:

struct {char c1;
        short s1;
        float f;
        char c2;
       }

Figure 2-2:  Default Structure Alignment

The first component of the structure, c1, starts at offset 0 and occupies the first byte. The second component, s1, is a short; it must start on a word boundary. Therefore, padding is added between c1 and s1. No padding is needed to make f and c2 fall on their natural boundaries. However, because size is rounded up to a multiple of f's alignment, three bytes of padding are added after c2.

You can use the following mechanisms to override the default alignment of structure members:

See Section 3.6 and Section 3.8 for information on these directives.

2.2.4    Bit-Field Alignment

In general, the alignment of a bit field is determined by the bit size and bit offset of the previous field. For example, the following structure is aligned as shown in Figure 2-3:

struct a {
    char  f0:   1;
    short f1:  12;
    char  f2:   3;
} struct_a;

Figure 2-3:  Default Bit-Field Alignment

The first bit field, f0, starts on bit offset 0 and occupies 1 bit. The second, f1, starts at offset 1 and occupies 12 bits. The third, f2, starts at offset 13 and occupies 3 bits. The size of the structure is two bytes.

Certain conditions can cause padding to occur prior to the alignment of the bit field:

Figure 2-4:  Padding to the Next Pack Boundary

2.2.5    The _ _align Storage Class Modifier

Data alignment is implied by data type. For example, the C compiler aligns an int (32 bits) on a 4-byte boundary and a long (64 bits) on an 8-byte boundary. The _ _align storage-class modifier aligns objects of any of the C data types on the specified storage boundary. It can be used in a data declaration or definition.

The _ _align modifier has the following format:

_ _align (keyword)
        _ _align (n)

Where keyword is a predefined alignment constant and n is an integer power of 2. The predefined constant or power of 2 tells the compiler the number of bytes to pad in order to align the data.

For example, to align an integer on the next quadword boundary, use any of the following declarations:

   int _ _align( QUADWORD ) data;
   int _ _align( quadword ) data;
   int _ _align( 3 ) data;

In this example, int _ _align  (  3  ) specifies an alignment of 2x2x2 bytes, which is 8 bytes, or a quadword of memory.

The following table shows the predefined alignment constants, their equivalent power of 2, and equivalent number of bytes:

Constant Power of 2 Number of Bytes
BYTE or byte 0 1
WORD or word 1 2
LONGWORD or longword 2 4
QUADWORD or quadword 3 8

2.3    Using the C Preprocessor

The C preprocessor performs macro expansion, includes header files, and executes preprocessor directives prior to compiling the source file. The following sections describe the Tru64 UNIX specific operations performed by the C preprocessor. For more information on the C preprocessor, see cc(1), cpp(1), and the Compaq C Language Reference Manual.

2.3.1    Predefined Macros

When the compiler is invoked, it defines C preprocessor macros that identify the language of the input files and the environments on which the code can run. See cc(1) for a list of the preprocessor macros. You can reference these macros in #ifdef statements to isolate code that applies to a particular language or environment. Use the following statement to uniquely identify Tru64 UNIX:

#if defined (_ _digital_ _) && defined (_ _unix_ _)

The type of source file and the type of standards you apply determine the macros that are defined. The C compiler supports several levels of standardization:

2.3.2    Header Files

Header files are typically used for the following purposes:

C header files, sometimes known as include files, have a .h suffix. Typically, the reference page for a library routine or system call indicates the required header files. Header files can be used in programs written in different languages.

Note

If you intend to debug your program using dbx or ladebug, do not place executable code in a header file. The debugger interprets a header file as one line of source code; none of the source lines in the file appears during the debugging session. For more information on the dbx debugger, see Chapter 5. For details on ladebug, see the Ladebug Debugger Manual.

You can include header files in a program source file in one of two ways:

#include "filename"

This indicates that the C macro preprocessor should first search for the include file filename in the directory in which it found the file that contains the directive, then in the search path indicated by the -I options, and finally in /usr/include.

#include <filename>

This indicates that the C macro preprocessor should search for the include file filename in the search path indicated by the -I options and then in /usr/include, but not in the directory where it found the file that contains the directive.

You can also use the -Idir and -nocurrent_include options to specify additional pathnames (directories) to be searched by the C preprocessor for #include files:

2.3.3    Setting Up Multilanguage Include Files

C, Fortran, and assembly code can reside in the same include files, and can then be conditionally included in programs as required. To set up a shareable include file, you must create a .h file and enter the respective code, as shown in the following example:

   #ifdef _ _LANGUAGE_C_ _
    .
    .    (C code)
    .
   #endif
   #ifdef _ _LANGUAGE_ASSEMBLY_ _
    .
    .    (assembly code)
    .
   #endif
 
 

When the compiler includes this file in a C source file, the _ _LANGUAGE_C_ _ macro is defined and the C code is compiled. When the compiler includes this file in an assembly language source file, the _ _LANGUAGE_ASSEMBLY_ _ macro is defined, and the assembly language code is compiled.

2.3.4    Implementation-Specific Preprocessor Directives (#pragma)

The #pragma directive is a standard method of implementing features that vary from one compiler to the next. The C compiler supports the following implementation-specific pragmas:

Chapter 3 provides detailed descriptions of these pragmas.

2.4    Compiling Source Programs

The compilation environment established by the cc command produces object files that comply with the common object file format (COFF).

Options supported by the cc command select a variety of program development functions, including debugging, optimizing, and profiling facilities, and the names assigned to output files. See cc(1) for details on cc command-line options.

The following sections describe the default compiler behavior and how to compile multilanguage programs.

2.4.1    Default Compilation Behavior

Most compiler options have default values that are used if the option is not specified on the command line. For example, the default name for an output file is filename.o for object files, where filename is the base name of the source file. The default name for an executable program object is a.out. The following example uses the defaults in compiling two source files named prog1.c and prog2.c:

% cc prog1.c prog2.c

This command runs the C compiler, creating object files prog1.o and prog2.o and the executable program a.out.

When you enter the cc compiler command with no other options, the following options are in effect:

-noansi_alias

Turns off ANSI C aliasing rules, which prevents the optimizer from being aggressive in its optimizations.

-arch generic

Generates instructions that are appropriate for all Alpha processors.

-assume aligned_objects

Allows the compiler to make such an assumption, and thereby generate more efficient code for pointer dereferences of aligned pointer types.

-assume math_errno

Allows the compiler to make the assumption that the program might interrogate errno after any call to a math library routine that is capable of setting errno.

-call_shared

Produces a dynamic executable file that uses shareable objects at run time.

-nocheck_bounds

Disables the run-time checking of array bounds.

-cpp

Causes the C macro preprocessor to be called on C and assembly source files before compiling.

-error_limit 30

Limits the number of error-level diagnostics that the compiler will output for a given compilation to 30.

-float

Informs the compiler that it is not necessary to promote expressions of type float to type double.

-nofp_reorder

Directs the compiler not to reorder floating-point computations in a way that might affect accuracy.

-fprm n

Performs normal rounding (unbiased round to nearest) of floating-point numbers.

-fptm n

Generates instructions that do not generate floating-point underflow or inexact trapping modes.

-g0

Does not produce symbol information for symbolic debugging.

-I/usr/include

Specifies that #include files whose names do not begin with a slash (/) are always sought first in the directory /usr/include.

-inline manual

Inlines only those function calls explicitly requested for inlining by a #pragma  inline directive.

-intrinsics

Directs the compiler to recognize certain functions as intrinsics and perform appropriate optimizations.

-member_alignment

Directs the compiler to naturally align data structure members (with the exception of bit-field members).

-nomisalign

Generates alignment faults for arbitrarily aligned addresses.

-nestlevel=50

Sets the nesting level limit for include files to 50.

-O1

Enables global optimizations.

-p0

Disables profiling.

-nopg

Turns off gprof profiling.

-preempt_module

Allows symbol preemption on a module-by-module basis.

-SD/usr/include

Suppresses messages for nonportable constructs in header files whose pathnames are prefixed with /usr/include.

-signed

Causes type char to use the same representation as signed  char.

-std

Enforces the ANSI C standard, but allows some common programming practices disallowed by the standard.

-tune generic

Selects instruction tuning that is appropriate for all implementations of the Alpha architecture.

-writable_strings

Makes string literals writable.

The following list includes miscellaneous aspects of the default cc compiler behavior:

2.4.2    Compiling Multilanguage Programs

When the source language of the main program differs from that of a subprogram, compile each program separately with the appropriate driver and link the object files in a separate step. You can create objects suitable for linking by specifying the -c option, which stops a driver immediately after the object file has been created. For example:

% cc -c main.c

This command produces the object file main.o, not the executable file a.out.

After creating object modules for source files written in languages other than C, you can use the cc command to compile C source files and link all of the object modules into an executable file. For example, the following cc command compiles c-prog.c and links c-prog.o and nonc-prog.o into the executable file a.out:


% cc nonc-prog.o c-prog.c

2.4.3    Enabling Run-Time Checking of Array Bounds

The cc command's -check_bounds option generates run-time code to perform array bounds verification. The -nocheck_bounds option (the default) disables the run-time checking of array bounds.

The kind of code that causes the compiler to emit run-time checks, and the exact bounds values used in a given check, are subject to certain characteristics of the compiler implementation that would not be obvious to a user. The exact conditions, which assume a good understanding of the C language rules involving arrays, are as follows:

The following examples illustrate these rules:

int a[10];
int *b;
int c;
int *d;
int one[1];
int vla[c];          // C9X variable-length array
 
a[c] = 1;            // check c is 0-9, array subscript
c[a] = 1;            // check c is 0-9, array subscript
b[c] = 1;            // no check, b is a pointer
d = a + c;           // check c is 0-10, computing address
d = b + c;           // no check, b is a pointer
b = &a[c]            // check c is 0-10, computing address
*(a + c) = 1;        // check c is 0-10, computing address 
*(a - c) = 1;        // check c is -10 to 0, computing address
 
a[1] = 1;            // no run-time check - know access is valid
vla[1] = 1;          // run-time check, vla has run-time bounds
a[10] = 1;           // run-time check  (and compiler diagnostic)
d = a + 10;          // no run-time check, computing address
                     // SUBSCRBOUNDS2 message can be enabled
 
c = one[5];          // no run-time check, array of one element
                     // SUBSCRBOUNDS1 message can be enabled
 
 

When an out-of-bounds access is encountered, the output is as follows:

Trace/BPT trap (core dumped)

A program can trap this error with the following code:

signal(SIGTRAP, handler);
 
 

Note that when run-time checking is enabled, incorrect checks might be made in certain cases where arrays are legitimately accessed using pointer arithmetic.

The compiler is only able to output the checking code for the first arithmetic operation performed on a pointer that results from converting an array name to a pointer. This can result in an incorrect check if the resulting pointer value is again operated on by pointer arithmetic. Consider the expression a = b + c - d, where a is a pointer, b is an array, and c and d are integers. When bounds-checking is enabled, a check will be made to verify that c is within the bounds of the array. This will lead to an incorrect run-time trap in cases where c is outside the bounds of the array but c - d is not.

In these cases, you can recode the pointer expression so that the integer part is in parentheses. This way, the expression will contain only one pointer arithmetic operation and the correct check will be made. In the previous example, the expression would be changed to the following:

a = b + (c - d);

2.5    Linking Object Files

The cc driver command can link object files to produce an executable program. In some cases, you may want to use the ld linker directly. Depending on the nature of the application, you must decide whether to compile and link separately or to compile and link with one compiler command. Factors to consider include:

2.5.1    Linking with Compiler Commands

You can use a compiler command instead of the linker command to link separate objects into one executable program. Each compiler (except the assembler) recognizes the .o suffix as the name of a file that contains object code suitable for linking and immediately invokes the linker.

Because the compiler driver programs pass the libraries associated with that language to the linker, using the compiler command is usually recommended. For example, the cc driver uses the C library (libc.so) by default. For information about the default libraries used by each compiler command, see the appropriate command in the reference pages, such as cc(1).

You can also use the -l option of the cc command to specify additional libraries to be searched for unresolved references. The following example shows how to use the cc driver to pass the names of two libraries to the linker with the -l option:

% cc -o all main.o more.o rest.o -lm -lexc

The -lm option specifies the math library; the -lexc option specifies the exception library.

Compile and link modules with a single command when you want to optimize your program. Most compilers support increasing levels of optimization with the use of certain options. For example:

2.5.2    Linking with the ld Command

Normally, you do not need to run the linker directly, but use the cc command to indirectly invoke the linker. Executables that need to be built solely from assembler objects can be built with the ld command.

The linker (ld) combines one or more object files (in the order specified) into one executable program file, performing relocation, external symbol resolutions, and all other processing required to make object files ready for execution. Unless you specify otherwise, the linker names the executable program file a.out. You can execute the program file or use it as input for another linker operation.

The as assembler does not automatically invoke the linker. To link a program written in assembly language, do either of the following:

For information about the options and libraries that affect the linking process, see ld(1).

2.5.3    Specifying Libraries

When you compile your program on the Tru64 UNIX system, it is automatically linked with the C library, libc.so. If you call routines that are not in libc.so or one of the archive libraries associated with your compiler command, you must explicitly link your program with the library. Otherwise, your program will not be linked correctly.

You need to explicitly specify libraries in the following situations:

2.6    Running Programs

To run an executable program in your current working directory, in most cases you enter its file name. For example, to run the program a.out located in your current directory, enter:

% a.out

If the executable program is not in a directory in your path, enter the directory path before the file name, or enter:


% ./a.out

When the program is invoked, the main function in a C program can accept arguments from the command line if the main function is defined with one or more of the following optional parameters:

int main ( int argc, char *argv[ ], char *envp[ ] )[...]

The argc parameter is the number of arguments in the command line that invoked the program. The argv parameter is an array of character strings containing the arguments. The envp parameter is the environment array containing process information, such as the user name and controlling terminal. (The envp parameter has no bearing on passing command-line arguments. Its primary use is during exec and getenv function calls.)

You can access only the parameters that you define. For example, the following program defines the argc and argv parameters to echo the values of parameters passed to the program:

/*
 * Filename: echo-args.c
 * This program echoes command-line arguments.
*/
 
#include <stdio.h>
 
int main( int argc, char *argv[] )
{
int i;
 
printf( "program: %s\n", argv[0] ); /* argv[0] is program name */
 
for ( i=1; i < argc; i++ )
	printf( "argument %d: %s\n", i, argv[i] );
 
return(0);
}

The program is compiled with the following command to produce a program file called a.out:


$ cc echo-args.c

When the user invokes a.out and passes command-line arguments, the program echoes those arguments on the terminal. For example:

$ a.out Long Day\'s "Journey into Night"
	    program: a.out
	    argument 1: Long
	    argument 2: Day's
	    argument 3: Journey into Night

The shell parses all arguments before passing them to a.out. For this reason, a single quote must be preceded by a backslash, alphabetic arguments are delimited by spaces or tabs, and arguments with embedded spaces or tabs are enclosed in quotation marks.

2.7    Object File Tools

After a source file has been compiled, you can examine the object file or executable file with following tools:

The following sections describe these tools. In addition, see strings(1) for information on using the strings command to find the printable strings in an object file or other binary file.

2.7.1    Dumping Selected Parts of Files (odump)

The odump tool displays header tables and other selected parts of an object or archive file. For example, odump displays the following information about the file echo-args.o:

% odump -at echo-args.o
 
 
			***ARCHIVE SYMBOL TABLE***
 
 
 
 
			***ARCHIVE HEADER***
	Member Name        Date       Uid     Gid     Mode      Size
 
 
 
			***SYMBOL TABLE INFORMATION***
[Index]	Name	Value	Sclass	Symtype	Ref
echo-args.o:
[0]	 main	0x0000000000000000	0x01	0x06	0xfffff
[1]	 printf	0x0000000000000000	0x06	0x06	0xfffff
[2]	 _fpdata	0x0000000000000000	0x06	0x01	0xfffff

For more information, see odump(1).

2.7.2    Listing Symbol Table Information (nm)

The nm tool displays symbol table information for object files. For example, nm displays the following information about the object file produced for the executable file a.out:

% nm
nm: Warning: - using a.out
 
Name                            Value        Type       Size
 
.bss                   | 0000005368709568 | B | 0000000000000000
.data                  | 0000005368709120 | D | 0000000000000000
.lit4                  | 0000005368709296 | G | 0000000000000000
.lit8                  | 0000005368709296 | G | 0000000000000000
.rconst                | 0000004831842144 | Q | 0000000000000000
.rdata                 | 0000005368709184 | R | 0000000000000000

.
.
.

The Name column contains the symbol or external name; the Value column shows the address of the symbol, or debugging information; the Type column contains a letter showing the symbol type; and the Size column shows the symbol's size (accurate only when the source file is compiled with the debugging option, for example, -g). Some of the symbol type letters are:

For more information, see nm(1).

2.7.3    Determining a File's Type (file)

The file command reads input files, tests each file to classify it by type, and writes the file's type to standard output. The file command uses the /etc/magic file to identify files that contain a magic number. (A magic number is a numeric or string constant that indicates a file's type.)

The following example shows the output of the file command on a directory containing a C source file, object file, and executable file:

% file *.*
.:       directory
..:      directory
a.out:   COFF format alpha dynamically linked, demand paged executable
or object module not stripped - version 3.11-8
echo-args.c:    c program text
echo-args.o:    COFF format alpha executable or object module not
stripped - version 3.12-6
 
 

For more information, see file(1).

2.7.4    Determining a File's Segment Sizes (size)

The size tool displays information about the text, data, and bss segments of the specified object or archive file or files in octal, hexadecimal, or decimal format. For example, when it is called without any arguments, the size command returns information on a.out. You can also specify the name of an object or executable file on the command line. For example:

% size
text    data    bss    dec      hex
8192    8192    0      16384    4000
% size echo-args.o
text    data    bss    dec      hex
176     96      0      272      110

For more information, see size(1).

2.7.5    Disassembling an Object File (dis)

The dis tool disassembles object file modules into machine language. For example, the dis command produces the following output when it disassembles the a.out program:

% dis a.out

.
.
.
_ _start: 0x120001080: 23defff0 lda sp, -16(sp) 0x120001084: b7fe0008 stq zero, 8(sp) 0x120001088: c0200000 br t0, 0x12000108c 0x12000108c: a21e0010 ldl a0, 16(sp) 0x120001090: 223e0018 lda a1, 24(sp)
.
.
.

For more information, see dis(1).

2.8    ANSI Name Space Pollution Cleanup in the Standard C Library

The ANSI C standard states that users whose programs link against libc are guaranteed a certain range of global identifiers that can be used in their programs without danger of conflict with, or pre-emption of, any global identifiers in libc.

The ANSI C standard also reserves a range of global identifiers that libc can use in its internal implementation. These are called reserved identifiers and consist of the following, as defined in ANSI document number X3.159-1989:

ANSI conformant programs are not permitted to define global identifiers that either match the names of ANSI routines or fall into the reserved name space specified earlier in this section. All other global identifier names are available for use in user programs.

Historical libc implementations contain large numbers of non-ANSI, nonreserved global identifiers that are both documented and supported. These routines are often called from within libc by other libc routines, both ANSI and otherwise. A user's program that defines its own version of one of these non-ANSI, nonreserved items would pre-empt the routine of the same name in libc. This could alter the behavior of supported libc routines, both ANSI and otherwise, even though the user's program may be ANSI-conformant. This potential conflict is known as ANSI name space pollution.

The implementation of libc on Tru64 UNIX includes a large number of non-ANSI, nonreserved global identifiers that are both documented and supported. To protect against pre-emption of these global identifiers within libc and to avoid pollution of the user's name space, the vast majority of these identifiers have been renamed to the reserved name space by prepending two underscores (_ _) to the identifier names. To preserve external access to these items, weak identifiers have been added using the original identifier names that correspond to their renamed reserved counterparts. Weak identifiers work much like symbolic links between files. When the weak identifier is referenced, the strong counterpart is used instead.

User programs linked statically against libc may have extra symbol table entries for weak identifiers. Each of these identifiers will have the same address as its reserved counterpart, which will also be included in the symbol table. For example, if a statically linked program simply called the tzset() function from libc, the symbol table would contain two entries for this call, as follows:


# stdump -b a.out | grep tzset
18. (file 9) (4831850384) tzset Proc Text symref 23 (weakext)
39. (file 9) (4831850384) _ _tzset Proc Text symref 23

In this example, tzset is the weak identifier and _ _tzset is its strong counterpart. The _ _tzset identifier is the routine that will actually do the work.

User programs linked as shared should not see such additions to the symbol table because the weak/strong identifier pairs remain in the shared library.

Existing user programs that reference non-ANSI, nonreserved identifiers from libc do not need to be recompiled because of these changes, with one exception: user programs that depended on pre-emption of these identifiers in libc will no longer be able to pre-empt them using the nonreserved names. This kind of pre-emption is not ANSI-compliant and is highly discouraged. However, the ability to pre-empt these identifiers still exists by using the new reserved names (those preceded by two underscores).

These changes apply to the dynamic and static versions of libc:

When debugging programs linked against libc, references to weak symbols resolve to their strong counterparts, as in the following example:

% dbx a.out
dbx version 3.11.4
 
Type 'help' for help.
 
main:   4   tzset
 
(dbx) stop in tzset
 
[2] stop in _ _tzset
 
(dbx)

When the weak symbol tzset in libc is referenced, the debugger responds with the strong counterpart _ _tzset instead because the strong counterpart actually does the work. The behavior of the dbx debugger is the same as if _ _tzset were referenced directly.