Multiple instances of initialized data, where each instance is specific to a system, can be provided within the same object. However, providing such data through functional interfaces if often simpler, and is recommended. See Creating a Family of Symbol Capabilities Functions. Special care is required to provide multiple instances of initialized data within an executable.
The following example initializes a data item foo within foo.c, to point to a machine name string. This file can be compiled for various machines, and each instance is identified with a machine capability. A reference to this data item is made from bar() from the file bar.c. A shared object foobar.so.1 is then created by combining bar() with two capabilities instances of foo.
$ cat foo.c char *foo = MACHINE; $ cat bar.c #include <stdio.h> extern char *foo = MACHINE; void bar() { (void) printf("machine: %s\n", foo); } $ elfdump -H foobar.so.1 Capabilities Section: .SUNW_cap Symbol Capabilities: index tag value [1] CA_SUNW_ID sun4u [2] CA_SUNW_MACH sun4u Symbols: index value size type bind oth ver shndx name [1] 0x000108d4 0x00000004 OBJT LOCL D 0 .data foo%sun4u Symbol Capabilities: index tag value [4] CA_SUNW_ID sun4v [5] CA_SUNW_MACH sun4v Symbols: index value size type bind oth ver shndx name [2] 0x000108d8 0x00000004 OBJT LOCL D 0 .data foo%sun4v |
An application can reference bar(), and the runtime linker binds to the instance of foo that is associated with the underlying system.
$ uname -m sun4u $ main machine: sun4u |
The proper operation of this code depends on the code having been compiled to be position-independent, as is normally the case for code in sharable objects. See Position-Independent Code. Position-independent data references are indirect references, which allow the runtime linker to locate the required reference and update elements of the data segment. This relocation update of the data segment preserves the text segment as read-only.
However, the code within an executable is typically position-dependent. In addition, data references within an executable are bound at link-edit time. Within an executable, a symbol capabilities data reference must remain unresolved through a global data item, so that the runtime linker can select from the symbol capabilities family. If the reference from bar() in the previous example bar.c is compiled as position-dependent code, then the text segment of the executable must be relocated at runtime. By default, this condition results in a fatal link-time error.
$ cc -o main main.c bar.c foo.o foo.1.o foo.2.o ... warning: Text relocation remains referenced against symbol offset in file foo 0x0 bar.o foo 0x8 bar.o |
One approach to solve this error condition is to compile bar.c as position-independent. Note however, that all references to any symbol capabilities data items from within the executable must be compiled position-independent for this technique to work.
Although data can be accessed using the symbol capabilities mechanism, making data items a part of the public interface to an object can be problematic. An alternative, and more flexible model, is to encapsulate each data item within a symbol capabilities function. This function provides the sole means of access to the data. Hiding data behind a symbol capabilities function has the important benefit of allowing the data to be defined static and kept private. The previous example can be coded to use symbol capabilities functions.
$ cat foobar.c cat bar.c #include <stdio.h> static char *foo = MACHINE; void bar() { (void) printf("machine: %s\n", foo); } $ elfdump -H main Capabilities Section: .SUNW_cap Symbol Capabilities: index tag value [1] CA_SUNW_ID sun4u [2] CA_SUNW_MACH sun4u Symbols: index value size type bind oth ver shndx name [1] 0x0001111c 0x0000001c FUNC LOCL D 0 .text bar%sun4u Symbol Capabilities: index tag value [4] CA_SUNW_ID sun4v [5] CA_SUNW_MACH sun4v Symbols: index value size type bind oth ver shndx name [2] 0x00011138 0x0000001c FUNC LOCL D 0 .text bar%sun4v $ uname -m sun4u $ main machine: sun4u |