Dynamic Linker
**************

The dynamic linker is responsible for loading dynamically linked programs and their dependencies (in the form of shared objects).  The dynamic linker in theglibc also supports loading shared objects (such as plugins) later at run time.

Dynamic linkers are sometimes called dynamic loaders.

Dynamic Linker Invocation
=========================

When a dynamically linked program starts, the operating system automatically loads the dynamic linker along with the program. Theglibc also supports invoking the dynamic linker explicitly to launch a program.  This command uses the implied dynamic linker (also sometimes called the program interpreter):

     sh -c 'echo "Hello, world!"'

This command specifies the dynamic linker explicitly:

     ld.so /bin/sh -c 'echo "Hello, world!"'

Note that ld.so does not search the PATH environment variable, so the full file name of the executable needs to be specified.

The ld.so program supports various options.  Options start -- and need to come before the program that is being launched. Some of the supported options are listed below.

--list-diagnostics
     Print system diagnostic information in a machine-readable format. See Dynamic Linker Diagnostics.

Dynamic Linker Diagnostics
--------------------------

The ld.so --list-diagnostics produces machine-readable diagnostics output.  This output contains system data that affects the behavior of theglibc, and potentially application behavior as well.

The exact set of diagnostic items can change between releases of theglibc.  The output format itself is not expected to change radically.

The following table shows some example lines that can be written by the diagnostics command.

dl_pagesize=0x1000
     The system page size is 4096 bytes.

env[0x14]="LANG=en_US.UTF-8"
     This item indicates that the 21st environment variable at process startup contains a setting for LANG.

env_filtered[0x22]="DISPLAY"
     The 35th environment variable is DISPLAY.  Its value is not included in the output for privacy reasons because it is not recognized as harmless by the diagnostics code.

path.prefix="/usr"
     This means that theglibc was configured with --prefix=/usr.

path.system_dirs[0x0]="/lib64/"
path.system_dirs[0x1]="/usr/lib64/"
     The built-in dynamic linker search path contains two directories, /lib64 and /usr/lib64.

Dynamic Linker Diagnostics Format
.................................

As seen above, diagnostic lines assign values (integers or strings) to a sequence of labeled subscripts, separated by ..  Some subscripts have integer indices associated with them.  The subscript indices are not necessarily contiguous or small, so an associative array should be used to store them.  Currently, all integers fit into the 64-bit unsigned integer range.  Every access path to a value has a fixed type (string or integer) independent of subscript index values.  Likewise, whether a subscript is indexed does not depend on previous indices (but may depend on previous subscript labels).

A syntax description in ABNF (RFC 5234) follows.  Note that %x30-39 denotes the range of decimal digits.  Diagnostic output lines are expected to match the line production.

     HEXDIG = %x30-39 / %x61-6f ; lowercase a-f only
     ALPHA = %x41-5a / %x61-7a / %x7f ; letters and underscore
     ALPHA-NUMERIC = ALPHA / %x30-39 / "_"
     DQUOTE = %x22 ; "

     ; Numbers are always hexadecimal and use a 0x prefix.
     hex-value-prefix = %x30 %x78
     hex-value = hex-value-prefix 1*HEXDIG

     ; Strings use octal escape sequences and \\, \".
     string-char = %x20-21 / %x23-5c / %x5d-7e ; printable but not "\
     string-quoted-octal = %x30-33 2*2%x30-37
     string-quoted = "\" ("\" / DQUOTE / string-quoted-octal)
     string-value = DQUOTE *(string-char / string-quoted) DQUOTE

     value = hex-value / string-value

     label = ALPHA *ALPHA-NUMERIC
     index = "[" hex-value "]"
     subscript = label [index]

     line = subscript *("." subscript) "=" value

Dynamic Linker Diagnostics Values
.................................

As mentioned above, the set of diagnostics may change between theglibc releases.  Nevertheless, the following table documents a few common diagnostic items.  All numbers are in hexadecimal, with a 0x prefix.

dl_dst_lib=STRING
     The $LIB dynamic string token expands to STRING.

dl_hwcap=INTEGER
dl_hwcap2=INTEGER
     The HWCAP and HWCAP2 values, as returned for getauxval, and as used in other places depending on the architecture.

dl_pagesize=INTEGER
     The system page size is INTEGER bytes.

dl_platform=STRING
     The $PLATFORM dynamic string token expands to STRING.

dso.libc=STRING
     This is the soname of the shared libc object that is part of theglibc.  On most architectures, this is libc.so.6.

env[INDEX]=STRING
env_filtered[INDEX]=STRING
     An environment variable from the process environment.  The integer INDEX is the array index in the environment array.  Variables under env include the variable value after the = (assuming that it was present), variables under env_filtered do not.

path.prefix=STRING
     This indicates that theglibc was configured using --prefix=STRING.

path.sysconfdir=STRING
     Theglibc was configured (perhaps implicitly) with --sysconfdir=STRING (typically /etc).

path.system_dirs[INDEX]=STRING
     These items list the elements of the built-in array that describes the default library search path.  The value STRING is a directory file name with a trailing /.

path.rtld=STRING
     This string indicates the application binary interface (ABI) file name of the run-time dynamic linker.

version.release="stable"
version.release="development"
     The value "stable" indicates that this build of theglibc is from a release branch.  Releases labeled as "development" are unreleased development versions.

version.version="MAJOR.MINOR"
version.version="MAJOR.MINOR.9000"
     Theglibc version.  Development releases end in .9000.

auxv[INDEX].a_type=TYPE
auxv[INDEX].a_val=INTEGER
auxv[INDEX].a_val_string=STRING
     An entry in the auxiliary vector (specific to Linux).  The values TYPE (an integer) and INTEGER correspond to the members of struct auxv.  If the value is a string, a_val_string is used instead of a_val, so that values have consistent types.

     The AT_HWCAP and AT_HWCAP2 values in this output do not reflect adjustment by theglibc.

uname.sysname=STRING
uname.nodename=STRING
uname.release=STRING
uname.version=STRING
uname.machine=STRING
uname.domain=STRING
     These Linux-specific items show the values of struct utsname, as reported by the uname function.  See Platform Type.

aarch64.cpu_features....
     These items are specific to the AArch64 architectures.  They report data theglibc uses to activate conditionally supported features such as BTI and MTE, and to select alternative function implementations.

aarch64.processor[INDEX]....
     These are additional items for the AArch64 architecture and are described below.

aarch64.processor[INDEX].requested=KERNEL-CPU
     The kernel is told to run the subsequent probing on the CPU numbered KERNEL-CPU.  The values KERNEL-CPU and INDEX can be distinct if there are gaps in the process CPU affinity mask.  This line is not included if CPU affinity mask information is not available.

aarch64.processor[INDEX].observed=KERNEL-CPU
     This line reports the kernel CPU number KERNEL-CPU on which the probing code initially ran.  If the CPU number cannot be obtained, this line is not printed.

aarch64.processor[INDEX].observed_node=NODE
     This reports the observed NUMA node number, as reported by the getcpu system call.  If this information cannot be obtained, this line is not printed.

aarch64.processor[INDEX].midr_el1=VALUE
     The value of the midr_el1 system register on the processor INDEX.  This line is only printed if the kernel indicates that this system register is supported.

aarch64.processor[INDEX].dczid_el0=VALUE
     The value of the dczid_el0 system register on the processor INDEX.

x86.cpu_features....
     These items are specific to the i386 and x86-64 architectures.  They reflect supported CPU features and information on cache geometry, mostly collected using the CPUID instruction.

x86.processor[INDEX]....
     These are additional items for the i386 and x86-64 architectures, as described below.  They mostly contain raw data from the CPUID instruction.  The probes are performed for each active CPU for the ld.so process, and data for different probed CPUs receives a uniqe INDEX value.  Some CPUID data is expected to differ from CPU core to CPU core.  In some cases, CPUs are not correctly initialized and indicate the presence of different feature sets.

x86.processor[INDEX].requested=KERNEL-CPU
     The kernel is told to run the subsequent probing on the CPU numbered KERNEL-CPU.  The values KERNEL-CPU and INDEX can be distinct if there are gaps in the process CPU affinity mask.  This line is not included if CPU affinity mask information is not available.

x86.processor[INDEX].observed=KERNEL-CPU
     This line reports the kernel CPU number KERNEL-CPU on which the probing code initially ran.  If the CPU number cannot be obtained, this line is not printed.

x86.processor[INDEX].observed_node=NODE
     This reports the observed NUMA node number, as reported by the getcpu system call.  If this information cannot be obtained, this line is not printed.

x86.processor[INDEX].cpuid_leaves=COUNT
     This line indicates that COUNT distinct CPUID leaves were encountered.  (This reflects internal ld.so storage space, it does not directly correspond to CPUID enumeration ranges.)

x86.processor[INDEX].ecx_limit=VALUE
     The CPUID data extraction code uses a brute-force approach to enumerate subleaves (see the .subleaf_eax lines below).  The last %rcx value used in a CPUID query on this probed CPU was VALUE.

x86.processor[INDEX].cpuid.eax[QUERY_EAX].eax=EAX
x86.processor[INDEX].cpuid.eax[QUERY_EAX].ebx=EBX
x86.processor[INDEX].cpuid.eax[QUERY_EAX].ecx=ECX
x86.processor[INDEX].cpuid.eax[QUERY_EAX].edx=EDX
     These lines report the register contents after executing the CPUID instruction with %rax == QUERY_EAX and %rcx == 0 (a leaf).  For the first probed CPU (with a zero INDEX), only leaves with non-zero register contents are reported.  For subsequent CPUs, only leaves whose register contents differs from the previously probed CPUs (with INDEX one less) are reported.

     Basic and extended leaves are reported using the same syntax.  This means there is a large jump in QUERY_EAX for the first reported extended leaf.

x86.processor[INDEX].cpuid.subleaf_eax[QUERY_EAX].ecx[QUERY_ECX].eax=EAX
x86.processor[INDEX].cpuid.subleaf_eax[QUERY_EAX].ecx[QUERY_ECX].ebx=EBX
x86.processor[INDEX].cpuid.subleaf_eax[QUERY_EAX].ecx[QUERY_ECX].ecx=ECX
x86.processor[INDEX].cpuid.subleaf_eax[QUERY_EAX].ecx[QUERY_ECX].edx=EDX
     This is similar to the leaves above, but for a subleaf.  For subleaves, the CPUID instruction is executed with %rax == QUERY_EAX and %rcx == QUERY_ECX, so the result depends on both register values.  The same rules about filtering zero and identical results apply.

x86.processor[INDEX].cpuid.subleaf_eax[QUERY_EAX].ecx[QUERY_ECX].until_ecx=ECX_LIMIT
     Some CPUID results are the same regardless the QUERY_ECX value. If this situation is detected, a line with the .until_ecx selector ins included, and this indicates that the CPUID register contents is the same for %rcx values between QUERY_ECX and ECX_LIMIT (inclusive).

x86.processor[INDEX].cpuid.subleaf_eax[QUERY_EAX].ecx[QUERY_ECX].ecx_query_mask=0xff
     This line indicates that in an .until_ecx range, the CPUID instruction preserved the lowested 8 bits of the input %rcx in the output %rcx registers.  Otherwise, the subleaves in the range have identical values.  This special treatment is necessary to report compact range information in case such copying occurs (because the subleaves would otherwise be all different).

x86.processor[INDEX].xgetbv.ecx[QUERY_ECX]=RESULT
     This line shows the 64-bit RESULT value in the %rdx:%rax register pair after executing the XGETBV instruction with %rcx set to QUERY_ECX.  Zero values and values matching the previously probed CPU are omitted.  Nothing is printed if the system does not support the XGETBV instruction.

Dynamic Linker Introspection
============================

Theglibc provides various facilities for querying information from the dynamic linker.

 -- Data Type: struct link_map

     A link map is associated with the main executable and each shared object.  Some fields of the link map are accessible to applications and exposed through the struct link_map.  Applications must not modify the link map directly.

     Pointers to link maps can be obtained from the _r_debug variable, from the RTLD_DI_LINKMAP request for dlinfo, and from the _dl_find_object function.  See below for details.

     l_addr
          This field contains the load address of the object.  This is the offset that needs to be applied to unrelocated addresses in the object image (as stored on disk) to form an address that can be used at run time for accessing data or running code.  For position-dependent executables, the load address is typically zero, and no adjustment is required.  For position-independent objects, the l_addr field usually contains the address of the object's ELF header in the process image.  However, this correspondence is not guaranteed because the ELF header might not be mapped at all, and the ELF file as stored on disk might use zero as the lowest virtual address.  Due to the second variable, values of the l_addr field do not necessarily uniquely identify a shared object.

          On Linux, to obtain the lowest loaded address of the main program, use getauxval to obtain the AT_PHDR and AT_PHNUM values for the current process.  Alternatively, call dlinfo (_r_debug.r_map, &PHDR) to obtain the number of program headers, and the address of the program header array will be stored in PHDR (of type const ElfW(Phdr) *, as explained below). These values allow processing the array of program headers and the address information in the PT_LOAD entries among them. This works even when the program was started with an explicit loader invocation.

     l_name
          For a shared object, this field contains the file name that the theglibc dynamic loader used when opening the object.  This can be a relative path (relative to the current directory at process start, or if the object was loaded later, via dlopen or dlmopen).  Symbolic links are not necessarily resolved.

          For the main executable, l_name is "" (the empty string). (The main executable is not loaded by theglibc, so its file name is not available.)  On Linux, the main executable is available as /proc/self/exe (unless an explicit loader invocation was used to start the program).  The file name /proc/self/exe continues to resolve to the same file even if it is moved within or deleted from the file system.  Its current location can be read using readlink. See Symbolic Links.  (Although /proc/self/exe is not actually a symbol link, it is only presented as one.)  Note that /proc may not be mounted, in which case /proc/self/exe is not available.

          If an explicit loader invocation is used (such as ld.so /usr/bin/emacs), the /proc/self/exe approach does not work because the file name refers to the dynamic linker ld.so, and not the /usr/bin/emacs program.  An approximation to the executable path is still available in the INFO.dli_fname member after calling dladdr (_r_debug.r_map->l_ld, &INFO).  Note that this could be a relative path, and it is supplied by the process that created the current process, not the kernel, so it could be inaccurate.

     l_ld
          This is a pointer to the ELF dynamic segment, an array of tag/value pairs that provide various pieces of information that the dynamic linking process uses.  On most architectures, addresses in the dynamic segment are relocated at run time, but on some architectures and in some run-time configurations, it is necessary to add the l_addr field value to obtain a proper address.

     l_prev
     l_next
          These fields are used to maintain a double-linked linked list of all link maps within one dlmopen namespace.  Note that there is currently no thread-safe way to iterate over this list.  The callback-based dl_iterate_phdr interface can be used instead.

Portability note: It is not possible to create a struct link_map object and pass a pointer to a function that expects a struct link_map * argument.  Only link map pointers initially supplied by theglibc are permitted as arguments.  In current versions of theglibc, handles returned by dlopen and dlmopen are pointers to link maps.  However, this is not a portable assumption, and may even change in future versions of theglibc.  To obtain the link map associated with a handle, see dlinfo and RTLD_DI_LINKMAP below.  If a function accepts both dlopen/dlmopen handles and struct link_map pointers in its void * argument, that is documented explicitly.

Querying information for loaded objects
---------------------------------------

The dlinfo function provides access to internal information associated with dlopen/dlmopen handles and link maps.

 -- Function: int dlinfo (void *HANDLE, int REQUEST, void *ARG)
     GNU, dlfcn.h This function returns information about HANDLE in the memory location ARG, based on REQUEST.  The HANDLE argument must be a pointer returned by dlopen or dlmopen; it must not have been closed by dlclose.  Alternatively, HANDLE can be a struct link_map * value for a link map of an object that has not been closed.

     On success, dlinfo returns 0 for most request types; exceptions are noted below.  If there is an error, the function returns -1, and dlerror can be used to obtain a corresponding error message.

     The following operations are defined for use with REQUEST:

     RTLD_DI_LINKMAP
          The corresponding struct link_map pointer for HANDLE is written to *ARG.  The ARG argument must be the address of an object of type struct link_map *.

     RTLD_DI_LMID
          The namespace identifier of HANDLE is written to *ARG.  The ARG argument must be the address of an object of type Lmid_t.

     RTLD_DI_ORIGIN
          The value of the $ORIGIN dynamic string token for HANDLE is written to the character array starting at ARG as a null-terminated string.

          This request type should not be used because it is prone to buffer overflows.

     RTLD_DI_SERINFO
     RTLD_DI_SERINFOSIZE
          These requests can be used to obtain search path information for HANDLE.  For both requests, ARG must point to a Dl_serinfo object.  The RTLD_DI_SERINFOSIZE request must be made first; it updates the dls_size and dls_cnt members of the Dl_serinfo object.  The caller should then allocate memory to store at least dls_size bytes and pass that buffer to a RTLD_DI_SERINFO request.  This second request fills the dls_serpath array.  The number of array elements was returned in the dls_cnt member in the initial RTLD_DI_SERINFOSIZE request.  The caller is responsible for freeing the allocated buffer.

          This interface is prone to buffer overflows in multi-threaded processes because the required size can change between the RTLD_DI_SERINFOSIZE and RTLD_DI_SERINFO requests.

     RTLD_DI_TLS_DATA
          This request writes the address of the TLS block (in the current thread) for the shared object identified by HANDLE to *ARG. The argument ARG must be the address of an object of type void *.  A null pointer is written if the object does not have any associated TLS block.

     RTLD_DI_TLS_MODID
          This request writes the TLS module ID for the shared object HANDLE to *ARG.  The argument ARG must be the address of an object of type size_t.  The module ID is zero if the object does not have an associated TLS block.

     RTLD_DI_PHDR
          This request writes the address of the program header array to *ARG.  The argument ARG must be the address of an object of type const ElfW(Phdr) * (that is, const Elf32_Phdr * or const Elf64_Phdr *, as appropriate for the current architecture).  For this request, the value returned by dlinfo is the number of program headers in the program header array.

     The dlinfo function is a GNU extension.

The remainder of this section documents the _dl_find_object function and supporting types and constants.

 -- Data Type: struct dl_find_object
     GNU, dlfcn.h This structure contains information about a main program or loaded object.  The _dl_find_object function uses it to return result data to the caller.

     unsigned long long int dlfo_flags
          Bit zero signals if SFrame stack data is valid.  See DLFO_FLAG_SFRAME below.

     void *dlfo_map_start
          The start address of the inspected mapping.  This information comes from the program header, so it follows its convention, and the address is not necessarily page-aligned.

     void *dlfo_map_end
          The end address of the mapping.

     struct link_map *dlfo_link_map
          This member contains a pointer to the link map of the object.

     void *dlfo_eh_frame
          This member contains a pointer to the exception handling data of the object.  See DLFO_EH_SEGMENT_TYPE below.

     void *dlfo_sframe
          This member points to the SFrame stack trace data associated with the object.  It is valid only when DLFO_FLAG_SFRAME is set in dlfo_flags; otherwise, it may be null or undefined.

     This structure is a GNU extension.

 -- Macro: int DLFO_STRUCT_HAS_EH_DBASE
     GNU, dlfcn.h On most targets, this macro is defined as 0.  If it is defined to 1, struct dl_find_object contains an additional member dlfo_eh_dbase of type void *.  It is the base address for DW_EH_PE_datarel DWARF encodings to this location.

     This macro is a GNU extension.

 -- Macro: int DLFO_STRUCT_HAS_EH_COUNT
     GNU, dlfcn.h On most targets, this macro is defined as 0.  If it is defined to 1, struct dl_find_object contains an additional member dlfo_eh_count of type int.  It is the number of exception handling entries in the EH frame segment identified by the dlfo_eh_frame member.

     This macro is a GNU extension.

 -- Macro: int DLFO_EH_SEGMENT_TYPE
     GNU, dlfcn.h On targets using DWARF-based exception unwinding, this macro expands to PT_GNU_EH_FRAME.  This indicates that dlfo_eh_frame in struct dl_find_object points to the PT_GNU_EH_FRAME segment of the object.  On targets that use other unwinding formats, the macro expands to the program header type for the unwinding data.

     This macro is a GNU extension.

 -- Function: int _dl_find_object (void *ADDRESS, struct dl_find_object *RESULT)
     GNU, dlfcn.h On success, this function returns 0 and writes about the object surrounding the address to *RESULT.  On failure, -1 is returned.

     The ADDRESS can be a code address or data address.  On architectures using function descriptors, no attempt is made to decode the function descriptor.  Depending on how these descriptors are implemented, _dl_find_object may return the object that defines the function descriptor (and not the object that contains the code implementing the function), or fail to find any object at all.

     On success ADDRESS is greater than or equal to RESULT->dlfo_map_start and less than RESULT->dlfo_map_end, that is, the supplied code address is located within the reported mapping.

     This function returns a pointer to the unwinding information for the object that contains the program code ADDRESS in RESULT->dlfo_eh_frame.  If the platform uses DWARF unwinding information, this is the in-memory address of the PT_GNU_EH_FRAME segment.  See DLFO_EH_SEGMENT_TYPE above. In case ADDRESS resides in an object that lacks unwinding information, the function still returns 0, but sets RESULT->dlfo_eh_frame to a null pointer.

     _dl_find_object itself is thread-safe.  However, if the application invokes dlclose for the object that contains ADDRESS concurrently with _dl_find_object or after the call returns, accessing the unwinding data for that object or the link map (through RESULT->dlfo_link_map) is not safe.  Therefore, the application needs to ensure by other means (e.g., by convention) that ADDRESS remains a valid code address while the unwinding information is processed.

     This function is a GNU extension.

The following flag masks are defined for use with dlfo_flags:

DLFO_FLAG_SFRAME
     A bit mask used to signal that the object contains SFrame data.  See dlfo_sframe above.

Avoiding Unexpected Issues With Dynamic Linking
===============================================

This section details recommendations for increasing application robustness, by avoiding potential issues related to dynamic linking. The recommendations have two main aims: reduce the involvement of the dynamic linker in application execution after process startup, and restrict the application to a dynamic linker feature set whose behavior is more easily understood.

Key aspects of limiting dynamic linker usage after startup are: no use of the dlopen function, disabling lazy binding, and using the static TLS model.  More easily understood dynamic linker behavior requires avoiding name conflicts (symbols and sonames) and highly customizable features like the audit subsystem.

Note that while these steps can be considered a form of application hardening, they do not guard against potential harm from accidental or deliberate loading of untrusted or malicious code.  There is only limited overlap with traditional security hardening for applications running on GNU systems.

Restricted Dynamic Linker Features
----------------------------------

Avoiding certain dynamic linker features can increase predictability of applications and reduce the risk of running into dynamic linker defects.

  * Do not use the functions dlopen, dlmopen, or dlclose.  Dynamic loading and unloading of shared objects introduces substantial complications related to symbol and thread-local storage (TLS) management.

  * Without the dlopen function, dlsym and dlvsym cannot be used with shared object handles.  Minimizing the use of both functions is recommended.  If they have to be used, only the RTLD_DEFAULT pseudo-handle should be used.

  * Use the local-exec or initial-exec TLS models.  If dlopen is not used, there are no compatibility concerns for initial-exec TLS.  This TLS model avoids most of the complexity around TLS access.  In particular, there are no TLS-related run-time memory allocations after process or thread start.

     If shared objects are expected to be used more generally, outside the hardened, feature-restricted context, lack of compatibility between dlopen and initial-exec TLS could be a concern.  In that case, the second-best alternative is to use global-dynamic TLS with GNU2 TLS descriptors, for targets that fully implement them, including the fast path for access to TLS variables defined in the initially loaded set of objects.  Like initial-exec TLS, this avoids memory allocations after thread creation, but only if the dlopen function is not used.

  * Do not use lazy binding.  Lazy binding may require run-time memory allocation, is not async-signal-safe, and introduces considerable complexity.

  * Make dependencies on shared objects explicit.  Do not assume that certain libraries (such as libc.so.6) are always loaded. Specifically, if a main program or shared object references a symbol, create an ELF DT_NEEDED dependency on that shared object, or on another shared object that is documented (or otherwise guaranteed) to have the required explicit dependency.  Referencing a symbol without a matching link dependency results in underlinking, and underlinked objects cannot always be loaded correctly: Initialization of objects may not happen in the required order.

  * Do not create dependency loops between shared objects (libA.so.1 depending on libB.so.1 depending on libC.so.1 depending on libA.so.1).  Theglibc has to initialize one of the objects in the cycle first, and the choice of that object is arbitrary and can change over time.  The object which is initialized first (and other objects involved in the cycle) may not run correctly because not all of its dependencies have been initialized.

     Underlinking (see above) can hide the presence of cycles.

  * Limit the creation of indirect function (IFUNC) resolvers.  These resolvers run during relocation processing, when theglibc is not in a fully consistent state.  If you write your own IFUNC resolvers, do not depend on external data or function references in those resolvers.

  * Do not use the audit functionality (LD_AUDIT, DT_AUDIT, DT_DEPAUDIT).  Its callback and hooking capabilities introduce a lot of complexity and subtly alter dynamic linker behavior in corner cases even if the audit module is inactive.

  * Do not use symbol interposition.  Without symbol interposition, the exact order in which shared objects are searched are less relevant.

     Exceptions to this rule are copy relocations (see the next item), and vague linkage, as used by the C++ implementation (see below).

  * One potential source of symbol interposition is a combination of static and dynamic linking, namely linking a static archive into multiple dynamic shared objects.  For such scenarios, the static library should be converted into its own dynamic shared object.

     A different approach to this situation uses hidden visibility for symbols in the static library, but this can cause problems if the library does not expect that multiple copies of its code coexist within the same process, with no or partial sharing of state.

  * If you use shared objects that are linked with -Wl,-Bsymbolic (or equivalent) or use protected visibility, the code for the main program must be built as -fpic or -fPIC to avoid creating copy relocations (and the main program must not use copy relocations for other reasons).  Using -fpie or -fPIE is not an alternative to PIC code in this context.

  * Be careful about explicit section annotations.  Make sure that the target section matches the properties of the declared entity (e.g., no writable objects in .text).

  * Ensure that all assembler or object input files have the recommended security markup, particularly for non-executable stack.

  * Avoid using non-default linker flags and features.  In particular, do not use the DT_PREINIT_ARRAY dynamic tag, and do not flag objects as DF_1_INITFIRST.  Do not change the default linker script of BFD ld.  Do not override ABI defaults, such as the dynamic linker path (with --dynamic-linker).

  * Some features of theglibc indirectly depend on run-time code loading and dlopen.  Use iconv_open with built-in converters only (such as UTF-8).  Do not use NSS functionality such as getaddrinfo or getpwuid_r unless the system is configured for built-in NSS service modules only (see below).

Several considerations apply to ELF constructors and destructors.

  * The dynamic linker does not take constructor and destructor priorities into account when determining their execution order.  Priorities are only used by the link editor for ordering execution within a completely linked object.  If a dynamic shared object needs to be initialized before another object, this can be expressed with a DT_NEEDED dependency on the object that needs to be initialized earlier.

  * The recommendations to avoid cyclic dependencies and symbol interposition make it less likely that ELF objects are accessed before their ELF constructors have run.  However, using dlsym and dlvsym, it is still possible to access uninitialized facilities even with these restrictions in place.  (Of course, access to uninitialized functionality is also possible within a single shared object or the main executable, without resorting to explicit symbol lookup.)  Consider using dynamic, on-demand initialization instead.  To deal with access after de-initialization, it may be necessary to implement special cases for that scenario, potentially with degraded functionality.

  * Be aware that when ELF destructors are executed, it is possible to reference already-deconstructed shared objects.  This can happen even in the absence of dlsym and dlvsym function calls, for example if client code using a shared object has registered callbacks or objects with another shared object.  The ELF destructor for the client code is executed before the ELF destructor for the shared objects that it uses, based on the expected dependency order.

  * If dlopen and dlmopen are not used, DT_NEEDED dependency information is complete, and lazy binding is disabled, the execution order of ELF destructors is expected to be the reverse of the ELF constructor order.  However, two separate dependency sort operations still occur.  Even though the listed preconditions should ensure that both sorts produce the same ordering, it is recommended not to depend on the destructor order being the reverse of the constructor order.

The following items provide C++-specific guidance for preparing applications.  If another programming language is used and it uses these toolchain features targeted at C++ to implement some language constructs, these restrictions and recommendations still apply in analogous ways.

  * C++ inline functions, templates, and other constructs may need to be duplicated into multiple shared objects using vague linkage, resulting in symbol interposition.  This type of symbol interposition is unproblematic, as long as the C++ one definition rule (ODR) is followed, and all definitions in different translation units are equivalent according to the language C++ rules.

  * Be aware that under C++ language rules, it is unspecified whether evaluating a string literal results in the same address for each evaluation.  This also applies to anonymous objects of static storage duration that GCC creates, for example to implement the compound literals C++ extension.  As a result, comparing pointers to such objects, or using them directly as hash table keys, may give unexpected results.

     By default, variables of block scope of static storage have consistent addresses across different translation units, even if defined in functions that use vague linkage.

  * Special care is needed if a C++ project uses symbol visibility or symbol version management (for example, the GCC visibility attribute, the GCC -fvisibility option, or a linker version script with the linker option --version-script).  It is necessary to ensure that the symbol management remains consistent with how the symbols are used.  Some C++ constructs are implemented with the help of ancillary symbols, which can make complicated to achieve consistency.  For example, an inline function that is always inlined into its callers has no symbol footprint for the function itself, but if the function contains a variable of static storage duration, this variable may result in the creation of one or more global symbols. For correctness, such symbols must be visible and bound to the same object in all other places where the inline function may be called. This requirement is not met if the symbol visibility is set to hidden, or if symbols are assigned a textually different symbol version (effectively creating two distinct symbols).

     Due to the complex interaction between ELF symbol management and C++ symbol generation, it is recommended to use C++ language features for symbol management, in particular inline namespaces.

  * The toolchain and dynamic linker have multiple mechanisms that bypass the usual symbol binding procedures.  This means that the C++ one definition rule (ODR) still holds even if certain symbol-based isolation mechanisms are used, and object addresses are not shared across translation units with incompatible type definitions.

     This does not matter if the original (language-independent) advice regarding symbol interposition is followed.  However, as the advice may be difficult to implement for C++ applications, it is recommended to avoid ODR violations across the entire process image.  Inline namespaces can be helpful in this context because they can be used to create distinct ELF symbols while maintaining source code compatibility at the C++ level.

  * Be aware that as a special case of interposed symbols, symbols with the STB_GNU_UNIQUE binding type do not follow the usual ELF symbol namespace isolation rules: such symbols bind across RTLD_LOCAL boundaries.  Furthermore, symbol versioning is ignored for such symbols; they are bound by symbol name only.  All their definitions and uses must therefore be compatible.  Hidden visibility still prevents the creation of STB_GNU_UNIQUE symbols and can achieve isolation of incompatible definitions.

  * C++ constructor priorities only affect constructor ordering within one shared object.  Global constructor order across shared objects is consistent with ELF dependency ordering if there are no ELF dependency cycles.

  * C++ exception handling and run-time type information (RTTI), as implemented in the GNU toolchain, is not address-significant, and therefore is not affected by the symbol binding behaviour of the dynamic linker.  This means that types of the same fully-qualified name (in non-anonymous namespaces) are always considered the same from an exception-handling or RTTI perspective.  This is true even if the type information object or vtable has hidden symbol visibility, or the corresponding symbols are versioned under different symbol versions, or the symbols are not bound to the same objects due to the use of RTLD_LOCAL or dlmopen.

     This can cause issues in applications that contain multiple incompatible definitions of the same type.  Inline namespaces can be used to create distinct symbols at the ELF layer, avoiding this type of issue.

  * C++ exception handling across multiple dlmopen namespaces may not work, particular with the unwinder in GCC versions before 12. Current toolchain versions are able to process unwinding tables across dlmopen boundaries.  However, note that type comparison is name-based, not address-based (see the previous item), so exception types may still be matched in unexpected ways.  An important special case of exception handling, invoking destructors for variables of block scope, is not impacted by this RTTI type-sharing.  Likewise, regular virtual member function dispatch for objects is unaffected (but still requires that the type definitions match in all directly involved translation units).

     Once more, inline namespaces can be used to create distinct ELF symbols for different types.

  * Although the C++ standard requires that destructors for global objects run in the opposite order of their constructors, the Itanium C++ ABI requires a different destruction order in some cases.  As a result, do not depend on the precise destructor invocation order in applications that use dlclose.

  * Registering destructors for later invocation allocates memory and may silently fail if insufficient memory is available.  As a result, the destructor is never invoked.  This applies to all forms of destructor registration, with the exception of thread-local variables (see the next item).  To avoid this issue, ensure that such objects merely have trivial destructors, avoiding the need for registration, and deallocate resources using a different mechanism (for example, from an ELF destructor).

  * A similar issue exists for thread_local variables with thread storage duration of types that have non-trivial destructors.  However, in this case, memory allocation failure during registration leads to process termination.  If process termination is not acceptable, use thread_local variables with trivial destructors only. Functions for per-thread cleanup can be registered using pthread_key_create (globally for all threads) and activated using pthread_setspecific (on each thread).  Note that a pthread_key_create call may still fail (and pthread_create keys are a limited resource in theglibc), but this failure can be handled without terminating the process.

Producing Matching Binaries
---------------------------

This subsection recommends tools and build flags for producing applications that meet the recommendations of the previous subsection.

  * Use BFD ld (bfd.ld) from GNU binutils to produce binaries, invoked through a compiler driver such as gcc.  The version should be not too far ahead of what was current when the version of theglibc was first released.

  * Do not use a binutils release that is older than the one used to build theglibc itself.

  * Compile with -ftls-model=initial-exec to force the initial-exec TLS model.

  * Link with -Wl,-z,now to disable lazy binding.

  * Link with -Wl,-z,relro to enable RELRO (which is the default on most targets).

  * Specify all direct shared objects dependencies using -l options to avoid underlinking.  Rely on .so files (which can be linker scripts) and searching with the -l option.  Do not specify the file names of shared objects on the linker command line.

  * Consider using -Wl,-z,defs to treat underlinking as an error condition.

  * When creating a shared object (linked with -shared), use -Wl,-soname,lib... to set a soname that matches the final installed name of the file.

  * Do not use the -rpath linker option.  (As explained below, all required shared objects should be installed into the default search path.)

  * Use -Wl,--error-rwx-segments and -Wl,--error-execstack to instruct the link editor to fail the link if the resulting final object would have read-write-execute segments or an executable stack.  Such issues usually indicate that the input files are not marked up correctly.

  * Ensure that for each LOAD segment in the ELF program header, file offsets, memory sizes, and load addresses are multiples of the largest page size supported at run time.  Similarly, the start address and size of the GNU_RELRO range should be multiples of the page size.

     Avoid creating gaps between LOAD segments.  The difference between the load addresses of two subsequent LOAD segments should be the size of the first LOAD segment.  (This may require linking with -Wl,-z,noseparate-code.)

     This may not be possible to achieve with the currently available link editors.

  * If the multiple-of-page-size criterion for the GNU_RELRO region cannot be achieved, ensure that the process memory image right before the start of the region does not contain executable or writable memory.

Checking Binaries
-----------------

In some cases, if the previous recommendations are not followed, this can be determined from the produced binaries.  This section contains suggestions for verifying aspects of these binaries.

  * To detect underlinking, examine the dynamic symbol table, for example using readelf -sDW.  If the symbol is defined in a shared object that uses symbol versioning, it must carry a symbol version, as in pthread_kill@GLIBC_2.34.

  * Examine the dynamic segment with readelf -dW to check that all the required NEEDED entries are present.  (It is not necessary to list indirect dependencies if these dependencies are guaranteed to remain during the evolution of the explicitly listed direct dependencies.)

  * The NEEDED entries should not contain full path names including slashes, only sonames.

  * For a further consistency check, collect all shared objects referenced via NEEDED entries in dynamic segments, transitively, starting at the main program.  Then determine their dynamic symbol tables (using readelf -sDW, for example).  Ideally, every symbol should be defined at most once, so that symbol interposition does not happen.

     If there are interposed data symbols, check if the single interposing definition is in the main program.  In this case, there must be a copy relocation for it.  (This only applies to targets with copy relocations.)

     Function symbols should only be interposed in C++ applications, to implement vague linkage.  (See the discussion in the C++ recommendations above.)

  * Using the previously collected NEEDED entries, check that the dependency graph does not contain any cycles.

  * The dynamic segment should also mention BIND_NOW on the FLAGS line or NOW on the FLAGS_1 line (one is enough).

  * Ensure that only static TLS relocations (thread-pointer relative offset locations) are used, for example R_AARCH64_TLS_TPREL and X86_64_TPOFF64.  As the second-best option, and only if compatibility with non-hardened applications using dlopen is needed, GNU2 TLS descriptor relocations can be used (for example, R_AARCH64_TLSDESC or R_X86_64_TLSDESC).

  * There should not be references to the traditional TLS function symbols __tls_get_addr, __tls_get_offset, __tls_get_addr_opt in the dynamic symbol table (in the readelf -sDW output).  Supporting global dynamic TLS relocations (such as R_AARCH64_TLS_DTPMOD, R_AARCH64_TLS_DTPREL, R_X86_64_DTPMOD64, R_X86_64_DTPOFF64) should not be used, either.

  * Likewise, the functions dlopen, dlmopen, dlclose should not be referenced from the dynamic symbol table.

  * For shared objects, there should be a SONAME entry that matches the file name (the base name, i.e., the part after the slash).  The SONAME string must not contain a slash /.

  * For all objects, the dynamic segment (as shown by readelf -dW) should not contain RPATH or RUNPATH entries.

  * Likewise, the dynamic segment should not show any AUDIT, DEPAUDIT, AUXILIARY, FILTER, or PREINIT_ARRAY tags.

  * If the dynamic segment contains a (deprecated) HASH tag, it must also contain a GNU_HASH tag.

  * The INITFIRST flag (undeer FLAGS_1) should not be used.

  * The program header must not have LOAD segments that are writable and executable at the same time.

  * All produced objects should have a GNU_STACK program header that is not marked as executable.  (However, on some newer targets, a non-executable stack is the default, so the GNU_STACK program header is not required.)

Run-time Considerations
-----------------------

In addition to preparing program binaries in a recommended fashion, the run-time environment should be set up in such a way that problematic dynamic linker features are not used.

  * Install shared objects using their sonames in a default search path directory (usually /usr/lib64).  Do not use symbolic links.

  * The default search path must not contain objects with duplicate file names or sonames.

  * Do not use environment variables (LD_... variables such as LD_PRELOAD or LD_LIBRARY_PATH, or GLIBC_TUNABLES) to change default dynamic linker behavior.

  * Do not install shared objects in non-default locations.  (Such locations are listed explicitly in the configuration file for ldconfig, usually /etc/ld.so.conf, or in files included from there.)

  * In relation to the previous item, do not install any objects it glibc-hwcaps subdirectories.

  * Do not configure dynamically-loaded NSS service modules, to avoid accidental internal use of the dlopen facility.  The files and dns modules are built in and do not rely on dlopen.

  * Do not truncate and overwrite files containing programs and shared objects in place, while they are used.  Instead, write the new version to a different path and use rename to replace the already-installed version.

  * Be aware that during a component update procedure that involves multiple object files (shared objects and main programs), concurrently starting processes may observe an inconsistent combination of object files (some already updated, some still at the previous version).  For example, this can happen during an update of theglibc itself.