Programming Interfaces Guide

Chapter 13 The Solaris ABI and ABI Tools

The Solaris Application Binary Interface (ABI) defines the interfaces that are available for the use of application developers. Conforming to the ABI enhances an application's binary stability. This chapter discusses the Solaris ABI and the tools provided to verify an application's compliance with the ABI, including:

What is the Solaris ABI?

The Solaris ABI is the set of supported run-time interfaces that are available for an application to use with the Solaris operating system. The most important components of the ABI are in the following list:

The main component of the Solaris ABI is the set of system library interfaces. The term ABI in this chapter refers only to that component. The ABI contains exclusively C language interfaces, as C is the only language for which the Solaris operating system provides interfaces.

C source code that is written to the Solaris API (Application Programming Interface) is transformed by the C compiler into a binary for one of four ABI versions. The versions are:

While the ABI is very similar to the API, the source compilation process introduces several important differences:

For these reasons, source-level (API) compatibility does not provide a sufficient expectation of binary compatibility across Solaris releases.

The Solaris ABI is made up of the supported interfaces provided by the operating system. Some of the interfaces that are available in the system are intended for the exclusive use of the operating system. These exclusive interfaces are not available for use by an application. Prior to the SunOS 5.6 release, all of the interfaces in Solaris libraries were available for application developers to use. With the library symbol scoping technology available in the Solaris link editor, interfaces not intended for use outside of a library have their scope reduced to be purely local to the library. See the Linker and Libraries Guide for details. Due to system requirements, not all private interfaces can have such a reduced scope. These interfaces are labeled private, and are not included in the Solaris ABI.

Defining the Solaris ABI

The Solaris ABI is defined in the Solaris libraries. These definitions are done by means of the library versioning technology and policies used in the link editor and run-time linker.

Symbol Versioning in Solaris Libraries

The Solaris link editor and run-time linker use two kinds of library versioning: file versioning and symbol versioning. In file versioning, a library is named with an appended version number, such as libc.so.1. When an incompatible change is made to one or more public interfaces in that library, the version number is incremented (for example, to libc.so.2). In a dynamically linked application, a symbol bound to at build time might not be present in the library at run time. In symbol versioning, the Solaris linker associates a set of symbols with a name. The linker then checks for the presence of the name in the library during run-time linking to verify the presence of the associated symbols.

Library symbol versioning associates a set of symbols with a symbol version name, and number if that name has a numbering scheme, by means of a mapfile. The following is an example mapfile for a hypothetical Sun library, libfoo.so.1.

        SUNW_1.2 {
            global:
                symbolD;
                symbolE
        } SUNW_1.1;

        SUNW_1.1 {
            global:
                symbolA;
                symbolB;
                symbolC;
        };

        SUNWprivate {
            global:
                __fooimpl;
            local: *;
        };

This mapfile indicates that symbolA, symbolB, and symbolC are associated with version SUNW_1.1, symbolD and symbolE are associated with SUNW_1.2, and that SUNW_1.2 inherits all the symbols associated with SUNW_1.1. The symbol __fooimpl is associated with a different named set which does not have a numbered inheritance chain.

During build time, the link editor examines the symbols used by the application. The link editor records the set names in the application on which those symbols depend. In the case of chained sets, the link editor records the smallest named set containing all the symbols used by the application. If an application uses only symbolA and symbolB, the link editor records a dependency on SUNW_1.1. If an application uses symbolA, symbolB, and symbolD, the link editor records a dependency on SUNW_1.2, because SUNW_1.2 includes SUNW_1.1.

At run time, the linker verifies that the version names recorded as dependencies in the application are present in the libraries that are being linked. This process is a quick way to verify the presence of required symbols. For more details, see the Linker and Libraries Guide.


Note –

The local: * directive in the mapfile means that any symbol in the library that is not explicitly associated with a named set is scoped locally to the library. Such locally scoped symbols are not visible outside the library. This convention ensures that symbols are only visible when associated with a symbol versioning name.


Using Symbol Versioning to Label the Solaris ABI

Since all visible symbols in a library belong to some named set, the naming scheme can be used to label the symbols' ABI status. This labeling is done by associating all private interfaces with a set name beginning with SUNWprivate. Public interfaces begin with other names, specifically:

These public, named sets are numbered with a major.minor numbering scheme. When a set includes new symbols, the set's minor version number increases. When an existing symbol changes in a way that makes the symbol incompatible with its previous behavior, the major version number of the set that includes that symbol increases. When an existing symbol changes incompatibly, the version number in the library's file name also increases.

The definition of the Solaris library ABI is therefore contained in the libraries, and consists of the set of symbols that are associated with symbol version names that do not begin with SUNWprivate. The pvs command lists the symbols in a library.

Solaris ABI Tools

The Solaris operating system provides two tools to verify that an application's use of Solaris interfaces conforms to the Solaris ABI. The appcert utility statically examines the Solaris library interfaces used by ELF binaries for instances of private interface usage. The appcert utility produces summary and detailed reports of any potential binary stability problems it finds. The apptrace tool uses the link-auditing capability of the run-time linker to dynamically trace Solaris library routine calls as the application runs. This capability enables developers to examine an application's use of the Solaris system interfaces.

The ABI tools enable easy, rapid identification of binaries that might have binary compatibility problems with a given Solaris release. To check binary stability, perform the following steps:

appcert Utility

The appcert utility is a Perl script that statically examines ELF binaries and compares the library symbols used against a model of public interfaces and private interfaces in a given Solaris release. The utility runs on either SPARC or x86 platforms. The utility can check interface usage for both SPARC and x86 3F2-bit interfaces as well as the 64-bit interfaces on SPARC. Note that appcert only examines C language interfaces.

As new Solaris releases become available, some library interfaces might change their behavior or disappear entirely. These changes can affect the performance of applications that rely on those interfaces. The Solaris ABI defines runtime library interfaces that are safe and stable for application use. The appcert utility is designed to help developers verify an application's compliance with the Solaris ABI.

What appcert Checks

The appcert utility examines your applications for:

Private Symbol Usage

Private symbols are functions or data that is used by Solaris libraries to call each other. The semantic behavior of private symbols might change, and symbols might sometimes be removed. Such symbols are called demoted symbols. The mutable nature of private symbols introduces the potential for instability in applications that depend on private symbols.

Static Linking

The semantics of private symbol calls between Solaris libraries might change between releases. Therefore, the creation of static links to archives degrades an application's binary stability. Dynamic links to the archive's corresponding shared object file avoid this problem.

Unbound Symbols

The appcert utility uses the dynamic linker to resolve the library symbols that are used by the application being examined. Symbols that the dynamic linker cannot resolve are called unbound symbols. Unbound symbols might be caused by environment problems, such as an incorrectly set LD_LIBRARY_PATH variable. Unbound symbols might also be caused by build problems, such as omitting the definitions of the -llib or -z switches at compile time. While these examples are minor, unbound symbols that are reported by appcert might indicate a more serious problem, such as a dependency on a private symbol that no longer exists.

What appcert Does Not Check

If the object file appcert is examining depends on libraries, those dependencies must be recorded in the object. To do so, be sure to use the compiler's -l switch when compiling the code. If the object file depends on other shared libraries, those libraries must be accessible through LD_LIBRARY_PATH or RPATH at the time you run appcert.

The appcert application cannot check 64–bit applications unless the machine is running the 64–bit Solaris kernel. Since Solaris provides no 64–bit static libraries, appcert does not perform static-linking checks on 64–bit applications.

The appcert utility cannot examine:

Working with appcert

To check your application with appcert, type:


appcert object|directory

replacing object|directory with either:


Note –

You might run appcert in an environment that is different from the environment in which the application runs. If these environments are different, appcert might not be able to correctly resolve references to Solaris library interfaces.


The appcert utility uses the Solaris runtime linker to construct a profile of interface dependencies for each executable or shared object file. This profile is used to determine the Solaris system interfaces upon which the application depends. The dependencies that are outlined in the profile are compared to the Solaris ABI to verify conformance. No private interfaces should be found.

The appcert utility recursively searches directories for object files, ignoring non-ELF object files. After appcert has finished checking the application, appcert prints a rollup report to the standard output, usually the screen. A copy of this report is written in the working directory, which is usually /tmp/appcert.pid, in a file that is named Report. In the subdirectory name, pid represents the 1–to–6 digit process ID of that particular instantiation of appcert. See appcert Results for more on the directory structure to which appcert writes output files.

appcert Options

The following options modify the behavior of the appcert utility. You can type any of these options at the command line, after the appcert command but before the object|directory operand.

-B

Runs appcert in batch mode.

In batch mode, the report produced by appcert contains one line for each binary checked.

A line that begins with PASS indicates the binary that is named in that line did not trigger any appcert warnings.

A line that begins with FAIL indicates problems were found in that binary.

A line that begins with INC indicates the binary that is named in that line could not be completely checked.

-f infile

The file infile should contain a list of files to check, with one file name per line. These files are added to any files already specified at the command line. If you use this switch, you do not need to specify an object or directory at the command line.

-h

Prints usage information for appcert.

-L

By default, appcert notes any shared objects in an application, and appends the directories in which the shared objects reside to LD_LIBRARY_PATH. The -L switch disables this behavior.

-n

By default, appcert follows symbolic links when appcert searches directories for binaries to check. The -n switch disables this behavior.

-S

Appends the Solaris library directories /usr/openwin/lib and /usr/dt/lib to LD_LIBRARY_PATH.

-w working_dir

Specifies a directory in which to run the library components. Temporary files are also created in the directory specified by this switch. If this switch is not specified, appcert uses the /tmp directory.

Using appcert for Application Triage

The appcert utility can be used to quickly and easily discern which applications in a given set have potential stability problems. If appcert does not report any stability problems, the application is not likely to encounter binary stability problems in subsequent Solaris releases. The following table lists some common binary stability problems.

Table 13–1 Common Binary Stability Problems

Problem 

Course of Action 

Use of a private symbol that is known to change 

Eliminate use of symbol immediately. 

Use of a private symbol that has not changed yet 

Application can still be run for now, but eliminate use of symbol as soon as practical. 

Static linking of a library with a shared object counterpart 

Use shared object counterpart instead. 

Static linking of a library with no shared object counterpart 

Convert .a file to .so file by using the command ld -z allextract if possible. Otherwise, continue to use static library until shared object is available.

Use of a private symbol for which no public equivalent is available 

Contact Sun and request a public interface. 

Use of a symbol that is deprecated, or use of a symbol that is planned for removal 

Application can still be run for now, but eliminate use of symbol as soon as practical. 

Use of a public symbol that has changed 

Recompile. 

Potential stability problems caused by the use of private interfaces might not occur on a given release. The behavior of private interfaces does not always change between releases. To verify that a private interface's behavior has changed in the target release, use the apptrace tool. Usage of apptrace is discussed in Using apptrace for Application Verification.

appcert Results

The results of the appcert utility's analysis of an application's object files are written to several files that are located in the appcert utility's working directory, typically /tmp. The main subdirectory under the working directory is appcert.pid, where pid is the process ID for that instantiation of appcert. The appcert utility's results are written to the following files:

Index

Contains the mapping between checked binaries and the subdirectory in which appcert output specific to that binary is located.

Report

Contains a copy of the rollup report that is displayed on stdout when appcert is run.

Skipped

Contains a list of binaries that appcert was asked to check but was forced to skip, along with the reason each binary was skipped. These reasons are in the following list:

  • File is not a binary object

  • File cannot be read by the user

  • File name contains metacharacters

  • File does not have the execute bit set

objects/object_name

A separate subdirectory is under the objects subdirectory for each object examined by appcert. Each of these subdirectories contains the following files:

check.demoted.symbols

Contains a list of symbols that appcert suspects are demoted Solaris symbols.

check.dynamic.private

Contains a list of private Solaris symbols to which the object is directly bound.

check.dynamic.public

Contains a list of public Solaris symbols to which the object is directly bound.

check.dynamic.unbound

Contains a list of symbols not bound by the dynamic linker when running ldd -r. Lines returned by ldd containing “file not found” are also included.

summary.dynamic

Contains a printer-formatted summary of dynamic bindings in the objects appcert examined, including tables of public and private symbols used from each Solaris library.

Returns one of four exit values.

0

No potential sources of binary instability were found by appcert.

1

The appcert utility did not run successfully.

2

Some of the objects checked by appcert have potential binary stability problems.

3

The appcert utility did not find any binary objects to check.

Correcting Problems Reported by appcert

Using apptrace for Application Verification

The apptrace utility is a C program which dynamically traces calls to Solaris library routines as an application runs. apptrace works on either SPARC or x86 platforms. apptrace can trace interface calls for both SPARC and x86 32-bit interfaces, as well as the 64-bit interfaces on SPARC. As with appcert, apptrace only examines C language interfaces.

Application Verification

After using appcert to determine an application is at risk of binary instability, apptrace helps assess the degree of risk in each case. To determine an application's binary compatibility with a given release, verify the successful use of each interface used by the application with apptrace.

The apptrace utility can verify that an application is using public interfaces correctly. For example, an application that is using the open() to open the administrative file /etc/passwd directly should instead use the appropriate programmatic interfaces. This ability to inspect the usage of the Solaris ABI enables easy and rapid identification of potential interface problems.

Running apptrace

The apptrace utility does not require any modification of the application being traced. To use apptrace, type apptrace, followed by any desired options along with the command line used to run the application of interest. The apptrace utility works by using the link-auditing capability of the runtime linker to intercept the application's calls to Solaris library interfaces. The apptrace utility then traces the calls by printing the names and values of the call's arguments and return value. The tracing output can be on a single line or arranged across multiple lines for readability. Public interfaces are printed in human-readable form. Private interfaces are printed in hexadecimal.

The apptrace utility enables selective tracing of calls, both at the level of individual interfaces and the level of libraries. For example, apptrace can trace calls to printf() coming from libnsl, or a range of calls within a specific library. The apptrace utility can also verbosely trace user-specified calls. The specifications that dictate apptrace behavior are governed by a syntax that is consistent with the usage of truss(1). The -f option directs apptrace to follow forked child processes. The -o option specifies an output file for apptrace results.

The apptrace utility traces only library-level calls and is loaded into the running application process, gaining a performance increase over truss. With the exception of printf, apptrace cannot trace calls to functions that accept variable argument lists or examine the stack or other caller information, for example, setcontext, getcontext, setjmp, longjmp, and vfork.

Interpreting apptrace Output

The following examples contain sample apptrace output from tracing a simple one-binary application, ls.


Example 13–1 Default Tracing Behavior


% apptrace ls /etc/passwd
ls       -> libc.so.1:atexit(func = 0xff3cb8f0) = 0x0
ls       -> libc.so.1:atexit(func = 0x129a4) = 0x0
ls       -> libc.so.1:getuid() = 0x32c3
ls       -> libc.so.1:time(tloc = 0x23918) = 0x3b2fe4ef
ls       -> libc.so.1:isatty(fildes = 0x1) = 0x1
ls       -> libc.so.1:ioctl(0x1, 0x540d, 0xffbff7ac)
ls       -> libc.so.1:ioctl(0x1, 0x5468, 0x23908)
ls       -> libc.so.1:setlocale(category = 0x6, locale = "") = "C"
ls       -> libc.so.1:calloc(nelem = 0x1, elsize = 0x40) = 0x23cd0
ls       -> libc.so.1:lstat64(path = "/etc/passwd", buf = 0xffbff6b0) = 0x0
ls       -> libc.so.1:acl(pathp = "/etc/passwd", cmd = 0x3, nentries = 0x0,
             aclbufp = 0x0) = 0x4
ls       -> libc.so.1:qsort(base = 0x23cd0, nel = 0x1, width = 0x40,
             compar = 0x12038)
ls       -> libc.so.1:sprintf(buf = 0x233d0, format = 0x12af8, ...) = 0
ls       -> libc.so.1:strlen(s = "") = 0x0
ls       -> libc.so.1:strlen(s = "/etc/passwd") = 0xb
ls       -> libc.so.1:sprintf(buf = 0x233d0, format = 0x12af8, ...) = 0
ls       -> libc.so.1:strlen(s = "") = 0x0
ls       -> libc.so.1:printf(format = 0x12ab8, ...) = 11
ls       -> libc.so.1:printf(/etc/passwd
format = 0x12abc, ...) = 1
ls       -> libc.so.1:exit(status = 0)

The previous example shows the default tracing behavior, tracing every library call on the command ls /etc/passwd. The apptrace utility prints a line of output for every system call, indicating:

The output from ls is mixed in with the apptrace output.


Example 13–2 Selective Tracing


% apptrace -t \*printf ls /etc/passwd
ls       -> libc.so.1:sprintf(buf = 0x233d0, format = 0x12af8, ...) = 0
ls       -> libc.so.1:sprintf(buf = 0x233d0, format = 0x12af8, ...) = 0
ls       -> libc.so.1:printf(format = 0x12ab8, ...) = 11
ls       -> libc.so.1:printf(/etc/passwd
format = 0x12abc, ...) = 1

The previous example shows how apptrace can selectively trace calls with regular-expression syntax. In the example, calls to interfaces ending in printf, which include sprintf, are traced in the same ls command as before. Consequently, apptrace only traces the printf and sprintf calls.


Example 13–3 Verbose Tracing


% apptrace -v sprintf ls /etc/passwd
ls       -> libc.so.1:sprintf(buf = 0x233d0, format = 0x12af8, ...) = 0
  buf =    (char *) 0x233d0 ""
  format = (char *) 0x12af8 "%s%s%s"
ls       -> libc.so.1:sprintf(buf = 0x233d0, format = 0x12af8, ...) = 0
  buf =    (char *) 0x233d0 ""
  format = (char *) 0x12af8 "%s%s%s"
/etc/passwd

The previous example shows the verbose tracing mode, where the arguments to sprintf are printed on multiple output lines for readability. At the end, apptrace displays the output of the ls command.