Complex Resolutions
Complex resolutions occur when two symbols of the same name are found with differing attributes. In these cases, the link-editor selects the most appropriate symbol and generates a warning message indicating the symbol, the attributes that conflict, and the identity of the file from which the symbol definition is taken. In the following example two files with a definition of the data item array have different size requirements.
$ cat foo.c int array[1]; $ cat bar.c int array[2] = { 1, 2 }; $ cc -dn -r -o temp.o foo.c bar.c ld: warning: symbol `array' has differing sizes: (file foo.o value=0x4; file bar.o value=0x8); bar.o definition taken |
A similar diagnostic is produced if the symbol's alignment requirements differ. In both of these cases, the diagnostic can be suppressed by using the link-editor's -t option.
Another form of attribute difference is the symbol's type. In the following example the symbol bar() has been defined as both a data item and a function.
$ cat foo.c bar() { return (0); } $ cc -o libfoo.so -G -K pic foo.c $ cat main.c int bar = 1; main() { return (bar); } $ cc -o main main.c -L. -lfoo ld: warning: symbol `bar' has differing types: (file main.o type=OBJT; file ./libfoo.so type=FUNC); main.o definition taken |
Note - Symbol types in this context are classifications that can be expressed in ELF. They are not related to the data types as employed by the programming language, except in the crudest fashion.
In cases like the previous example, the relocatable object definition is taken when the resolution occurs between a relocatable object and a shared object, or the first definition is taken when the resolution occurs between two shared objects. When such resolutions occur between symbols of different bindings (weak or global), a warning is also produced.
Inconsistencies between symbol types are not suppressed by the link-editor's -t option.
Fatal Resolutions
Symbol conflicts that cannot be resolved result in a fatal error condition. In this case, an appropriate error message is provided indicating the symbol name together with the names of the files that provided the symbols, and no output file is generated. Although the fatal condition is sufficient to terminate the link-edit, all input file processing is first completed. In this manner, all fatal resolution errors can be identified.
The most common fatal error condition exists when two relocatable objects both define symbols of the same name, and neither symbol is a weak definition:
$ cat foo.c int bar = 1; $ cat bar.c bar() { return (0); } $ cc -dn -r -o temp.o foo.c bar.c ld: fatal: symbol `bar' is multiply-defined: (file foo.o and file bar.o); ld: fatal: File processing errors. No output written to int.o |
foo.c and bar.c have conflicting definitions for the symbol bar. Because the link-editor cannot determine which should dominate, the link-edit usually terminates with an error message. You can use the link-editor's -z muldefs option to suppress this error condition, and allow the first symbol definition to be taken.
Undefined Symbols
After all of the input files have been read and all symbol resolution is complete, the link-editor searches the internal symbol table for any symbol references that have not been bound to symbol definitions. These symbol references are referred to as undefined symbols. The effect of these undefined symbols on the link-edit process can vary according to the type of output file being generated, and possibly the type of symbol.
Generating an Executable Output File
When the link-editor is generating an executable output file, the link-editor's default behavior is to terminate with an appropriate error message should any symbols remain undefined. A symbol remains undefined when a symbol reference in a relocatable object is never matched to a symbol definition:
$ cat main.c extern int foo(); main() { return (foo()); } $ cc -o prog main.c Undefined first referenced symbol in file foo main.o ld: fatal: Symbol referencing errors. No output written to prog |
In a similar manner, a symbol reference within a shared object that is never matched to a symbol definition when the shared object is being used to create a dynamic executable will also result in an undefined symbol:
$ cat foo.c extern int bar; foo() { return (bar); } $ cc -o libfoo.so -G -K pic foo.c $ cc -o prog main.c -L. -lfoo Undefined first referenced symbol in file bar ./libfoo.so ld: fatal: Symbol referencing errors. No output written to prog |
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 will occur. Although this error can be detected during the initial execution and testing of an application, 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 being built with an explicit reference to libbar.so, and because libbar.so has a dependency on libfoo.so, 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 will fail 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.