Creating a Symbol Capabilities Example
The function foo
() is used to create a family of variants where a compilation directive is used to produce diagnostics that identify the eventual variant.
$ cat foo.c
#include <stdio.h>
void bar(const char *fmt, const char *str)
{
(void) printf(fmt, str);
}
void foo()
{
bar("called: foo-%s\n", CAPABILITY);
}
Three variants are created, a generic lead variant, and two variants that will have capabilities assigned. Each variant is supplied a variant identifier string from the command line.
$ cc -c -Kpic -DCAPABILITY=\"generic\" -o foo.o foo.c $ cc -c -Kpic -DCAPABILITY=\"HWCAP-1\" -o foo.1.o foo.c $ cc -c -Kpic -DCAPABILITY=\"HWCAP-2\" -o foo.2.o foo.c
Normally the two capabilities variants, foo.hwcap-1.o
and foo.hwcap-2.o
, would be created using targeted compiler options or specialized assembler code. The compilation tools would record in each variant the capabilities needed. However, in this example each variant is assigned a hard coded set of capability requirements using a mapfile
. In addition, this mapfile
defines the interface that is exported for the variant.
$ cat mapfile.hwcap.1 $mapfile_version 2 CAPABILITY hwcap-1 { HW = AES; }; SYMBOL_SCOPE { global: foo; local: *; }; $ cat mapfile.hwcap.2 $mapfile_version 2 CAPABILITY hwcap-2 { HW = AVX; }; SYMBOL_SCOPE { global: foo; local: *; };
The two capabilities variants are built using these mapfiles
.
$ ld -r -o foo.1.objcap.o -Breduce -M mapfile.hwcap.1 foo.1.o $ ld -r -o foo.2.objcap.o -Breduce -M mapfile.hwcap.2 foo.2.o
The capabilities that are recorded in foo.1.objcap.o
and foo.2.objcap.o
apply to the entire object.
$ elfdump -H foo.1.objcap.o ... Object Capabilities: index tag value [0] CA_SUNW_ID 0x25 hwcap-1 [1] CA_SUNW_HW_1 0x4000000 [ AES ] $ elfdump -H foo.2.objcap.o ... Object Capabilities: index tag value [0] CA_SUNW_ID 0x45 hwcap-2 [1] CA_SUNW_HW_1 0x20000000 [ AVX ]
The only interface that each object offers is foo
(). bar
() has been demoted to a local symbol.
$ elfdump -s foo.1.objcap.o | egrep "foo|bar" | fgrep FUNC [20] 0x10 0x36 FUNC LOCL H 0 .text bar [22] 0x50 0x3c FUNC GLOB D 1 .text foo $ elfdump -s foo.2.objcap.o | egrep "foo|bar" | fgrep FUNC [20] 0x10 0x36 FUNC LOCL H 0 .text bar [22] 0x50 0x3c FUNC GLOB D 1 .text foo
Note:
The sole export offoo
() could have been achieved by defining bar
() as static. However, in a real solution this variant could be constructed from multiple relocatable objects. The use of the -B reduce
option and the mapfile
SYMBOL_SCOPE
directive illustrate how the interfaces exported from the variant can be controlled in such cases.
The next step transforms these object capabilities variants into symbol capabilities variants.
$ ld -r -o foo.1.symcap.o -z symbolcap foo.1.objcap.o $ ld -r -o foo.2.symcap.o -z symbolcap foo.2.objcap.o
The capabilities that are recorded in foo.1.symcap.o
and foo.2.symcap.o
apply to the exported symbols of the object.
$ elfdump -H foo.1.symcap.o ... Symbol Capabilities: index tag value [1] CA_SUNW_ID 0x2d hwcap-1 [2] CA_SUNW_HW_1 0x4000000 [ AES ] Symbols: index value size type bind oth ver shndx name [22] 0x50 0x3c FUNC LOCL D 0 .text foo%hwcap-1 ... $ elfdump -H foo.2.symcap.o ... Symbol Capabilities: index tag value [1] CA_SUNW_ID 0x37 hwcap-2 [2] CA_SUNW_HW_1 0x20000000 [ AVX ] Symbols: index value size type bind oth ver shndx name [24] 0x50 0x3c FUNC LOCL D 0 .text foo%hwcap-2 ...
The three variants are now combined into a final object. Here, a shared object is created, as this can provide capability variants to many applications.
$ cc -G -o libfoo.so.1 -Kpic foo.o foo.1.symcap.o foo.2.symcap.o
The variants are captured into a family, with foo
() being the single exported interface that leads the family.
$ elfdump -H libfoo.so.1
...
Capabilities family: foo
chainndx symndx name
1 [9] foo
2 [1] foo%hwcap-1
3 [2] foo%hwcap-2
Note that these capability variants can also be used to create a dynamic executable.
$ cc -o main.2 main.c foo.o foo.1.symcap.o foo.2.symcap.o