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