-
-
Notifications
You must be signed in to change notification settings - Fork 604
Dynamic Linker
OSv is made of many components but the dynamic linker is probably the most essential one as it interacts with and ties all other components together and is responsible for bootstrapping an application. In essence, it involves locating ELF file on the filesystem, loading it into memory using mmap()
, processing its headers and segments to relocate symbols, configuring TLS, executing its init functions, loading any dependant ELF objects and finally starting the app. Please note that unlike Linux, the dynamic linker is an integral part of the OSv kernel. Most of the dynamic linker code is located in core/elf.cc
, arch/<arch>/arch-elf.cc
and core/app.cc
.
-
application
- represents running program (_program
member points to it) -
elf::program
- typically there is only one instance of it so effectively it is a singleton, but it is possible to create new programs for new ELF namespaces-
symbol_module program::lookup(const char* name)
- iterates over all objects inelf::program::modules_list
and callsobject::lookup_symbol(name)
for each to finally returnsymbol_module
for the first found occurrence
-
-
elf::object
- represents an ELF object; implements logic to load an ELF file into memory and process its headers and relocations-
void object::relocate_pltgot()
-- ????; iterates over entries inDT_JMPREL
and either callsobject::arch_relocate_jump_slot()
ifbind_now
or sets the jump slots (???) to resolve lazily later (PLT_GOT
) -
void* object::resolve_pltgot(unsigned index)
- finds relocation info underdynamic_ptr<Elf64_Rela>(DT_JMPREL)
and symbol index and finds symbol by callingobject::symbol()
and callsobject::arch_relocate_jump_slot()
to write the symbol`s relocated address -
void object::relocate_rela()
- iterates over the table of relocation entries (dynamic_ptr<Elf64_Rela>(DT_RELA)
) and callsobject::arch_relocate_rela()
for eachElf64_Rela*
and passes its relocation type (p->r_info & 0xffffffff
), index in the symbol table of the object being relocated (p->r_info >> 32
), address of the relocation (_base + p->r_offset
, where write the relocation value to) and addend (p->r_addend
) -
bool object::arch_relocate_rela(u32 type, u32 sym, void *addr, Elf64_Sxword addend)
- based on the relocation type (type
argument) determines the relocation value (symbol relocated address or object module index orst_value
(?) for TLS) and writes it to the relocation address (addr
argument):-
R_X86_64_COPY
- callsobject::symbol_other(sym)
to find symbol in other objects -
R_X86_64_64
- callsobject::symbol(sym, true)
to find symbol in all objects (see below) and calculates the value assymbol.relocated_addr() + addend
-
R_X86_64_RELATIVE
- calculates the value as_base + addend
-
R_X86_64_JUMP_SLOT
,R_X86_64_GLOB_DAT
- callsobject::symbol(sym, true)
to find symbol in all objects (see below) and calculates the value assymbol.relocated_addr()
-
R_X86_64_DTPMOD64
- callsobject::symbol(sym, true)
to find symbol in all objects (see below) and calculates value as the module index of the object where symbol was found in; forSTN_UNDEF
uses index ofthis
object -
R_X86_64_DTPOFF64
- (TLS) ??? -
R_X86_64_TPOFF64
- (TLS)???
-
-
bool object::arch_relocate_jump_slot(u32 sym, void *addr, Elf64_Sxword addend, bool ignore_missing)
- callsobject::symbol(sym, true)
to find symbol in all objects (see below) and writessymbol.relocated_addr()
to the relocation jump slot address (addr
argument) -
symbol_module object::symbol(unsigned idx, bool ignore_missing)
- entry point to symbol lookup; accepts symbol index, finds its name in the object symbols table (dynamic_ptr<Elf64_Sym>(DT_SYMTAB)
) and searches for a symbol by name in all objects programs knows about by callingprogram::lookup(name)
; if symbol not found it aborts ifignore_missing
isfalse
otherwise just warns; returnssymbol_module
that is a tuple of the object the symbol resides and the symbol definition (Elf64_Sym *
); called by following methods during relocation phase:object::arch_relocate_rela()
object::arch_relocate_jump_slot()
object::resolve_pltgot()
-
Elf64_Sym* object::lookup_symbol(const char* name)
- looks up symbol by name by delegating to eitherlookup_symbol_old
orlookup_symbol_gnu
; bails out if object not visible (during construction) -
Elf64_Sym* object::lookup_symbol_old(const char* name)
- ??? -
Elf64_Sym* object::lookup_symbol_gnu(const char* name)
- uses GNU hashmap
-
The program::get_library()
is the critical point where dynamic linker gets involved in instantiating new application.
The main program (kernel?) gets instantiated by elf::create_main_program()
called from loader.cc
application::new_program()
instantiates new program for new ELF namespace with new base address.
Thread local storage is what allows applications and shared libraries to use variables stored in memory specific to a given thread. These include variables marked with __thread
and C++ thread_local
modifiers. For TLS variables to work correctly, OSv dynamic linker needs to recognize TLS segments in an ELF file, construct static TLS blocks in memory, process relevant relocations and provide certain functions like __tls_get_addr
among other things.
Before we delve into what OSv dynamic linker does to support TLS, it is important to understand two different formats of static TLS block layout - so-called Variant I and Variant II - and 4 different models of accessing TLS variables.
- The ELF Specification
- How To Write Shared Libraries
- The ELF format - how programs look from the inside
- ELF loading and dynamic linking
- Introduction to The ELF Format: Series
- The 101 of ELF files on Linux: Understanding and Analysis
- Understanding Symbols Relocation
- Relocations, Relocations
- Executable and Linkable Format 101 Part 3: Relocations
- Dynamic Linking
- ELF symbol resolution
- A Deep dive into (implicit) Thread Local Storage