TOC PREV NEXT INDEX

LynxOS User's Guide


Shared Libraries

Users may have an application that relies on shared libraries. Or perhaps users are considering using shared libraries for an embedded application.

This chapter describes shared libraries and explains whether they should be used, how they are used, and how they are built.

Overview

A shared library is a collection of routines, organized so that code can be shared among processes; its primary goal is to reduce system and disk memory usage.

LynxOS supports tools to create and use C and C++ dynamic ELF shared libraries on all of its supported platforms.

Shared library development is supported by all native and cross development tools.

The term shared library is not entirely accurate when used with the ELF/SVR4 implementation. The correct term is shared object, because shared libraries are not archive (ar) libraries. They have more in common with partially linked (ld -r) object files. Shared objects can be identified by the .so suffix. This document, however, uses the term shared library when referring to shared objects.

The main difference in the treatment of LynxOS shared libraries and those on other systems at load-time, is that lazy linking is not supported for LynxOS shared libraries. The use of lazy linking makes application startup faster, but causes problems in real-time systems with deterministic behavior requirements.

Compatibility between different revisions of a library is easy to maintain, as long as the libraries are functionally compatible; applications linked with the library do not need to be relinked in order to use the newer version.

No source code changes are necessary when making a shared library. Object files that are to be part of the shared library must be compiled as Position Independent Code (PIC) using the -fPIC -mshared -shared compiler flags.

The LynxOS implementation also includes dlopen()/libdl.so support. This library provides functions that allow users to open, close, and find symbols in an arbitrary shared library, even if the application in use has not been linked with the library. For more details, see the dlopen() man page.

Creating Shared Libraries by Default

Some ELF-based systems, such as Linux, create shared objects by default, and make it necessary to provide a special option to the linker to produce statically linked objects. LynxOS takes the reverse approach. In LynxOS 4.0, the linker produces statically linked objects by default. To produce executables that link with shared objects at run-time, you must link with the option -mshared.

Single/Multithreaded Applications and Shared Libraries

When an application links with a shared object, it is important to note if the application uses single-threaded processes or mulithreaded processes. Processes that create multiple threads (using the system call pthread_create) should only call thread-safe functions. Thread-safe functions are coded in such a way that they work correctly even when they are concurrently executed by more than one thread.

On the other hand, processes that never create new threads can call non-thread safe functions without difficulty. Since non-thread safe functions may be faster than their thread-safe equivalents, users may prefer to use non-thread safe functions whenever possible. The usual practice is to keep thread-safe functions and non-thread safe functions in different libraries. LynxOS follows this practice with the libraries that it provides, and users should consider following this practice with their own libraries.

The libraries provided by LynxOS are kept in directories that distinguish thread-safe from non-thread safe libraries. The following table details the library types and locations:

LynxOS Library Types & Directories  
Library Type
Directory
Thread-safe shared libraries
/lib/thread/shlib
Non-thread safe shared libraries
/lib/shlib
Thread-safe static libraries
/lib/thread
Non-thread safe static libraries
/lib

Effects of Using Shared Libraries

In essence, when users modify the contents of a shared library, they modify all the programs that rely on it, unless they never deploy the new library, or only deploy it selectively. Using a shared library instead of an ordinary non-shared archive library may affect the following:

System Memory Usage

System memory usage is different because multiple processes can share the library code. The amount of system memory saved (or lost) by using a shared library depends on three factors:

(See "Determining the Use of Shared Libraries" for additional details and several examples).

The following relationships are true in general:

Routines and data items in the shared library that are rarely used waste system memory simply by their lack of use as follows:
- A rarely used routine wastes memory because the entire shared library resides in memory whenever it is used, regardless of which subset of routines from the library are actually linked.
- A rarely used data item wastes system memory equal to its size multiplied by the number of processes currently using the shared library. This is because each process has its own copy of the data.
Multiple concurrent executions of the same program use the same amount of text memory as does a single execution. The operating system ensures that the executing text segments of the same program are shared.

Disk Space Usage

Disk space usage is typically lower because a program linked with a shared library is almost always smaller than the same program linked with an equivalent archive library. The shared library itself is not a factor in disk space usage because it is comparable in size to an equivalent archive library.

Consider this simple "hello world" program:

#include <stdio.h>
main()
{
printf("Hello, world!\n");
}

The printf() function is included in the C library. If the program is linked to the static C library, the resulting program executable file is close to 36 kilobytes:

# gcc -o hellostatic hello.c
# ls -l hellostatic
 -rwxr-xr-x 1 root 36014 Jan 24 01:01 hellostatic*

If the same program is linked with the shared C library, then it is only about 5 kilobytes:

# gcc -mshared -o hellodynamic hello.c
# ls -l hellodynamic
 -rwxr-xr-x 1 root 5133 Jan 24 01:02 hellodynamic*

In many cases, using shared libraries provides a considerable difference in size compared to static libraries.

Code Maintenance

Maintaining programs that use a shared library is somewhat easier than maintaining programs that use an archive library because if an error in the library is fixed, only the shared library needs to be remade. The programs do not need to be re-linked with the library because the operating system maps the shared library code into their address spaces at run time. In effect, modifying the contents of a shared library modifies all of the programs that use it.

Note: The above statements hold true only when the change to the shared library is backward compatible. Compatibility problems can be avoided as long as the shared library can be built using the guidelines described later in this chapter. For bug-fixes, compatibility is often automatic. For changes that are broader in scope, adherence to the guidelines may become challenging or impractical.

Determining the Use of Shared Libraries

In order to decide whether or not to use shared libraries, users must take the following factors into consideration:

If a target system is running a single application, multiple instances of a single application, or multiple different applications (not concurrently), the using of shared libraries will probably increase memory and disk space usage. If, however, the target system is running many different applications at the same time (and they use large portions of the same shared libraries), there may be significant reductions in memory and disk space usage.

For comparison purposes, the space requirements of three sets of applications are explored in three pairs of tables that follow. All applications in a given set require a common library.

In the tables "Space Requirements for a 1 MB Library (X) Used by 6 Applications" "Space Requirements with a 1 MB Library (Y) Used by 6 Applications" and "Space Requirements with a 1 MB Library (Z) that Includes Data" space requirements are shown when the associated library is implemented as a non-shared library.

In the tables "Space Requirements for 1 MB Shared Library (X) Used by 6 Applications" "Space Requirements with a 1 MB Shared Library Used by 6 Applications" and "Space Requirements with a 1 MB Library (Z) that Includes Data" space requirements are shown when the associated library is implemented as a shared library.

Example 1

Each of the applications shown in the next two tables uses half of a 1 MB library, but uses a different mixture of the library's routines. Where these applications do not use a shared library, they require 3 MB of RAM and disk space. When they employ a shared library, they require only 1 MB of RAM and disk space.

Space Requirements for a 1 MB Library (X) Used by 6 Applications  
Library X Usage
· library text used by application
Space Requirements for Library X on Target
text
RAM
Disk
Static Library X
0
0
Application A
·
·
·
·
·
.5 MB
.5 MB
Application B
·
·
·
·
·
.5 MB
.5 MB
Application C
·
·
·
·
·
.5 MB
.5 MB
Application D
·
·
·
·
·
.5 MB
.5 MB
Application E
·
·
·
·
·
.5 MB
.5 MB
Application F
·
·
·
·
·
.5 MB
.5 MB
TOTALS
3 MB
3 MB

Space Requirements for 1 MB Shared Library (X) Used by 6 Applications  
Library X Usage
· library X deploys element
+ library text used by application
Space Requirements for Library X on Target
text
RAM
Disk
Shared Library X
·
·
·
·
·
·
·
·
·
·
1 MB
1 MB
Application A
+
+
+
+
+
0
0
Application B
+
+
+
+
+
0
0
Application C
+
+
+
+
+
0
0
Application D
+
+
+
+
+
0
0
Application E
+
+
+
+
+
0
0
Application F
+
+
+
+
+
0
0
TOTALS
1 MB
1 MB

Example 2

Each application shown in the next two tables uses 10% of a 1 MB shared library. Each application uses a different portion of the library, all mutually disjoint. Where these applications do not use a shared library, they require 0.6 MB of RAM and disk space (with all six applications running). When using a shared library, they require 1 MB of RAM and disk space.

This is a worst-case scenario; it is not possible to save any space by using a shared library for this mix of applications. This example is for illustration purposes only. It is unlikely that a group of applications would use completely disjoint sets of library routines.

Note: If the unused routines were removed from the shared library, the memory usage for both shared and non-shared libraries would be the same.

Space Requirements with a 1 MB Library (Y) Used by 6
Applications  
Library Y Usage
· library text used by application
Space Requirements for Library Y on Target
text
RAM
Disk
Static Library Y
0
0
Application A
·
.1 MB
.1 MB
Application B
·
.1 MB
.1 MB
Application C
·
.1 MB
.1 MB
Application D
·
.1 MB
.1 MB
Application E
·
.1 MB
.1 MB
Application F
·
.1 MB
.1 MB
TOTALS
.6 MB
.6 MB

Space Requirements with a 1 MB Shared Library Used by 6 Applications  
Library Y Usage
· library X deploys element
+ library text used by application
Space Requirements for Library Y on Target
text
RAM
Disk
Shared Library Y
·
·
·
·
·
·
·
·
·
·
1 MB
1 MB
Application A
+
0
0
Application B
+
0
0
Application C
+
0
0
Application D
+
0
0
Application E
+
0
0
Application F
+
0
0
TOTALS
1 MB
1 MB

The preceding examples assume that the library contains no data or bss sections. While this is the best way to build a shared library, it is not always possible to do so.

Due to the inability of applications to share data, the data and bss sections of a shared library are not treated the same way as the text section: Every process gets a full copy of the data and bss sections whether it uses all of it or not.

Example 3

In the applications shown in the next two tables, the issues surrounding bss and data section utilization in a shared library are illustrated. Both shared and non-shared versions of the same library contain 0.5 MB of text, 0.3 MB of data, and 0.2 MB of bss. Each application uses forty percent of the text and forty percent of the data and bss sections.

In the case of the non-shared library (the table below), memory usage is 2.4 MB and disk usage is 1.9 MB. In the case of the shared library (the second table), the memory usage has increased to 3.5 MB, while the disk usage has dropped to 0.8 MB.

Space Requirements with a 1 MB Library (Z) that Includes Data
Type
Library Z Usage
· library text/data/bss used by application
Space Requirements for Library Z on Target
text
data
bss
RAM
Disk
Static Library Z
0
0
Application A
·
·
·
·
.4 MB
.4 MB
Application B
·
·
·
·
.4 MB
.4 MB
Application C
·
·
·
·
.4 MB
.3 MB
Application D
·
·
·
·
.4 MB
.2 MB
Application E
·
·
·
·
.4 MB
.3 MB
Application F
·
·
·
·
.4 MB
.3 MB
TOTALS
2.4 MB
1.9 MB

Space Requirements with a 1 MB Library (Z) that Includes Data  
Type
Library Z Usage
· library text/data/bss
+ library data/bss used by application
- library data/bss allocated but not used by application
* library text used by application
Space Requirements for Library Z on Target
text
data
bss
RAM
Disk
Shared Library Z
·
·
·
·
·
· 1
· 1
· 1
· 2
· 2
.5 MB
.8 MB
Application A
*
*
+
+
-
-
-
.5 MB
0
Application B
*
*
-
+
+
-
-
.5 MB
0
Application C
*
*
-
-
+
+
-
.5 MB
0
Application D
*
*
-
-
-
+
+
.5 MB
0
Application E
*
*
+
-
-
-
+
.5 MB
0
Application F
*
*
-
+
-
+
-
.5 MB
0
TOTALS
3.5 MB
.8 MB

1. Shared library occupies disk space only.
2. Shared library bss does not occupy disk space.

As the preceding examples show, a well designed shared library may provide a significant savings in the memory space needed. In an embedded system with tight memory constraints, these savings could mean the following:

The preceding examples also show that a well designed shared library may provide a significant savings in the disk, flash, or ROM space needed. In an embedded system with tight memory constraints, these savings could mean the following:

Choosing Shared Library Contents

Choosing the contents of a shared library involves more considerations than choosing the contents of an ordinary non-shared archive library. It is perhaps the most important factor determining whether or not the shared library decreases system memory usage.

When choosing routines for the shared library, use the following guidelines:

How to Save Space

In order to minimize the space used by a shared library, global data should be minimized. One technique for doing this is to use local (stack) variables instead of global variables wherever possible. Another technique is to allocate buffers dynamically (for example, with malloc()) instead of statically. These are effectively identical methods of reducing the data section of the shared library. This is important because each process using the shared library gets its own separate copy of the data section.

The above space-saving techniques have the added benefit of making maintenance of the shared library easier by reducing the number of external symbols in the library. The number of external symbols can be further reduced by explicitly using static variables wherever possible.

Updating Shared Libraries

The way that shared libraries are designed allows for an application to be updated (by rebuilding and replacing the target shared library) without the need to relink the application. This means that bug-fixes or other library changes can be made in the field without replacing the entire application. This feature of shared libraries may require extra work on the part of the library designer to ensure that a new library is compatible with previous versions.

To support compatibility between newer and older versions of the library, shared libraries use a global offset table to access function calls. This adds one or more additional instructions to the normal function call process. These extra instructions produce slightly longer execution times for library calls. In general, the performance decrease is not measurable.

Libraries Provided

LynxOS provides the following system shared libraries. Single-threaded versions are located in the /lib/shlib directory; multi-threaded versions are located in the /lib/thread/shlib directory. The X and Motif shared libraries are located in /usr/lib/shlib.

Shared Libraries in the LynxOS Distribution  
Library
Description
Availability
Single-threaded
Multi-threaded
libc
Standard C Library
·
·
libm
Standard Math Library
·
·
libdl
Dynamic Linker Library
·
·
libalias
Packet aliasing library for network address translation and masquerading.
·
·
libcurses
CRT Screen Handling Library
·
·
libbsd
Network Address Resolution Library
·
·
libmsng
LnyxOS Messenger Library
·
·
libgcc
GNU C library
·
·
libstdc++
GNU C++ Support Library
·
·
libXt1
X Windows Support Library
·

libX111
X Windows Toolkit Library
·

libICE1
X Communications Library
·

libSM1
X Sessions Management Library
·

libXext1
X Extensions Library
·

libPEX5
Phigs (Programmer's Hierarchical Interactive Graphics System) Extension to X
·

libXIE
XIE server extension library
·

libXaw
X Athena widget library
·

libXi
X input extension
·

libXmu
X miscellaneous utilities
·

libXp
X print library
·

libXpm
X pixmap library
·

libxtst
X testing extension
·

libXm1
Motif Library
·

1
x86 and PowerPC only.

Creating Shared Libraries

Shared libraries are easy to create. The following example shows how to create a shared library named myshared.so from the file myshared.c:

# gcc -shared -mshared -fPIC -o myshared.so myshared.c

The options in this line are:

-shared
The object file of this command will be a shared object.
-mshared
Any references to other library functions will be to those found in the LynxOS-provided shared libraries.
-fPIC
The text created will be position independent, i.e., it can be linked to shared objects.
-o
The following token, myshared.so, is the name of the created shared library.

The option -shared is used to create a shared object.

The option -fPIC causes the compiler to produce position-independent code. In other words, it produces text that does not contain pointers in its read-only area. Instead, the text works with a jump table so that it can be dynamically linked to functions in the shared libraries, whose locations in memory are not known until run-time. At run-time, the addresses of those functions are determined and stored in the jump table. When the function is called, the call address is fetched from the jump table.

Linking to a Shared Library

In order to link with one of the shared libraries supplied by LynxOS, users must tell the compiler/linker to look for libraries in one of the shared library directories. There are two ways to do this: by specifying the -mshared flag; or by specifying the directory to search using the -L directory flag.

The following commands are for single-threaded applications. Either command can be used; both are equivalent:

$ gcc -o output_file source_files -mshared
$ gcc -o output_file source_files -L /lib/shlib

The -mshared option links the executable with the dynamic shared libraries in /lib/shlib/.

The following commands are for multi-threaded applications (i.e., if the program calls pthread_create). Either command can be used; both are equivalent. The user needs to choose thread-safe binary functions. Use the option -mthreads to the gcc command.

$ gcc -o output_file source_files -mthreads -mshared
$ gcc -o output_file source_files -mthreads -L \ /lib/thread/shlib

The use of -mshared and -mthreads together selects the dynamic shared libraries that are thread-safe, found in /lib/thread/shlib.



LynuxWorks, Inc.
855 Branham Lane East
San Jose, CA 95138
http://www.lynuxworks.com
1.800.255.5969
TOC PREV NEXT INDEX