The significance of reducing symbol scope from global to local is covered in more detail in the section "Reducing Symbol Scope".
The following example shows how two absolute symbol definitions can be defined and used to resolve the references from the input file main.c.
$ cat main.c extern int foo(); extern int bar; main() { (void) printf("&foo = %x\n", &foo); (void) printf("&bar = %x\n", &bar); } $ cat mapfile { global: foo = FUNCTION V0x400; bar = DATA V0x800; }; $ cc -o prog -M mapfile main.c $ prog &foo = 400 &bar = 800 $ nm -x prog | egrep "foo$|bar$" [37] |0x00000800|0x00000000|OBJT |GLOB |0x0 |ABS |bar [42] |0x00000400|0x00000000|FUNC |GLOB |0x0 |ABS |foo |
When obtained from an input file, symbol definitions for functions or data items are usually associated with elements of data storage. Amapfile definition is insufficient to be able to construct this data storage, so these symbols must remain as absolute values.
However, a mapfile can also be used to define a COMMON, or tentative, symbol. Unlike other types of symbol definition, tentative symbols do not occupy storage within a file, but define storage that must be allocated at runtime. Therefore, symbol definitions of this kind can contribute to the storage allocation of the output file being generated.
A feature of tentative symbols that differs from other symbol types is that their value attribute indicates their alignment requirement. A mapfile definition can therefore be used to realign tentative definitions obtained from the input files of a link-edit.
The following example shows the definition of two tentative symbols. The symbol foo defines a new storage region whereas the symbol bar is actually used to change the alignment of the same tentative definition within the file main.c.
$ cat main.c extern int foo; int bar[0x10]; main() { (void) printf("&foo = %x\n", &foo); (void) printf("&bar = %x\n", &bar); } $ cat mapfile { global: foo = COMMON V0x4 S0x200; bar = COMMON V0x100 S0x40; }; $ cc -o prog -M mapfile main.c ld: warning: symbol `bar' has differing alignments: (file mapfile value=0x100; file main.o value=0x4); largest value applied $ prog &foo = 20940 &bar = 20900 $ nm -x prog | egrep "foo$|bar$" [37] |0x00020900|0x00000040|OBJT |GLOB |0x0 |16 |bar [42] |0x00020940|0x00000200|OBJT |GLOB |0x0 |16 |foo |
Note - This symbol resolution diagnostic can be suppressed by using the link-editor's -t option.
Reducing Symbol Scope
Symbol definitions defined to have local scope within a mapfile can be used to reduce the symbol's eventual binding. This mechanism can play an important role in reducing the symbol's visibility to future link-edits that use the generated file as part of their input. In fact, this mechanism can provide for the precise definition of a file's interface, and so restrict the functionality made available to others.
For example, say you want to generate a simple shared object from the files foo.c and bar.c. The file foo.c contains the global symbol foo, which provides the service that you want to make available to others. The file bar.c contains the symbols bar and str, which provide the underlying implementation of the shared object. The creation of a simple shared object usually results in all three of these symbols having global scope.
$ cat foo.c extern const char * bar(); const char * foo() { return (bar()); } $ cat bar.c const char * str = "returned from bar.c"; const char * bar() { return (str); } $ cc -o lib.so.1 -G foo.c bar.c $ nm -x lib.so.1 | egrep "foo$|bar$|str$" [29] |0x000104d0|0x00000004|OBJT |GLOB |0x0 |12 |str [32] |0x00000418|0x00000028|FUNC |GLOB |0x0 |6 |bar [33] |0x000003f0|0x00000028|FUNC |GLOB |0x0 |6 |foo |
You can now use the functionality offered by this shared object as part of the link-edit of another application. References to the symbol foo are bound to the implementation provided by the shared object.
Because of their global binding, direct reference to the symbols bar and str is also possible. This can have dangerous consequences, as you might later change the implementation underlying the function foo. In so doing, you could unintentionally cause an existing application that had bound to bar or str to fail or misbehave.
Another consequence of the global binding of the symbols bar and str is that they can be interposed upon by symbols of the same name. The interposition of symbols within shared objects is covered in section "Simple Resolutions". This interposition can be intentional and be used as a means of circumventing the intended functionality offered by the shared object. On the other hand, this interposition can be unintentional, the result of the same common symbol name used for both the application and the shared object.
When developing the shared object, you can protect against this type of scenario by reducing the scope of the symbols bar and str to a local binding. In the following example the symbols bar and str are no longer available as part of the shared objects interface. Thus these symbols cannot be referenced, or interposed upon, by an external object. You have effectively defined an interface for the shared object. This interface can be managed while hiding the details of the underlying implementation.
$ cat mapfile { local: bar; str; }; $ cc -o lib.so.1 -M mapfile -G foo.c bar.c $ nm -x lib.so.1 | egrep "foo$|bar$|str$" [27] |0x000003dc|0x00000028|FUNC |LOCL |0x0 |6 |bar [28] |0x00010494|0x00000004|OBJT |LOCL |0x0 |12 |str [33] |0x000003b4|0x00000028|FUNC |GLOB |0x0 |6 |foo |
This symbol scope reduction has an additional performance advantage. The symbolic relocations against the symbols bar and str that would have been necessary at runtime are now reduced to relative relocations. This reduces the runtime overhead of initializing and processing the shared object. See "When Relocations are Performed" for details of symbolic relocation overhead.
As the number of symbols processed during a link-edit increases, the ability to define each local scope reduction within a mapfile becomes harder to maintain. An alternative and more flexible mechanism enables you to define the shared objects interface in terms of the global symbols that should be maintained, and instructs the link-editor to reduce all other symbols to local binding. This mechanism is achieved using the special auto-reduction directive "*". For example, the previous mapfile definition can be rewritten to define foo as the only global symbol required in the output file generated:
$ cat mapfile lib.so.1.1 { global: foo; local: *; }; $ cc -o lib.so.1 -M mapfile -G foo.c bar.c $ nm -x lib.so.1 | egrep "foo$|bar$|str$" [30] |0x00000370|0x00000028|FUNC |LOCL |0x0 |6 |bar [31] |0x00010428|0x00000004|OBJT |LOCL |0x0 |12 |str [35] |0x00000348|0x00000028|FUNC |GLOB |0x0 |6 |foo |
This example also defines a version name, lib.so.1.1, as part of the mapfile directive. This version name establishes an internal version definition that defines the file's symbolic interface. The creation of a version definition is recommended, and forms the foundation of an internal versioning mechanism that can be used throughout the evolution of the file. See Chapter 5, Application Binary Interfaces and Versioning.
Note - If a version name is not supplied, the output file name is used to label the version definition. The versioning information created within the output file can be suppressed using the link-editor's -z noversion option.
Whenever a version name is specified, all global symbols must be assigned to a version definition. If any global symbols remain unassigned to a version definition, the link-editor generates a fatal error condition:
$ cat mapfile lib.so.1.1 { global: foo; }; $ cc -o lib.so.1 -M mapfile -G foo.c bar.c Undefined first referenced symbol in file str bar.o (symbol has no version assigned) bar bar.o (symbol has no version assigned) ld: fatal: Symbol referencing errors. No output written to lib.so.1 |