Fact-checked by Grok 2 weeks ago

Shared library

A shared library, also known as a dynamic-link library (DLL) on Windows or a shared object (.so) file on Unix-like systems, is a reusable file containing compiled code, data, and symbols that multiple executing programs can load and share in memory at runtime, enabling efficient code reuse without duplication in each executable. Unlike static libraries, which are embedded directly into an application's binary during compilation, shared libraries are linked dynamically by the operating system's loader, resolving references to functions and variables only when the program starts or as needed. This mechanism supports position-independent code (PIC), allowing the library to be loaded at arbitrary memory addresses without relocation overhead in most cases. The concept of shared libraries traces its origins to the operating system in the late , where dynamic linking resolved symbolic references to procedures and variables at using segment tables and linkage pointers, facilitating modular code sharing across processes. This approach influenced early Unix systems, which initially relied on static linking but adopted shared libraries in the 1980s with extensions like those in , evolving further with the (ELF) standard in the 1990s for and other Unix variants to handle dependencies and relocations more flexibly. On Windows, the equivalent DLL format was introduced with in 1985, promoting similar loading and sharing. Shared libraries offer key advantages, including reduced memory usage by loading a single instance into physical memory for sharing via virtual memory mappings across processes, which is particularly beneficial in resource-constrained environments. They enhance modularity and maintenance by allowing libraries to be updated independently without recompiling dependent applications, while supporting features like symbol versioning to maintain through multiple application binary interfaces (ABIs). However, they introduce complexities such as dependency resolution by dynamic linkers (e.g., ld.so on ) and potential issues with version conflicts or delayed loading overhead. Today, shared libraries are fundamental to , powering everything from system calls in libc to plugins in applications like web browsers.

Fundamentals

Definition and Purpose

A shared library is a file containing executable code and data that can be loaded into memory and used by multiple programs or processes simultaneously, with the library's contents mapped into each process's address space without duplication. This design allows the operating system to load a single instance of the library into physical memory, which is then shared across all dependent processes, promoting efficient resource utilization. The primary purpose of shared libraries is to reduce memory consumption, requirements, and program startup times by eliminating the need to embed duplicate copies of common code within each . They also facilitate modular , enabling developers to update or shared components centrally without recompiling or redistributing every application that uses them, which enhances and supports easier deployment of common functionalities like networking or routines. Shared libraries emerged in the as part of the evolution toward dynamic linking in Unix systems, initially introduced in around 1986 and further developed in implementations to overcome the inefficiencies of static linking, such as and redundant memory usage in multi-program environments. In the basic workflow, programs reference shared libraries during compilation by including their symbols in the object files, but the actual resolution and loading of the library occur at runtime via a , which binds the necessary addresses when the program executes.

Types of Libraries

In programming, libraries are categorized primarily into static and dynamic (also known as shared) types, with static libraries serving as archives of object files and dynamic libraries enabling loading. Static libraries are linked during the phase, embedding their code directly into the resulting file, which makes the self-contained and independent of external dependencies at . This approach offers advantages such as portability across systems without needing additional files, but it increases the executable's size since the library code is duplicated in every application that uses it, and updates to the library require recompiling and relinking all dependent programs. Shared or dynamic libraries, in contrast, are loaded into memory at runtime and can be shared among multiple processes, promoting efficient resource use. These libraries remain as separate files outside the executable, allowing a single copy to serve multiple applications simultaneously, which reduces overall storage requirements and facilitates easier updates without altering the executables. Examples include .so files on systems and .dll files on Windows. However, they introduce runtime dependencies, potentially complicating deployment if the required libraries are missing or incompatible. Beyond these core types, libraries may take other forms such as archive libraries, which are essentially static libraries stored in formats like .a files on Unix systems, containing collections of object files (.o) for linking. Object files are individual compiled units (e.g., .o files on systems) that can be directly linked into executables or archived into static libraries. Runtime libraries, such as variants of the (e.g., libc on systems or the C runtime library on Windows), provide essential functions like and ; these can be implemented as either static or dynamic variants to suit different linking needs.
AspectStatic LibrariesShared/Dynamic Libraries
Linking PhaseCompile time; code embedded in executableRuntime; separate files loaded as needed
Memory ImpactHigher usage; full copy per executableLower usage; shared across processes
Update MechanismRequires recompilation of dependentsIndependent updates to library files
Executable SizeLarger due to included codeSmaller; defers code inclusion
DependenciesNone at runtimeRequires library presence at runtime
This table highlights key differences, emphasizing how static libraries prioritize reliability and dynamic libraries focus on and .

Technical Foundations

File Formats

Shared libraries are encoded in platform-specific binary file formats that support and linking. The most common formats include the and Linking Format (ELF) used on systems, the (PE) format employed by Windows for dynamic-link libraries (DLLs), and the format utilized on macOS and for dynamic shared libraries. These formats share a common architectural foundation consisting of headers, sections, and supporting tables to organize , , and . Headers typically begin with a magic number for identification—such as 0x7F 'E' 'L' 'F' for files—and include details like the file type (e.g., ET_DYN for shared objects in ), machine architecture, and an optional address. Sections delineate distinct content areas: executable resides in read-only sections like .text (marked with execute permissions in and ), initialized in .data, uninitialized in .bss, and read-only constants in .rodata or equivalent. Symbol tables, such as .symtab and .dynsym in or export tables in 's .edata section, catalog and names for linking, while uses __nl_symbol_ptr and __la_symbol_ptr sections for non-lazy and lazy symbol pointers. Relocation tables, found in .rel or .rela sections for , .reloc for , and pointer sections in , store offset and type information to adjust addresses at load time, enabling dynamic resolution of external references without fixed positioning. A key requirement for shared libraries is the use of (PIC), which allows the library to execute correctly when loaded at arbitrary memory addresses across multiple processes. Unlike absolute addressing, where code embeds fixed memory locations that demand runtime modifications to the read-only text segment (reducing sharability and incurring overhead), PIC employs relocatable addressing through mechanisms like the Global Offset Table (GOT) and Procedure Linkage Table (PLT) in ELF, base relocations in PE (enabled by the IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE flag), and symbol stubs in Mach-O's __picsymbol_stub section. This approach confines relocations to writable data segments, preserving the immutability of code sections and facilitating efficient memory sharing. To manage (ABI) evolution, incorporates symbol versioning, which tags symbols with version identifiers (e.g., via mapfiles assigning labels like GLIBCXX_3.4) to coexist multiple implementations within the same library file. This mechanism supports by allowing new symbols and changes without invalidating existing binaries dependent on prior versions, as the resolves calls to the appropriate versioned symbol. For instance, libraries can append new functionality while retaining stable ABIs through release versioning in filenames and DT_SONAME tags.

Memory Sharing

Shared libraries are loaded into a process's by the operating system mapping the library file directly into the , typically using the mmap on systems. This mapping treats sections of the library file as if they were part of the process's , with the handling the allocation of physical pages on demand. Read-only segments, such as executable code and constant data, are mapped in a shared manner, allowing multiple processes to reference the same physical pages without duplication. Process isolation is maintained through separate virtual address spaces for each process, ensuring that one process's modifications do not directly affect others. However, for the shared read-only portions of libraries, the physical memory pages remain common across all processes that load the same library, promoting efficient reuse. Writable data segments, like initialized variables, use a (COW) approach: they are initially mapped as shared read-only, but any write attempt by a process triggers the to create a copy of the affected page for that process alone, leaving the original intact for others. This mechanism, facilitated by flags like MAP_PRIVATE in , balances sharing with safety. The primary benefit of this memory sharing is a substantial reduction in overall system RAM consumption, as a single copy of the library's code and read-only data serves multiple applications. For example, the standard C library (libc), which provides essential functions like printf and malloc, is commonly shared among hundreds of processes on a typical system, preventing redundant loading that could otherwise multiply memory usage dramatically. However, limitations exist, particularly with thread-local storage (TLS), where per-thread variables in shared libraries face challenges such as static allocation limits, dynamic resizing issues during loading (e.g., via dlopen), and potential failures if the thread control block cannot accommodate additional TLS space across multiple libraries. This memory sharing becomes effective following the dynamic linking phase, where symbols are resolved to enable the mapping.

Dynamic Linking

Dynamic linking in shared libraries involves a multi-phase process that defers the final resolution of external symbols until program execution, enabling flexible loading of libraries at . During the compile-time , the generates with calls to external functions treated as unresolved references, often producing (PIC) using flags like -fPIC to facilitate later address adjustments. At link-time, the static linker (e.g., GNU ld) creates an file that includes stubs for these external calls, records dependencies on shared libraries via entries like DT_NEEDED in the ELF dynamic section, and sets up structures such as the Procedure Linkage Table (PLT) and Global Offset Table (GOT) for deferred resolution, without embedding the actual library code. This deferred approach contrasts with static linking by avoiding the inclusion of library routines in the executable, thus keeping it smaller and allowing library updates without recompilation. Symbol resolution occurs primarily at by the (e.g., ld.so on ), which performs a across the dependency chain of shared objects listed in DT_NEEDED. For each undefined in the or a loaded , the linker consults the dynamic symbol tables (.dynsym sections) and associated tables in the libraries, looking up exports in a defined that prioritizes the main , followed by dependencies in load order. If a remains unresolved after traversing the chain, the program typically fails to start with an error; however, visibility attributes like DF_SYMBOLIC can restrict searches to local scopes for faster resolution in certain libraries. This process ensures that symbols from multiple libraries are correctly mapped, handling interdependencies without manual intervention. Binding modes determine when symbol resolution and associated relocations are performed: lazy binding, the default, delays these actions until the first use of a function (e.g., via a PLT stub that jumps to the for resolution and updates the GOT entry), improving startup time by avoiding unnecessary work for unused symbols. In contrast, eager binding resolves all dynamic relocations immediately upon loading the libraries, triggered by environment variables like LD_BIND_NOW or linker flags such as -z now, which is useful for or security but increases initial load overhead. ELF supports this through specific relocation tables like DT_JMPREL for lazy procedure linkages, separating them from immediate ones. Relocation adjusts code and data references to account for the actual memory addresses where libraries are loaded, which may vary due to (ASLR) or other factors. Absolute relocations (e.g., R_X86_64_GLOB_DAT in ) require full resolution to compute fixed addresses relative to the address, making them suitable for global data but costlier at runtime. Relative relocations (e.g., R_X86_64_RELATIVE), computed using offsets known at link-time, are position-independent and faster, as they avoid symbol lookups and remain valid regardless of load position; formats support these via tables like DT_REL and DT_RELA for implicit or explicit addends. The processes these entries in sections such as .rel.dyn or .rela.dyn to patch the in-memory image correctly.

Runtime Mechanisms

Locating Libraries at Runtime

When a program requires shared libraries at runtime, the operating system's dynamic linker must locate these files using a prioritized set of search strategies to ensure efficient and correct loading. In Unix-like systems, which commonly employ the Executable and Linking Format (ELF), the dynamic linker such as ld.so follows a conditional search sequence based on the ELF dynamic section attributes: if the DT_RPATH tag is present (and no DT_RUNPATH), it first searches directories listed in DT_RPATH; otherwise, it begins with the user-defined environment variable LD_LIBRARY_PATH (if not in secure-execution mode). Next, if present, it searches DT_RUNPATH directories. The process then consults the ldconfig cache before default system directories. These mechanisms are specific to Unix-like systems; other platforms, such as Microsoft Windows, use distinct search orders (see Platform Implementations section). Search paths form the core of library location in these systems. Default directories, such as /lib and /usr/lib (or their 64-bit variants like /lib64), are hardcoded into the dynamic linker and searched last. The environment variable LD_LIBRARY_PATH allows users to specify a colon-separated list of directories searched early in the process (after DT_RPATH but before DT_RUNPATH and defaults), though it is ignored in secure-execution modes for safety. Executables and libraries can embed runtime search paths via the DT_RPATH or DT_RUNPATH tags in their ELF dynamic section: DT_RPATH lists directories with highest precedence (searched before LD_LIBRARY_PATH and applying to the entire dependency tree), while DT_RUNPATH provides similar functionality but is searched after LD_LIBRARY_PATH (thus overridable by it) and applies only to direct dependencies. To accelerate lookups, systems employ caching mechanisms that precompute library locations. On , the ldconfig utility scans specified directories—defined in /etc/ld.so.conf or passed via command line—and builds a cache at /etc/ld.so.cache, which the consults after environment variables and embedded paths (DT_RUNPATH or DT_RPATH) for rapid resolution of library names to full paths. This cache includes symbolic links for versioned libraries (e.g., libfoo.so pointing to libfoo.so.1.2) and is updated periodically, typically by the system administrator, to reflect new installations without runtime overhead. Dependency resolution involves parsing the ELF dynamic section to identify and load prerequisites recursively. The DT_NEEDED tag in an object's lists the names of required shared libraries as null-terminated strings, which the linker processes in order to build a dependency tree; for each, it applies the search paths to locate and load the file, then recurses on that library's own DT_NEEDED entries. This traversal ensures all transitive dependencies are resolved before program execution, using the applicable search paths including embedded ones from the parent object where relevant. Error handling occurs when resolution fails, typically resulting in immediate termination. If a required specified in DT_NEEDED cannot be found in any search path or cache, the issues an such as "file not found" and exits with a non-zero , preventing execution of potentially unstable programs. Fallbacks are limited; for example, the linker may attempt versioned matches (e.g., seeking libc.so.6 if libc.so is requested) but does not substitute incompatible libraries, emphasizing the need for complete installations.

Dynamic Loading

Dynamic loading enables programs to explicitly load shared libraries at under programmatic control, allowing flexibility in extending functionality without requiring recompilation or restart. This mechanism contrasts with implicit linking by providing handles to loaded modules, which can then be queried for and managed independently. On systems adhering to standards, the primary for this is provided by the <dlfcn.h> header, which includes functions such as dlopen() to load a library and return a , and dlclose() to unload it. The dlopen() function accepts a pathname to the shared object file and optional flags like RTLD_LAZY for deferred symbol resolution, ensuring the library is mapped into the process's only when invoked. On Microsoft Windows, the equivalent functionality is offered through the in libloaderapi.h, where LoadLibrary() or LoadLibraryEx() loads a (DLL) and returns a module handle, while FreeLibrary() decrements the reference count to facilitate unloading. These functions search for the library using standard paths, such as the system directory or application directory, as a prerequisite for loading. Once a is loaded, programs retrieve addresses of functions or variables via symbol lookup APIs: dlsym() on systems, which takes the handle from dlopen() and a symbol name to return a pointer, and GetProcAddress() on Windows, which uses the module handle from LoadLibrary() to obtain the procedure address. This allows direct invocation of library code, such as casting the returned pointer to the expected function type in C or C++ programs. Common use cases for include implementing architectures, where a host application scans a and loads extension modules to add features like image filters in ; just-in-time loading of optional components to reduce initial , such as cryptographic libraries loaded only when is needed; and hot-swapping libraries for updating functionality without restarting the process, as seen in database servers or web browsers. Unloading shared libraries employs to ensure safe : both dlclose() and Windows FreeLibrary() decrement a per-module count maintained by the , and the library is only unmapped from memory when the count reaches zero, preventing premature deallocation if multiple components reference it. This mechanism avoids dangling pointers and resource leaks, though developers must track handles carefully to avoid over-unloading.

Platform Implementations

Unix-like Systems

In systems, shared libraries are commonly distributed as files with the .so extension, following the shared object . These files include a special identifier known as the , which encodes versioning information to ensure binary compatibility across updates; for example, the is often named libc.so.6, where 6 represents the major interface version, allowing programs linked against it to continue functioning even if minor updates occur without breaking the ABI. The is embedded during compilation and serves as a logical name for the to resolve dependencies at runtime, preventing mismatches between library versions and linked executables. Several tools facilitate the creation, inspection, and management of shared libraries. The ld linker, part of the GNU Binutils suite, combines object files into shared libraries by resolving symbols and generating the necessary dynamic sections, often invoked via compiler flags like -shared in GCC. For examining dependencies, the ldd utility lists the shared libraries required by an executable or another library, querying the dynamic linker to display resolved paths and versions without executing the program. Inspection of library internals, such as symbols, sections, and relocations, is performed using objdump, which disassembles and extracts metadata from ELF object files, aiding in debugging and verification. The process is handled by the runtime linker, typically ld.so on generic systems or ld-linux.so variants on architectures, which searches standard paths like /lib and /usr/lib to load and relocate shared libraries into the process . Environment variables provide fine-grained control; for instance, LD_PRELOAD allows preloading specific libraries before others, overriding default symbols for debugging or testing, while LD_LIBRARY_PATH extends the search path for non-standard locations. These mechanisms enable flexible behavior without recompiling executables. Advanced features enhance security and observability. The LD_AUDIT supports audit modules by loading specified shared objects that intercept linker events, such as library loading or symbol resolution, through callbacks defined in the GNU C Library's runtime linking interface; this is useful for tracing, profiling, or custom interventions but is disabled in secure-execution modes to prevent abuse. For protection against exploits targeting relocations, RELRO (Relocation Read-Only) marks the global offset table (GOT) and other relocation sections as read-only after initial linking, mitigating attacks like GOT overwrites; partial RELRO applies lazily per relocation, while full RELRO processes all at startup for stronger hardening, enabled via compiler flags like -Wl,-z,relro,-z,now. Shared libraries in these systems are based on the (ELF), which structures the file with sections for code, data, and dynamic linking metadata.

Microsoft Windows

In Microsoft Windows, shared libraries are implemented as dynamic-link libraries (DLLs), which are executable files with the .dll extension that contain code, data, and resources usable by multiple applications simultaneously. DLLs follow the (PE) file format, enabling the operating system to load them into process address space for dynamic linking at . To address versioning conflicts, Windows supports side-by-side assemblies, which allow multiple versions of a DLL to coexist on the system, each identified by a unique name, version, and . The Windows loader searches for DLLs in a specific order to resolve dependencies: first the application directory, then the current , followed by the 16-bit system directory, the 32-bit Windows system directory, the Windows directory, and finally directories listed in the . To mitigate security risks such as DLL hijacking, where malicious DLLs in untrusted locations are loaded preferentially, Windows provides safe DLL search mode, which rearranges the order to prioritize system directories over the current directory and application directory. Developers use tools like DUMPBIN, a utility included in , to inspect DLL contents such as exports, imports, and headers from PE files. (depends.exe) is a legacy tool for analyzing DLL dependencies by generating a hierarchical tree of modules and their imported/exported functions, but it is no longer bundled with (last included in version 2005) or the Windows SDK and does not work reliably on and later; modern alternatives include the open-source Dependencies.exe. Application and assembly manifests, XML files embedded in executables or stored alongside, declare DLL dependencies, including required versions and side-by-side assemblies, ensuring the correct libraries are loaded without conflicts. The DLL loading process is managed by the native in ntdll.dll, which exports functions like LdrLoadDll to map DLLs into memory and resolve imports. For performance optimization, Windows supports delay-load imports, where DLLs are loaded only when their functions are first called, rather than at process startup; this is enabled via the /DELAYLOAD linker option and handled by a helper routine in the executable.

Other Systems

macOS inherits its dynamic linking system from the heritage, utilizing the executable format for shared libraries, which are typically distributed as files with the .dylib extension. These libraries are loaded and managed by the dyld at , enabling efficient code sharing across applications while supporting features like two-level namespaces for symbol resolution. To facilitate flexible path resolution, macOS employs the @rpath mechanism, where library paths embedded in executables are substituted with search paths defined via the LC_RPATH load command, allowing libraries to be located relative to the executable or framework bundle. Historically, and its successor introduced frameworks as a core mechanism for organizing shared libraries, bundling dynamic shared with associated header files, resources such as files and images, and documentation into a single directory structure typically installed under /NextLibrary/Frameworks. These frameworks, exemplified by the Application Kit and Foundation frameworks, promote modularity by allowing applications to link against a single copy of library routines and load only required modules dynamically. Bundles in and extend this concept, serving as directories that encapsulate executable code, resources like sounds and archived objects, and principal es for loadable extensions; the NSBundle enables runtime access and of these bundles, such as auxiliary files for inspectors or document templates, enhancing application extensibility without recompilation. In embedded systems like Android, shared libraries are implemented as .so files compatible with the ELF format, leveraging Bionic as the lightweight C library (libc) implementation tailored for the platform's resource constraints and security model. Bionic provides essential runtime functions, including dynamic linking via the ld-android.so linker, and supports shared C++ runtimes like libc++_shared.so, which must be explicitly included in applications to avoid loading order issues on older Android versions; this setup allows native code modules to be reused across apps while integrating with the Android Runtime (ART). Cross-platform efforts, such as , enable shared code modules in browser environments through a where modules define imports and exports for functions, globals, memories, and tables, allowing them to function as reusable libraries. These modules, compiled to a compact format, can be instantiated and linked at runtime in modern browsers, with exports exposing entities to hosts or other modules for shared access, thus supporting efficient, sandboxed code reuse without traditional dynamic linker dependencies. Emerging implementations include lightweight variants using libc, which fully supports dynamic linking and shared libraries to provide standards-compliant functionality with minimal overhead, as seen in distributions like for containerized and embedded deployments. In operating systems (RTOS), shared library support varies due to deterministic requirements, but systems like offer modular libraries for connectivity, security, and AWS integration that can be linked as reusable components, often statically to ensure predictability.

Advanced Topics

Optimizations

Shared libraries, building on memory sharing mechanisms, employ various optimizations to enhance loading times, reduce resource usage, and improve efficiency. These techniques address overheads in dynamic linking and loading, such as resolution and , while maintaining across processes. Preloading involves loading anticipated shared libraries into memory before program execution, often guided by static analysis of dependencies to predict common usage patterns. In systems, tools like the (ld.so) support preloading via environment variables such as LD_PRELOAD, which prioritizes specified libraries during startup, thereby minimizing searches and achieving up to a 2.3-fold in initialization latency in serverless environments, as demonstrated in profile-guided optimizations. Profile-guided approaches extend this by analyzing execution traces to preload workload-specific libraries, further optimizing cold-start scenarios in serverless environments. Address space layout randomization (ASLR) introduces trade-offs in shared library performance by randomizing load addresses to enhance , which can incur minor overhead from additional relocation computations. On systems, ASLR typically exhibits negligible performance impact for shared libraries using (PIC), though benchmarks for PIE-enabled executables indicate an average 9-10% slowdown on x86 systems due to shared mappings mitigating per-process randomization costs. Windows DLL implementations benefit from ASLR by avoiding relocation overhead through a single shared copy, similar to Unix optimizations like precomputed relocations in ELF shared libraries. Overall, the efficiency gains from memory sharing often outweigh ASLR costs in multi-process environments. To minimize disk and , shared libraries undergo and stripping, accelerating loading by reducing transfer and parsing times. Stripping removes unnecessary and non-exported identifiers using tools like the GNU strip utility with options such as --strip-unneeded, which can reduce startup time by 30-70% by minimizing overhead, while also shrinking file without affecting functionality, as required for dynamic linking remain intact. For further reduction, executable packers like apply to non-shared sections, though compatibility issues limit their use for fully dynamic libraries; instead, techniques like tailoring selectively prune unused paths, yielding reductions of up to 39% in embedded systems, with negligible impact on load times. Modern optimizations leverage profile-guided techniques and hardware features for finer-grained efficiency. (PGO) instruments shared libraries during compilation to collect runtime profiles, enabling the to reorder and inline hot paths specific to library usage patterns, resulting in 5-15% performance gains in dynamic linking scenarios. accelerations, such as Intel's Control-flow Enforcement Technology (CET), provide low-overhead verification of control transfers in shared via dedicated shadow stacks and indirect branch instructions, with benchmarks indicating less than 5% overhead in library-heavy workloads on supported processors. These methods ensure shared libraries scale efficiently in multi-threaded and distributed environments.

Security Considerations

Shared libraries introduce several security risks due to their and shared nature across processes, particularly in how locating mechanisms can serve as attack vectors for injecting malicious code. One prominent vulnerability is DLL hijacking on Windows s, where attackers exploit the dynamic linker's search to load a malicious DLL instead of the intended legitimate one, potentially leading to code execution with the privileges of the calling process. This occurs when an application loads a DLL without specifying a fully qualified , allowing the attacker to place a rogue file in a earlier in the search order, such as the current or directories under their control. Mitigations include enforcing a safe DLL search order via registry settings to prioritize trusted paths, using fully qualified paths in load calls, and verifying binaries with digital signatures to prevent unsigned malicious substitutes. On systems, symbol interposition via environment variables like LD_PRELOAD poses similar risks by allowing attackers to preload a malicious that overrides legitimate symbols, enabling arbitrary for or data interception. For instance, setting LD_PRELOAD to a can hijack functions in libc, such as calls, to alter behavior without modifying the . Secure alternatives include using interfaces like LD_AUDIT, which provides a controlled mechanism for inspecting and modifying linker behavior without the broad override capabilities of LD_PRELOAD, though it requires careful implementation to avoid similar abuses. Additionally, disabling LD_PRELOAD for binaries and monitoring environment variables in privileged contexts help mitigate these interposition attacks. Buffer overflows within shared libraries, particularly in widely used ones like libc, have historically enabled devastating exploits by allowing attackers to overwrite adjacent memory, including return addresses or control data. A classic example is the , first detailed in 1997, which leverages buffer overflows to redirect execution to functions within libc, such as system(), bypassing non-executable stack protections and spawning shells without injecting . Such vulnerabilities in libc have been exploited in early worms like the in 1988, which targeted buffer overflows in Unix server software involving library routines, leading to widespread system compromises. Modern hardening techniques include stack canaries, random values placed between buffers and control data to detect overflows during function returns, and (ASLR), which randomizes library load addresses to complicate return-oriented exploits. These measures, integrated into compilers like , significantly raise the bar for successful exploitation. To enhance security, best practices emphasize the principle of least privilege by ensuring shared libraries run with minimal necessary permissions, such as avoiding bits on binaries that load them and confining es to restrict library access. Version pinning, where applications explicitly specify exact library versions in manifests or build configurations, prevents unexpected updates that could introduce vulnerabilities, as recommended for dependency management in production environments. Sandboxing tools like further isolate library usage by enforcing mandatory access controls that limit which shared libraries a can load or execute, denying unauthorized paths or interpositions while allowing legitimate operations.

References

  1. [1]
    Linking and Shared Libraries - Cornell: Computer Science
    Shared libraries (On Windows, DLLs) are libraries that are intended to be loaded into memory just once. When linking an executable against a shared library, the ...Missing: science authoritative
  2. [2]
    [PDF] How To Write Shared Libraries - Dartmouth Computer Science
    Dec 10, 2011 · Libraries of this kind are called shared libraries. The concept is not very new.
  3. [3]
    [PDF] Lecture 7: The Multics Virtual Memory - Berkeley
    Sep 21, 2005 · Dynamic linking: all references to variables and procedures are resolved late, in fact, when first referenced by the program. • Autonomy of ...
  4. [4]
    [PDF] The inside story on shared libraries and dynamic loading
    Sep 2, 2025 · If more than one library happens to define the same symbol, only the first definition applies. Duplicate symbols normally don't occur, because ...
  5. [5]
    C H A P T E R 16 - Building Libraries - Oracle Help Center
    First, they provide a way to share code among several applications. If you have such code, you can create a library with it and link the library with any ...Missing: software development<|control11|><|separator|>
  6. [6]
    [PDF] Chapter 10 Dynamic Linking and Loading
    Jun 15, 1999 · Sun Microsystems' SunOS introduced dynamic shared libraries to UNIX in the late 1980s. UNIX System V Release 4, which Sun co-developed,.
  7. [7]
    Chapter 16. Using Libraries with GCC | Red Hat Enterprise Linux | 7
    Static linking makes libraries part of the resulting executable file. Dynamic linking keeps these libraries as separate files. Dynamic and static linking can be ...
  8. [8]
    Advantages of Dynamic Linking - Win32 apps - Microsoft Learn
    Jan 7, 2021 · Dynamic linking has the following advantages over static linking: A potential disadvantage to using DLLs is that the application is not self-contained.
  9. [9]
    What are .a and .so Files? | Baeldung on Linux
    Mar 18, 2024 · On Linux, archive libraries end with the .a extension, and shared object libraries end with the .so extension.
  10. [10]
    Archive libraries - IBM
    The ar utility allows you to create and maintain a library of z/OS® XL C/C++ application object files.
  11. [11]
    C runtime (CRT) and C++ standard library (STL) lib files
    Feb 2, 2024 · You can't use the statically linked CRT ( /MT or /MTd options) with /clr . Use the dynamically linked libraries ( /MD or /MDd ) instead.
  12. [12]
    [PDF] Tool Interface Standard (TIS) Executable and Linking Format (ELF ...
    A set of object files, libraries, system shared resources and other shared libraries are linked together to create the executable. When this executable is ...
  13. [13]
    PE Format - Win32 apps - Microsoft Learn
    Jul 14, 2025 · This specification describes the structure of executable (image) files and object files under the Windows family of operating systems.General Concepts · Overview
  14. [14]
    Overview of the Mach-O Executable Format - Apple Developer
    Mar 10, 2014 · Mach-O is the native executable format of binaries in OS X and is the preferred format for shipping code. An executable format determines the ...
  15. [15]
    Position-Independent Code - Linker and Libraries Guide
    Position-independent code is not tied to a specific address. This independence allows the code to execute efficiently at a different address in each process ...
  16. [16]
    ABI Policy and Guidelines
    ### Summary of ELF Symbol Versioning for ABI Changes and Compatibility in Shared Libraries
  17. [17]
    ld.so(8) - Linux manual page
    ### Summary: How the Dynamic Linker Loads Shared Libraries
  18. [18]
    mmap(2) - Linux manual page - man7.org
    mmap() returns a pointer to the mapped area. On error, the value MAP_FAILED (that is, (void *) -1) is returned, and errno is set to indicate the error.<|control11|><|separator|>
  19. [19]
    How Libc shared library loaded in memory and shared amongst ...
    Apr 21, 2011 · This reduces memory use and improves system performance. See also "Dissecting shared libraries" article. Share.Shared libraries memory space - c++ - Stack OverflowHow are shared libraries addressed in each processes memory?More results from stackoverflow.comMissing: benefits example
  20. [20]
    All about thread-local storage | MaskRay
    Feb 14, 2021 · Thread-local storage (TLS) provides a mechanism allocating distinct objects for different threads. It is the usual implementation for GCC extension __thread.
  21. [21]
    None
    Summary of each segment:
  22. [22]
    Dynamic Linking
    ### Summary of Dynamic Linking in ELF (GABI 4+ Chapter 5)
  23. [23]
    ldconfig(8) - Linux manual page
    ### Summary of ldconfig (ldconfig man page)
  24. [24]
    LoadLibraryA function (libloaderapi.h) - Win32 apps - Microsoft Learn
    Feb 8, 2023 · LoadLibrary can be used to load a library module into the address space of the process and return a handle that can be used in GetProcAddress to ...Syntax · Parameters
  25. [25]
    GetProcAddress function (libloaderapi.h) - Win32 - Microsoft Learn
    Feb 6, 2024 · Retrieves the address of an exported function (also known as a procedure) or variable from the specified dynamic-link library (DLL).Syntax · Parameters
  26. [26]
    Dynamically Loaded (DL) Libraries - The Linux Documentation Project
    The converse of dlopen() is dlclose(), which closes a DL library. The dl library maintains link counts for dynamic file handles, so a dynamic library is not ...4.1. Dlopen() · 4.3. Dlsym() · 4.4. Dlclose()Missing: POSIX | Show results with:POSIX
  27. [27]
    Dynamic-Link Libraries (Dynamic-Link Libraries) - Win32 apps
    May 31, 2022 · A dynamic-link library (DLL) is a module that contains functions and data that can be used by another module (application or DLL).Missing: static | Show results with:static
  28. [28]
    About Side-by-Side Assemblies - Win32 apps - Microsoft Learn
    Jan 7, 2021 · A side-by-side assembly contains a collection of resources—a group of DLLs, Windows classes, COM servers, type libraries, or interfaces—that are ...
  29. [29]
    Dynamic-link library search order - Win32 apps | Microsoft Learn
    Feb 8, 2023 · The standard DLL search order used by the system depends on whether or not safe DLL search mode is enabled.
  30. [30]
    DUMPBIN Reference | Microsoft Learn
    Aug 10, 2021 · You can use DUMPBIN to examine COFF object files, standard libraries of COFF objects, executable files, and dynamic-link libraries (DLLs).DUMPBIN Command Line · DUMPBIN options · Building on the command line
  31. [31]
    Dependency Walker (depends.exe) Home Page
    **Summary of Dependency Walker:**
  32. [32]
    Application manifests - Win32 apps - Microsoft Learn
    May 30, 2024 · An application manifest is an XML file that describes and identifies the shared and private side-by-side assemblies that an application ...
  33. [33]
    What Goes On Inside Windows 2000: Solving the Mysteries of the ...
    This article explores DLL loading and exposes what really goes on inside the Windows 2000 loader. Knowing how DLLs are loaded and where, and how the loader ...<|separator|>
  34. [34]
    /DELAYLOAD (Delay Load Import) | Microsoft Learn
    Aug 3, 2021 · The /DELAYLOAD option causes the DLL that's specified by dllname to be loaded only on the first call by the program to a function in that DLL.
  35. [35]
    Technical Note TN2435: Embedding Frameworks In An App
    Mar 14, 2017 · Dynamic libraries outside of a framework bundle, which typically have the file extension .dylib , are not supported on iOS, watchOS, or tvOS, ...
  36. [36]
    [PDF] DISCOVERING OPENSTEP: A DEVELOPER TUTORIAL
    Common documents are word-processing documents and spreadsheets. From ... dynamic shared libraries 7 dynamic typing 36, 46, 206. E. Editmenu 24 editable ...
  37. [37]
    C++ library support - NDK - Android Developers
    Jan 3, 2024 · If you use libc++_shared.so , it must be included in your app. If you're building your application with Gradle this is handled automatically.
  38. [38]
    Modules — WebAssembly 3.0 (2025-11-02)
    WebAssembly programs are organized into modules, which are the unit of deployment, loading, and compilation. A module collects definitions for types, tags, and ...Missing: shared | Show results with:shared
  39. [39]
  40. [40]
    FreeRTOS libraries - FreeRTOS™
    ### Summary of Shared Library Implementations or Support in FreeRTOS
  41. [41]
    Reducing Library Loading Overhead by Profile-guided Optimization
    Apr 27, 2025 · Although existing static analysis techniques can identify unreachable libraries, they fail to address workload-dependent inefficiencies, ...<|separator|>
  42. [42]
    Efficient Serverless Cold Start: Reducing Library Loading Overhead ...
    Apr 27, 2025 · SlimStart demonstrates superior performance improvements by dynamically identifying and optimizing workload-dependent library usage, achieving ...
  43. [43]
    Differences Between ASLR on Windows and Linux
    Feb 10, 2014 · In general, ASLR has no performance impact. In some scenarios, there's a slight performance improvement on 32-bit systems. However, it is ...
  44. [44]
    Six Facts about Address Space Layout Randomization on Windows
    Mar 17, 2020 · The answer lies in performance and in trade-offs made in the design of Windows DLLs versus other designs like ELF shared libraries. Windows ...
  45. [45]
    [PDF] Performance and Entropy of Various ASLR Implementations
    Dec 14, 2015 · Linux showed almost no difference between ASLR on and off in this test. HardenedBSD had a small decrease in per- formance. Because we were ...<|separator|>
  46. [46]
    [PDF] Honey, I Shrunk the ELFs: Lightweight Binary Tailoring of Shared ...
    Reducing the size of shared libraries has a number of benefits: reduced storage sizes lead to a smaller footprint of the libraries in the system memory and can ...
  47. [47]
    Profile-guided optimizations | Microsoft Learn
    Oct 18, 2022 · Profile-guided optimization (PGO) lets you optimize a whole executable file, where the optimizer uses data from test runs of the .exe or .dll file.Steps to optimize your app · Optimizations performed by PGO
  48. [48]
    [PDF] Security Analysis of Processor Instruction Set Architecture for ...
    Jun 23, 2019 · Performance. The performance impact of shadow stacks was evaluated using a suite of microprocessor benchmark and application traces executed.<|separator|>
  49. [49]
    [PDF] CETIS: Retrofitting Intel CET for Generic and Efficient Intra-process ...
    Nov 11, 2022 · Performance impact due to SHSTK. As CETIS needs to enable the SHSTK mechanism, an application using CETIS would have to use the SHSTK ...
  50. [50]
    Dynamic-Link Library Security - Win32 apps | Microsoft Learn
    Jan 7, 2021 · If an attacker gains control of one of the directories on the DLL search path, it can place a malicious copy of the DLL in that directory.Missing: mitigations | Show results with:mitigations
  51. [51]
    Secure loading of libraries to prevent DLL preloading attacks
    Provides guidance for software developers that design applications that dynamically load DLLs without specifying a fully qualified path name.
  52. [52]
    Restrict Library Loading, Mitigation M1044 - MITRE ATT&CK®
    Jun 11, 2019 · Restricting library loading involves implementing security controls to ensure that only trusted and verified libraries (DLLs, shared objects, etc.) are loaded ...<|separator|>
  53. [53]
    Dynamic Linker Hijacking, Sub-technique T1574.006 - Enterprise
    Detection focuses on identifying abuse of LD_PRELOAD and related linker variables. Defender perspective: monitor unexpected setting or modification of ...<|separator|>
  54. [54]
    Leveraging LD_AUDIT to Beat the Traditional Linux Library ...
    Oct 13, 2020 · In SentinelOne's Linux research team, we found a technique that can load shared libraries even before LD_PRELOAD. The Loader's Auditing API.
  55. [55]
    Security - Oracle Solaris 11.1 Linkers and Libraries Guide
    In a secure process, LD_SIGNAL is ignored. Additional objects can be loaded with a secure process using the LD_PRELOAD or LD_AUDIT environment variables. These ...Missing: audit | Show results with:audit
  56. [56]
    Exploitation - Returning into libc
    Apr 8, 2006 · Exploit a buffer overflow bug to overwrite EIP with the address of system() or execl() included in the libc library to run an interactive shell.
  57. [57]
    In-Depth Exploration Of Buffer Overflow Risks In Linux Systems
    Sep 2, 2024 · Stack-based buffer overflows occur when a program writes more data to its stack than is allocated, potentially enabling an attacker to overwrite ...
  58. [58]
    [PDF] Real-World Buffer Overflow Protection for Userspace & Kernelspace
    A standard buffer overflow attack will change the canary value before over- writing protected data, and thus canary checks provide buffer overflow detection.
  59. [59]
    Stack Canaries – Gingerly Sidestepping the Cage - SANS Institute
    Feb 4, 2021 · Stack canaries were invented to prevent buffer overflow (BOF) vulnerabilities from being exploited. This BOF is the root problem that needs ...
  60. [60]
    What Is the Principle of Least Privilege? - Palo Alto Networks
    The principle of least privilege (PoLP) is an information security concept which maintains that a user or entity should only have access to the specific data, ...Missing: libraries | Show results with:libraries
  61. [61]
    Best practices for dependency management | Google Cloud Blog
    Jul 28, 2021 · Version pinning. In short, version pinning means restricting the version of a dependency of your application to a very specific version ...
  62. [62]
    AppArmor - Ubuntu security documentation
    AppArmor can limit a process's ability to load shared libraries, execute specific applications, send or receive signals. It can also restrict the use of ptrace.Missing: best practices least pinning sandboxing<|control11|><|separator|>