Guides and Whitepapers Dynamic linking issues
Tuesday, 13 July 2010 19:00

Dynamic linking issues

Written by  Carl Shaw
Rate this item
(0 votes)

What really happens when you start a program in a Linux based system and why does it sometimes take so long to start up? This whitepaper looks at the role of the dynamic linker, the problems associated with it and what MathEmbedded Consulting can do to improve system start-up time.

Introduction

Many embedded programmers find the role of the dynamic linker difficult to understand. Even the name is confusing - a linker is usually a compile-time tool that connects program object files together to produce a final executable program. However, the dynamic linker is a run-time program and vital to the operation of a Linux system.

The dynamic linker is actually a program that loads and binds all of the dynamic dependencies of a program before starting to execute that program. Given a particular program, the dynamic linker will find what dynamic libraries a program requires, what libraries those libraries require (and so on), then it will load all those libraries and make sure that all references to functions then correctly point to the right place. For example, even the most basic "hello world" program will usually require the C library to display the output and so the dynamic linker will load the C library before loading the hello world program and will make sure that any calls to printf() go to the right code.

In fact, not only is it called at program initialisation time, but it may also be called throughout the lifetime of a program to resolve functions (this is known as Lazy Binding and occurs in glibc and uclibc).

The Linux dynamic linker

The Linux dynamic linker is found in /lib and is usually called something like ld-<version>.so, ld-linux.so.2 (32 bit systems) or ld-linux-x86-64.so.2 (64 bit systems). Although found in /lib, it can actually be executed. For example, try running:

/lib/ld-linux.so.2

(or whatever the linker is called on your system). For a GNU glibc system, the following is displayed:

Usage: ld.so [OPTION]... EXECUTABLE-FILE [ARGS-FOR-PROGRAM...]
You have invoked `ld.so', the helper program for shared library executables.
This program usually lives in the file `/lib/ld.so', and special directives
in executable files using ELF shared libraries tell the system's program
loader to load the helper program from this file. This helper program loads
the shared libraries needed by the program executable, prepares the program
to run, and runs it. You may invoke this helper program directly from the
command line to load and run an ELF executable file; this is like executing
that file itself, but always uses this helper program from the file you
specified, instead of the helper program file specified in the executable
file you run. This is mostly of use for maintainers to test new versions
of this helper program; chances are you did not intend to run this program.

--list list all dependencies and how they are resolved
--verify verify that given object really is a dynamically linked
object we can handle
--library-path PATH use given PATH instead of content of the environment
variable LD_LIBRARY_PATH
--inhibit-rpath LIST ignore RUNPATH and RPATH information in object names
in LIST
--audit LIST use objects named in LIST as auditors

As you can see, the dynamic linker can be used to perform the same function as the Linux "ldd" utility simply by using it like /lib/ld-linux.so.2 --list <program>

Why use dynamically linked programs?

Embedded systems have traditionally contained statically linked executables. These are self-contained programs that don't need any external set-up before running. However, in all but the most heavily embedded Linux systems now, using static executables will result in an unacceptably large root filesystem.

The advantages of using dynamically linked programs are:

  • Dynamic libraries can be shared between different programs saving both filesystem space and run-time memory;
  • A single library can have a bug fixed and can then be replaced (e.g. through an over-the-internet download) without replacing the whole program;
  • It allows programs and libraries with different license constraints to be used together. For example a proprietary program can be linked with a LGPL library;
  • Dynamic libraries can be loaded and unloaded at run-time to provide "plug-in" features.

The disadvantages of using dynamically linked programs are:

  • They take longer to start up as the libraries need to be loaded and all function and symbol references in the program need to be resolved;
  • Having dynamically loaded components makes securing the system more difficult;
  • When creating a filesystem care has to be taken to include the required shared libraries along with the programs.

How does a program run?

The process begins when a user-space program such as a shell tells the kernel to start running a program by using the exec system call. The kernel then checks the file it is passed to see what format it is in. Linux programs and libraries are normally stored encapsulated in standard Executable and Linkable File (ELF) format which is a standard way of representing a program (we will deal with this in another whitepaper).

ELF files contain not only the program machine code and initialised data, but they also contain information on what the program needs to run. The first important thing they contain is the "INTERP" program header which points to a string which contains the name of the dynamic loader program to run.

You can see this by running the readelf utility with the -l option on a program and look for the line beginning INTERP:

readelf -l /bin/bash

Elf file type is EXEC (Executable file)
Entry point 0x41ed50
There are 9 program headers, starting at offset 64

Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
PHDR 0x0000000000000040 0x0000000000400040 0x0000000000400040
0x00000000000001f8 0x00000000000001f8 R E 8
INTERP 0x0000000000000238 0x0000000000400238 0x0000000000400238
0x000000000000001c 0x000000000000001c R 1
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
...

You can see that on my system the ELF file is requesting the dynamic linker /lib64/ld-linux-x86-64.so.2

The kernel, once it identifies the file as an ELF file, loads this dynamic linker and starts it executing.

The dynamic linker then checks the program ELF file for some further information. It looks at information in the "dynamic section" of the ELF file and for dynamic symbols of the type DT_NEEDED. readelf -d can be used to view these:

readelf -d /bin/bash

Dynamic section at offset 0xd6e20 contains 23 entries:
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [libncurses.so.5]
0x0000000000000001 (NEEDED) Shared library: [libdl.so.2]
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
...

As you can see /bin/bash requires libncurses.so.5, libdl.so.2 and libc.so.6. The dynamic linker then starts the process of loading these libraries and making sure that the functions required by the program are available.

How dynamic linker loads and links...

System performance issues

boot speed GNU_HASH prelinking

Security problems

LD_PRELOAD single ELF namespace address space randomisation

MathEmbedded.com services

Last modified on Tuesday, 13 July 2010 20:01

Latest from Carl Shaw

Leave a comment

Make sure you enter the (*) required information where indicated.
Basic HTML code is allowed.

Login Form