Go to main content

Oracle® Solaris 11.4 Linkers and Libraries Guide

Exit Print View

Updated: October 2019
 
 

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 of foo() 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