SPRAB89A September 2011 – March 2014
Each executable that shares a library's code must allocate its own private copy of the library's data. Furthermore, each static link unit's own data (including the GOT) is addressed using DP-relative addressing, with offsets that are fixed at static link time. (On systems with MMU's, this is typically accomplished by using PC-relative addressing to achieve position-independent virtual offsets, and using address translation to instantiate multiple physical copies of the data segment at the same (virtual) address.) Systems without an MMU, like the C6000, typically rely on some form of a static base pointer of some kind (the DP) and offset addressing.
All addressing from a given static link unit is relative to its data segment and is therefore independent of any other static link unit. The result is a model where a given program, comprised of an executable and one or more (possibly shared) libraries, has multiple data segments, each having a different address on which DP-relative offsets are based. When control transfers from one module to another, the DP must be changed to the base address of the new module's data segment.
The general issues with this model, common to most static-base addressing schemes, are:
Various solutions have been adopted for other architectures, such as FDPIC, XFLAT, and DSBT. We have chosen to adopt the DSBT model as the best compromise between efficiency, compatibility, and flexibility.
When a call to an imported function is made, the callee is responsible for setting the DP to point to its data segment (more precisely, the underlying executable's private copy of the data segment for the module containing the callee), and for restoring it upon return.
Before we explain how the callee achieves this, consider two observations. First, each module has its own data segment(s), with its own base address, and if shared among multiple executables, each has a private copy of that segment, with a different base address. Furthermore, these addresses are dynamically determined. So obviously, much like addresses stored in the GOT, the base address cannot be absolute and therefore must be stored in the data segment.
Second, although the callee is responsible for changing the DP, upon entry to the callee the DP is still pointing to the caller's data segment. Thus the callee has only the context of the caller to somehow set up its own DP.
The solution is that the first few words of each data segment contain a copy of a table, called the Data Segment Base Table (DSBT), listing the base addresses for all the other segments of the other modules that comprise the program. Each shared library is assigned a unique index, starting with 1. Index 0 is reserved for the executable. The callee uses its assigned index to lookup its own base address in the caller's copy of the table, and assigns that value to the DP. Within the private data segments for a given executable and its shared libraries, each copy of the DSBT is identical, enabling any callee to use any caller's table to find its own base address.
The DSBT approach has the desirable characteristic that the penalty for dynamic linking is isolated to exported functions. There is no effect on the ABI for bare-metal programs that do not use dynamic linking, or for an executable without exported functions. By judiciously using toolchain-specific declaration constructs to explicitly identify externally-accessible functions (see Section 6.8.2), the programmer can minimize the overhead. In functions that do need to adjust the DP, the overhead is typically only 3 instructions.
The DSBT model's drawback is the requirement to coordinate assignment of library indexes and to enforce agreement on the maximum number of modules, which determines the size of the table in each data segment.
The DSBT is allocated by the static linker in the .dsbt section, and must be located at the base address of each module's first DP-relative segment so that the DP points to it. The dynamic linker initializes the table entries when the module is loaded.
An executable always accesses the table using index 0; library indexes start at 1, 2, or some other index as specified for a specific platform. A library's index may be assigned in one of two ways:
Each object's DSBT must be at least as large as the largest index assigned to any module that is dynamically loaded as part of the program. The dynamic linker is responsible for ensuring that all modules have a large enough DSBT; if not, it must fail to load the program. The size of the DSBT is specified at static link time (or to a static post-link tool) via a command line option or environment variable. Embedded systems generally require a small number of dynamic libraries; so a typical size for the DSBT is 5 or less.
The module's dynamic section contains C6000-specific tags that specify the size of its DSBT table, and its index if assigned. These are detailed in Section 14.4.2.