Using and Redistributing Solaris Studio Libraries in an Application


Steve Clamage, Darryl Gove, Oracle/Solaris Studio Engineering
revised March 2011

The Oracle Solaris Studio software suite provides a number of libraries that can be incorporated into an application to provide functionality and reduce development time. These libraries are redistributable, meaning that they can be freely distributed along with the applications that depend on them. The full list of Solaris Studio 12.2 redistributable libraries and files can be found at the Oracle Technical Network Solaris Studio web site.

This article presents the best practices for redistributing these libraries and for maintaining applications that depend on them.

The Problem

There are two ways that an application can be linked to a library:

  • Static linking combines code from the library with the executable forming a single file. The advantage is that the library version used at runtime can be known explicitly, and cannot be changed. The disadvantage is that if the library has to be modified, for example to fix a bug, then the whole application has to be relinked and a new version provided.

  • Dynamic linking assumes the library is external to the application and will be loaded by the application at runtime. The advantages here are that the library can be updated or replaced without having to recompile or relink the application, and if multiple processes use the same library, only a single copy will be shared between them, thereby reducing the physical memory used. But there are some disadvantages: if multiple copies of a library resides on a system, it is possible for an application to choose the wrong library to load; and should a library used by an application be updated while the application is running, the behaviour could be unpredictable.

Dynamic linking is preferred, especially for the libraries supplied by the compiler, as well as for user libraries that are required by the application. This enables better management of updates to the library code.

Example

This code example uses the vector class from the C++ Standard Library

Example 1 — Example of Code Using the C++ Standard Library

#include <vector>

int main()
{
   std::vector<int> v;
   v.push_back(10);
   return(0);
}

By default the compiler will use the C++ Standard Library, libCstd, that is provided as part of Solaris. The library is installed in /usr/lib as part of the OS, and hence does not need to be packaged separately. Use the ldd utility to determine which libraries the application will link to, as shown below.

Example 2 — Using The Default Standard Library

% CC v.cc 
% ldd a.out   
        libCstd.so.1 =>  /usr/lib/libCstd.so.1
        libCrun.so.1 =>  /usr/lib/libCrun.so.1
        libm.so.1 =>     /usr/lib/libm.so.1
        libc.so.1 =>     /usr/lib/libc.so.1
        libdl.so.1 =>    /usr/lib/libdl.so.1
        /usr/lib/cpu/sparcv8plus/libCstd_isa.so.1
        /usr/platform/SUNW,Sun-Fire-880/lib/libc_psr.so.1

The developer can decide to use the alternative stlport4 library, which provides better standards conformance, and often better performance, than the default library. This library is not shipped as part of Oracle Solaris, but is part of the Solaris Studio distribution. The next example shows the same code compiled with the flag -library=stlport4, which tells the compiler to use the stlport4 library instead of the default libCstd. The output from ldd shows that the application links in the library located in the directory that is part of the Solaris Studio distribution.

Example 3 — Using stlport4

% CC -library=stlport4 v.cc

% ldd a.out  
        libstlport.so.1 => /opt/SUNWspro/lib/stlport4/libstlport.so.1
        librt.so.1 =>    /usr/lib/librt.so.1
        libCrun.so.1 =>  /usr/lib/libCrun.so.1
        libm.so.1 =>     /usr/lib/libm.so.1
        libc.so.1 =>     /usr/lib/libc.so.1
        libaio.so.1 =>   /usr/lib/libaio.so.1
        libmd5.so.1 =>   /usr/lib/libmd5.so.1
        libdl.so.1 =>    /usr/lib/libdl.so.1
        /usr/platform/SUNW,Sun-Fire-880/lib/libc_psr.so.1
        /usr/platform/SUNW,Sun-Fire-880/lib/libmd5_psr.so.1

Running this program on another system without the Solaris Studio software installed, the linker will not be able to locate the file libstlport.so.1. This situation is shown in the next example with the output from ldd on a system without Solaris Studio installed.

Example 4 — Example of being unable to locate a library

% ldd a.out 
        libstlport.so.1 =>       (file not found)
        librt.so.1 =>    /usr/lib/librt.so.1
        libCrun.so.1 =>  /usr/lib/libCrun.so.1
        libm.so.1 =>     /usr/lib/libm.so.1
        libc.so.1 =>     /usr/lib/libc.so.1
        libaio.so.1 =>   /usr/lib/libaio.so.1
        libmd.so.1 =>    /usr/lib/libmd.so.1
        libm.so.2 =>     /usr/lib/libm.so.2
        /platform/SUNW,Sun-Fire-T200/lib/libc_psr.so.1
        /platform/SUNW,Sun-Fire-T200/lib/libmd_psr.so.1

One workaround for this situation that is too frequently adopted is to set the environment variable LD_LIBRARY_PATH to point to the directory containing the missing library. Although this does work, it is not a recommended fix because it is fragile and requires the user's environment to be correctly set.

For example, the library libstlport.so.1 might have been copied into the current directory and LD_LIBRARY_PATH set to "." (dot). This is shown in the next example. But if the application is invoked from any directory other than the current directory, the application will not be able to locate the stlport4 library.

Example 5 — The workaround of setting LD_LIBRARY_PATH is not recommended

% cd test 
% export LD_LIBRARY_PATH=.  
% ldd a.out 
        libstlport.so.1 =>   ./libstlport.so.1
        librt.so.1 =>    /usr/lib/librt.so.1
        libCrun.so.1 =>  /usr/lib/libCrun.so.1
        libm.so.1 =>     /usr/lib/libm.so.1
        libc.so.1 =>     /usr/lib/libc.so.1
        libaio.so.1 =>   /usr/lib/libaio.so.1
        libmd.so.1 =>    /usr/lib/libmd.so.1
        libm.so.2 =>     /usr/lib/libm.so.2
        /platform/SUNW,Sun-Fire-T200/lib/libc_psr.so.1
        /platform/SUNW,Sun-Fire-T200/lib/libmd_psr.so.1
% cd .. 
% ldd test/a.out   
        libstlport.so.1 =>       (file not found)
        librt.so.1 =>    /usr/lib/librt.so.1
        libCrun.so.1 =>  /usr/lib/libCrun.so.1
        libm.so.1 =>     /usr/lib/libm.so.1
        libc.so.1 =>     /usr/lib/libc.so.1
        libaio.so.1 =>   /usr/lib/libaio.so.1
        libmd.so.1 =>    /usr/lib/libmd.so.1
        libm.so.2 =>     /usr/lib/libm.so.2
        /platform/SUNW,Sun-Fire-T200/lib/libc_psr.so.1
        /platform/SUNW,Sun-Fire-T200/lib/libmd_psr.so.1

The LD_LIBRARY_PATH environment variable is useful for special testing, but is not a scalable or maintainable solution for deployed programs. Rod Evans provides a detailed discussion in his blog entry "LD_LIBRARY_PATH - just say no".

The Right Way to Distribute Shared Libraries

Avoid using the LD_LIBRARY_PATH environment variable by packaging the application binary along with any additional libraries in a directory structure as shown in the next example.

Example 6 — Suggested directory structure for application

/application
            /bin       Contains executables
            /lib       Contains necessary libraries

In the example, the stlport.so.1 library would be copied into the /lib subdirectory. The compiler flag -library=stlport4 will enable linking the stlport4 library rather than the default library at build time. Compiling with the -R dir option, the linker will locate the library in the application's /lib subdirectory at runtime.

Although you could specify an absolute directory path to search for the library, this would restrict the installation to one specific location in the file system, again requiring use of LD_LIBRARY_PATH as an ugly workaround. The better approach is to use the token $ORIGIN with the -R option to tell the application to look in a path relative to the location of the executable. The $ORIGIN token may need special treatment to avoid being interpreted by the shell. This is shown in the next example.

Example 7 — Specifying a relative runtime path for a library

% CC -library=stlport4 -R'$ORIGIN/../lib' v.cc

In a Makefile, an extra $ escape is needed to avoid $ORIGIN being interpreted as a Make variable. This is shown in the next example.

Example 8 — Specifying a relative runtime path in a Makefile

v: v.cc 
      CC -library=stlport4 -R \$$ORIGIN/../lib v.cc -o v

On the target machine this results in the application locating the library in the application's /lib directory, as shown in the next example.

Example 9 — Locating a library in a relative directory path

% ldd a.out 
        libstlport.so.1 => /export/home/test/bin/../lib/libstlport.so.1
        librt.so.1 =>    /usr/lib/librt.so.1
        libCrun.so.1 =>  /usr/lib/libCrun.so.1
        libm.so.1 =>     /usr/lib/libm.so.1
        libc.so.1 =>     /usr/lib/libc.so.1
        libaio.so.1 =>   /usr/lib/libaio.so.1
        libmd.so.1 =>    /usr/lib/libmd.so.1
        libm.so.2 =>     /usr/lib/libm.so.2
        /platform/SUNW,Sun-Fire-T200/lib/libc_psr.so.1
        /platform/SUNW,Sun-Fire-T200/lib/libmd_psr.so.1

Conclusions

Using the $ORIGIN token with the -R option to locate the libraries on a path relative to the executable is recommended for the following reasons:

  • The executable and libraries can be co-located, which ensures that the executable is distributed with, and uses the appropriate library version.

  • If the support libraries are updated, it is easy to copy over the new updated version to replace an earlier version.

  • Each library is used by one application (or one family of applications), so this version of the library can be updated without risk to other applications installed on the system.

  • The application and libraries can be installed anywhere and be expected to work without the user having to use workarounds like LD_LIBRARY_PATH.

  • The technique described here applies equally well to third-party shared libraries or libraries created as part of the application.

Authors

Steve Clamage has been at Sun since 1994, and is currently technical lead for the Oracle Solaris Studio C++ compiler. He has been chair of the ANSI C++ Committee since 1995.

Darryl Gove is a senior staff engineer in Solaris Studio Compiler Performance Engineering at Oracle, analyzing and optimizing the performance of applications on current and future UltraSPARC systems. Darryl is also the author of Solaris Application Programming (Prentice Hall 2008). Read Darryl's blog here.