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:
The definition and purpose of the Solaris ABI, discussed in Defining the Solaris ABI.
The usage of the two ABI tools, appcert and apptrace, discussed in Solaris ABI Tools.
The Solaris ABI is the set of supported run-time interfaces that are available for an application to use with the Solaris operating environment. The most important components of the ABI are in the following list:
The interfaces provided by the Solaris system libraries, which are documented in section 3 of the man pages
The interfaces provided by the Solaris kernel system calls, which are documented in section 2 of the man pages
The locations and formats of various system files and directories, which are documented in section 4 of the man pages
The input and output syntax and semantics of Solaris utilities, which are documented in section 1 of the man pages
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 environment 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 three ABI versions. The three versions are 32-bit SPARC, 64-bit SPARC, or 32-bit Intel. While the ABI is very similar to the API, the source compilation process introduces several important differences:
Compiler directives such as #define can alter or replace source-level constructs. The resulting binary might lack a symbol present in the source or include a symbol not present in the source.
The compiler might generate processor-specific symbols, such as arithmetic instructions, which augment or replace source constructs.
The compiler's binary layout might be specific to that compiler and the versions of the source language which the compiler accepts. In such cases, identical code compiled with different compilers might produce incompatible binaries.
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.
Although the Solaris ABI is documented in the Solaris man pages, the 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.
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.
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.
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:
SYSVABI, for interfaces defined by the System V ABI definition
SISCD, for interfaces defined by the SPARC International SPARC Compliance Definition
SUNW, for interfaces defined by Sun Microsystems
These public, named sets are numbered with a major.minor numbering scheme. The minor number is incremented as new symbols are added to the set. The major number is incremented in the rare instance when an existing symbol changes incompatibly. When an existing symbol changes incompatibly, the version number in the library's file name is also incremented.
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.
The Solaris operating environment 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 private interface usage. The appcert utility then 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:
Use appcert on the current Solaris release for triage. This identifies which binaries use problematic interfaces and which do not.
Use apptrace on the target Solaris release for verification. This verifies whether interface compatibility problems exist by enabling dynamic observation of those interfaces as they are used.
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 Intel platforms. The utility can check interface usage for both SPARC and Intel 32-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.
The appcert utility examines your applications for:
Private symbol usage
Static linking
Unbound symbols
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.
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.
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.
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. Static-linking checks are not performed by appcert when appcert is checking 64–bit applications.
The appcert utility cannot examine:
Object files that are completely or partially statically linked. A completely statically linked object is reported as unstable.
Executable files that do not have the execute permission set. The appcert utility skips such executables. Shared objects without the execute permission set are examined normally.
Object files whose user ID is set to root.
Non-ELF executables, such as shell scripts.
Solaris interfaces in languages other than C. The code need not be in C, but the call to the Solaris library must be.
To check your application with appcert, type:
appcert object|directory |
replacing object|directory with either:
The complete list of objects you want appcert to examine
The complete list of directories that contain such objects
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.
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.
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.
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.
Prints usage information for appcert.
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.
By default, appcert follows symbolic links when appcert searches directories for binaries to check. The -n switch disables this behavior.
Appends the Solaris library directories /usr/openwin/lib and /usr/dt/lib to LD_LIBRARY_PATH.
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.
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 10–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.
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:
Contains the mapping between checked binaries and the subdirectory in which appcert output specific to that binary is located.
Contains a copy of the rollup report that is displayed on stdout when appcert is run.
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 contains metacharacters
File does not have the execute bit set
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.
No potential sources of binary instability were found by appcert.
The appcert utility did not run successfully.
Some of the objects checked by appcert have potential binary stability problems.
The appcert utility did not find any binary objects to check.
Private Symbol Use – An application that depends on private symbols might not run on a Solaris release different from the one in which it was developed. This phenomenon occurs because private symbols that occur in a given Solaris release might behave differently or not be present in another release. If appcert reports private symbol usage in your application, rewrite the application to avoid the use of private symbols.
Demoted Symbols – Demoted symbols are functions or data variables in a Solaris library that have been removed or have been scoped locally in a later Solaris release. An application that directly calls such a symbol fails to run on a release whose libraries do not export that symbol.
Unbound Symbols – Unbound symbols are library symbols that are referenced by the application that the dynamic linker was unable to resolve when called by appcert. While unbound symbols are not always an indicator of poor binary stability, unbound symbols might indicate a more serious problem, such as dependencies on demoted symbols.
Obsolete Library – An obsolete library might be removed from the Solaris operating environment in a future release. The appcert utility flags any use of such a library. Applications that depend on such a library might not function in a future release that does not feature the library. To avoid this problem, do not use interfaces from obsolete libraries.
Use of sys_errlist or sys_nerr – The use of the sys_errlist and sys_nerr symbols might degrade binary stability. A reference might be made past the end of the sys_errlist array. To avoid this risk, use strerror instead.
Use of strong and weak symbols – The strong symbols that are associated with weak symbols are reserved as private because their behavior might change in future Solaris releases. Applications should only directly reference weak symbols. An example of a strong symbol is _socket, which is associated with the weak symbol socket.
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 Intel platforms. apptrace can trace interface calls for both SPARC and Intel 32-bit interfaces, as well as the 64-bit interfaces on SPARC. As with appcert, apptrace only examines C language interfaces.
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.
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.
The following examples contain sample apptrace output from tracing a simple one-binary application, ls.
% 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 name of the call
The library the call is in
The arguments and return values of the call
% 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.
% 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.