Sun Microsystems Logo
Products & Services
 
Support & Training
 
 

Previous Previous     Contents     Index     Next Next

If you want to allow undefined symbols, as in cases like the previous example, then the default fatal error condition can be suppressed by using the link-editor's -z nodefs option.


Note - Take care when using the -z nodefs option. If an unavailable symbol reference is required during the execution of a process, a fatal runtime relocation error occurs. It may be possible to detect this error during the initial execution and testing of an application. However, more complex execution paths can result in this error condition taking much longer to detect, which can be time consuming and costly.


Symbols can also remain undefined when a symbol reference in a relocatable object is bound to a symbol definition in an implicitly defined shared object. For example, continuing with the files main.c and foo.c used in the previous example:

$ cat bar.c
int bar = 1;

$ cc -o libbar.so -R. -G -K pic bar.c -L. -lfoo
$ ldd libbar.so
        libfoo.so =>     ./libfoo.so

$ cc -o prog main.c -L. -lbar
Undefined           first referenced
 symbol                 in file
foo                     main.o  (symbol belongs to implicit \
                        dependency ./libfoo.so)
ld: fatal: Symbol referencing errors. No output written to prog

prog is built with an explicit reference to libbar.so. libbar.so has a dependency on libfoo.so, and therefore an implicit reference to libfoo.so from prog is established.

Because main.c made a specific reference to the interface provided by libfoo.so, prog really has a dependency on libfoo.so. However, only explicit shared object dependencies are recorded in the output file being generated. Thus, prog fails to run if a new version of libbar.so is developed that no longer has a dependency on libfoo.so.

For this reason, bindings of this type are deemed fatal, and the implicit reference must be made explicit by referencing the library directly during the link-edit of prog. The required reference is hinted at in the fatal error message shown in the preceding example.

Generating a Shared Object Output File

When the link-editor is generating a shared object output file, it allows undefined symbols to remain at the end of the link-edit. This default behavior allows the shared object to import symbols from either relocatable objects or from other shared objects when the object is used to create a dynamic executable.

The link-editor's -z defs option can be used to force a fatal error if any undefined symbols remain. This option is recommended when creating any shared objects. Shared objects that reference symbols from an application can use the -z defs option and define the applications symbols using the extern mapfile directive, as described in Defining Additional Symbols.

A self-contained shared object, in which all references to external symbols are satisfied by named dependencies, provides maximum flexibility. The shared object can be employed by many users without those users having to determine and establish dependencies to satisfy the shared object's requirements.

Weak Symbols

Weak symbol references that are not bound during a link-edit do not result in a fatal error condition, no matter what output file type is being generated.

If a static executable is being generated, the symbol is converted to an absolute symbol and assigned a value of zero.

If a dynamic executable or shared object is being produced, the symbol is left as an undefined weak reference and assigned the value zero. During process execution, the runtime linker searches for this symbol. If the runtime linker does not find a match, it binds the reference to an address of zero instead of generating a fatal runtime relocation error.

Historically, these undefined weak referenced symbols have been employed as a mechanism to test for the existence of functionality. For example, the following C code fragment might have been used in the shared object libfoo.so.1:

#pragma weak    foo

extern  void    foo(char *);

void bar(char * path)
{
        void (* fptr)(char *);

        if ((fptr = foo) != 0)
                (* fptr)(path);
}

When an application is built that references libfoo.so.1, the link-edit will complete successfully regardless of whether a definition for the symbol foo is found. If during execution of the application the function address tests nonzero, the function is called. However, if the symbol definition is not found, the function address tests zero and so it is not called.

Compilation systems view this address comparison technique as having undefined semantics, which can result in the test statement being removed under optimization. In addition, the runtime symbol binding mechanism places other restrictions on the use of this technique, which prevents a consistent model from being available for all dynamic objects.


Note - Undefined weak references in this manner are discouraged. Instead, you should use dlsym(3DL) with the RTLD_DEFAULT flag as a means of testing for a symbol's existence. See Testing for Functionality.


Tentative Symbol Order Within the Output File

Contributions from input files usually appear in the output file in the order of their contribution. An exception occurs when processing tentative symbols and their associated storage. These symbols are not fully defined until their resolution is complete. If the resolution occurs as a result of encountering a defined symbol from a relocatable object, then the order of appearance is that which would have occurred for the definition.

If you need to control the ordering of a group of symbols, then any tentative definition should be redefined to a zero-initialized data item. For example, the following tentative definitions result in a reordering of the data items within the output file, compared to the original order described in the source file foo.c:

$ cat foo.c
char A_array[0x10];
char B_array[0x20];
char C_array[0x30];

$ cc -o prog main.c foo.c
$ nm -vx prog | grep array
[32]    |0x00020754|0x00000010|OBJT |GLOB |0x0  |15  |A_array
[34]    |0x00020764|0x00000030|OBJT |GLOB |0x0  |15  |C_array
[42]    |0x00020794|0x00000020|OBJT |GLOB |0x0  |15  |B_array

By defining these symbols as initialized data items, the relative ordering of these symbols within the input file is carried over to the output file:

$ cat foo.c
char A_array[0x10] = { 0 };
char B_array[0x20] = { 0 };
char C_array[0x30] = { 0 };

$ cc -o prog main.c foo.c
$ nm -vx prog | grep array
[32]    |0x000206bc|0x00000010|OBJT |GLOB |0x0  |12  |A_array
[42]    |0x000206cc|0x00000020|OBJT |GLOB |0x0  |12  |B_array
[34]    |0x000206ec|0x00000030|OBJT |GLOB |0x0  |12  |C_array

Defining Additional Symbols

Besides the symbols provided from input files, you can supply additional symbol references or definitions to a link-edit. In the simplest form, symbol references can be generated using the link-editor's -u option. Greater flexibility is provided with the link-editor's -M option and an associated mapfile that enables you to define symbol references and a variety of symbol definitions.

The -u option provides a mechanism for generating a symbol reference from the link-edit command line. This option can be used to perform a link-edit entirely from archives, or to provide additional flexibility in selecting the objects to extract from multiple archives. See section Archive Processing for an overview of archive extraction.

For example, perhaps you want to generate a dynamic executable from the relocatable object main.o, which refers to the symbols foo and bar. You want to obtain the symbol definition foo from the relocatable object foo.o contained in lib1.a, and the symbol definition bar from the relocatable object bar.o, contained in lib2.a.

However, the archive lib1.a also contains a relocatable object defining the symbol bar. This relocatable object is presumably of differing functionality to the relocatable object provided in lib2.a. To specify the required archive extraction, you can use the following link-edit:

$ cc -o prog -L. -u foo -l1 main.o -l2

The -u option generates a reference to the symbol foo. This reference causes extraction of the relocatable object foo.o from the archive lib1.a. The first reference to the symbol bar occurs in main.o, which is encountered after lib1.a has been processed. Therefore, the relocatable object bar.o is obtained from the archive lib2.a.


Note - This simple example assumes that the relocatable object foo.o from lib1.a does not directly or indirectly reference the symbol bar. If it does then the relocatable object bar.o is also extracted from lib1.a during its processing. See Archive Processing for a discussion of the link-editor's multi-pass processing of an archive.


A more extensive set of symbol definitions can be provided using the link-editor's -M option and an associated mapfile. The syntax for these mapfile entries is:

[ name ] {
      scope:
            symbol [ = [ type ] [ value ] [ size ] [ attribute ] ];
} [ dependency ];

name

A label for this set of symbol definitions, if present, identifies a version definition within the image. See Chapter 5, Application Binary Interfaces and Versioning.

scope

Indicates the visibility of the symbols' binding within the output file being generated. All symbols defined with a mapfile are treated as global in scope during the link-edit process. That is, they are resolved against any other symbols of the same name obtained from any of the input files. The following definitions, and aliases, define a symbols' visibility in the object being created:

default / global

Symbols of this scope remain visible to other external objects. References to such symbols from within the object are bound at runtime, thus allowing interposition to take place.

protected / symbolic

Symbols of this scope remain visible to other external objects. References to these symbols from within the object are bound at link-edit, thus preventing runtime interposition. This scope definition has the same affect as a symbol with STV_PROTECTED visibility. See Table 7-24.

hidden / local

Symbols of this scope are reduced to symbols with a local binding. Symbols of this scope are not visible to other external objects. This scope definition has the same affect as a symbol with STV_HIDDEN visibility. See Table 7-24.

eliminate

Symbols of this scope are hidden. Their symbol table entries are eliminated.

symbol

The name of the symbol required. If the name is not followed by one of the symbol attributes, type, value, size or extern, a symbol reference is created. This reference is exactly the same as would be generated using the -u option discussed earlier in this section. If the symbol name is followed by any symbol attributes, then a symbol definition is generated using the associated attributes.

When in local scope, this symbol name can be defined as the special auto-reduction directive "*". This directive results in all global symbols, not explicitly defined to be global in the mapfile, receiving a local binding within any dynamic object file being generated.

type

Indicates the symbol type attribute. This attribute can be either data, function, or COMMON. The former two type attributes result in an absolute symbol definition. See Symbol Table Section. The latter type attribute results in a tentative symbol definition.

value

Indicates the value attribute and takes the form of Vnumber.

size

Indicates the size attribute and takes the form of Snumber.

attribute

This keyword provides additional attributes for the symbol:

EXTERN

Indicates the symbol is defined externally to the object being created. This attribute can be used with the DIRECT or NODIRECT attributes to establish individual direct, or no-direct references. Undefined symbols that would be flagged with the -z defs option can also be suppressed with this option.

DIRECT

Indicates that this symbol should be directly bound to. This attribute can be used with the EXTERN attribute to control binding to an external symbol. See Direct Binding.

NODIRECT

Indicates that this symbol should not be directly bound to. This state applies to references from within the object being created and from external references. This attribute can be used with the EXTERN attribute to control binding to an external symbol. See Direct Binding.

dependency

Represents a version definition that is inherited by this definition. See Chapter 5, Application Binary Interfaces and Versioning.

Previous Previous     Contents     Index     Next Next