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
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