.. _elf-en: ============= ELF Reference ============= About the ELF format ==================== ELF is a file format used in Linux (and many other systems) for programs, shared libraries (``.so``), intermediate build results (``.o``), and memory dump files (``core``). Although the basic features of the ELF format are always the same, there are many elements depending on the processor architecture and sometimes on the operating system. Here we will only deal with the ELF format on the x86 architecture in the Linux system. Unfortunately, there is no full ELF specification. The "base" ELF format is described in the System V generic ABI (gABI) documentation, and the architecture-dependent parts should be described in the appropriate processor-specific ABI (psABI). In practice, however, psABI for many architectures can be hard to come by, very incomplete, or not written at all. The matter is further complicated by ELF extensions: ELF, as an open and flexible format, allows operating systems to define their own types of sections, relocations, symbols, etc. Many of them are not described anywhere. Probably the most complete single document describing ELF is Sun's Linker and Libraries Guide. This is basically the documentation of the linker and dynamic libraries on the Solaris operating system, but it contains a full description of the ELF format for i386, x86_64, sparc, sparc64 architectures, as well as several extensions used also on Linux systems (versioning, TLS). Another useful resource is the ``elf.h`` header file from the glibc library (``/usr/include/elf.h``). It contains constants and ELF structures for the architectures and operating systems supported by glibc. Header files with support for even more architectures can be found in the libbfd library, which is part of binutils. In this file only a sketch of the ELF format will be presented -- for detailed information, I refer to the Linker and Libraries Guide. ELF - basic structure ===================== The ELF files on the basic level are composed of 4 areas: - ELF header (at the beginning of the file): contains information about the parameters of the file and the machine it is intended for, as well as information about the position of section and program headers - section headers: each header describes the type and location of one section. A section is a contiguous block of memory with uniform attributes. Most sections simply describe a memory area that should be created when the program is started and initialized with data from the file, but there are many special types of sections with more complex semantics. - program headers: each header describes the type and location of one segment. A segment is a contiguous block of memory with uniform purpose and attributes from the point of view of loading and running the program. If the file has both segments and sections, the segments have a one-to-many relationship with the sections (because there may be many sections that the linker must distinguish, but the loader does not). - contents of sections / segments Whether sections / program headers may or must be present depends on the type of ELF file. There are 4 types of ELF files: ``ET_REL`` (relocatable file) A compiled, but not yet linked file (``.o``). Usually created as the result of compiling a single source file. It is impossible to run it directly -- these files are an intermediate stage of compilation and are combined by the linker (program ``ld``) into executable files or dynamic libraries. There is also an (uncommonly used) ability to combine several ``.o`` files into one larger file using ``ld -r``. As intermediate files, ``ET_REL`` can contain undefined symbols and undefined references -- they will be fixed up by further linking steps. In the ``ET_REL`` type, section headers are required and program headers are not used. ``ET_EXEC`` (executable file) A compiled and fully linked program, usually created by linking ``.o`` files through a linker. Such a file is ready to be launched -- all segments have a fixed address at which they will be available during the program's operation. All references in the file are also fixed -- the only exceptions are special types of references to shared libraries, limited to one segment. This ensures that almost all the memory content loaded from the executable file is identical in all processes executing the given program and allows for sharing the memory. In the ``ET_EXEC`` type, program headers are required. Section headers are not needed to run the program, but are used by debuggers and are usually included. ``ET_DYN`` (shared object file) A compiled and linked dynamic library (``.so``). Very similar to ``ET_EXEC``, but with the following differences: - although most of the content is already set (undefined references, like in ``ET_EXEC``, are limited to external references in one segment), the address at which the library will be loaded is not fixed -- the library can be loaded to any place in memory. - because the library code cannot contain references to its own address, a special code style is used, which is called PIC (Position-Independent Code). Whenever an address of an object in the library is needed, PIC code must somehow determine its own position and calculate the address of the desired object from it. This code is usually bigger and slower than "regular" code. - thanks to the above features, the program can load many dynamic libraries into its address space, and even load them during operation It should be noted that although the ``ET_DYN`` type is usually used for libraries, there is nothing to prevent it from being used for the main program as well -- this technique is called PIE (Position-Independent Executable) and is sometimes used because of the possibility of full randomization of the process address space. An example of an executable ``ET_DYN`` file is the libc library (``/lib/libc.so.6``) -- it prints its version information on startup. Also, the dynamic linker is implemented as an executable ``ET_DYN`` (to avoid address conflict with the program that loads). ``ET_CORE`` (core file) A process memory dump, created when a process is killed by certain signals. It contains the full state of the process at the time of death, allowing you to open it in the debugger and determine the cause of the problem. Interestingly, Linux kernel modules (``.ko``) are of the ``ET_REL`` type, and are directly loaded by the kernel -- the benefits of ``ET_EXEC`` and ``ET_DYN`` (i.e., shared memory) do not apply in kernel mode and their disadvantages (fixed position ``ET_EXEC``, PIC ineffectiveness) would be quite severe. ELF header ---------- The ELF header contains the following information: - file format identifier (``"\x7fELF"``) - file format: little endian or big endian, 32-bit or 64-bit -- determines the format of the remaining structures - ELF format version (only 1.0 has been used so far) - operating system identifier (often ignored) - ELF file type (``ET_*``) - target architecture (``EM_386``, ``EM_X86_64``, ``EM_SPARC``, ...) - location and size of section and program headers - address of the program entry point (for ``ET_EXEC`` and executable ``ET_DYN``) Sections -------- The information contained in a section header is: - section name (a section can have any name, but for standard sections it is customary to use names that begin with a period) - section type - section attributes - size, location in the file, and section alignment - for ``ET_EXEC`` and ``ET_DYN``: the final address of the section in memory (relative to the base address in the case of ``ET_DYN``) - associated section IDs (for some types) The section type determines most of its semantics. The more important types are: ``SHT_PROGBITS`` normal section, content loaded from a file ``SHT_NOBITS`` ordinary section, but the content is filled with zeros instead of being loaded from a file ``SHT_SYMTAB`` symbol table -- contains information about objects contained in the file and external objects to which this file has references ``SHT_STRTAB`` table of strings -- contains the names used by section headers and entries in the symbol table ``SHT_REL``/``SHT_RELA`` contains information about unknown references used in a given (affiliated) section ``SHT_DYNAMIC`` contains information for the dynamic linker The more important section attributes are: ``SHF_WRITE`` the section is writable at runtime ``SHF_EXECINSTR`` the section contains executable code ``SHF_ALLOC`` the section will be loaded into memory at runtime (sections without this flag are used only by build and debugging tools) The standard section names used for regular code in C are: ``.text`` code section ``.rodata`` read-only data section (``const int x = 3;``) ``.data`` data section (``int x = 3;``) ``.bss`` the zeroed data section (``int x = 0;``) Segments -------- The information contained in a program header is: - segment type - segment attributes - location of the segment in the file and its address in memory - size of the segment in the file and size of the segment in memory (if they are different, the remaining part is filled with zeros -- used for sections of the type ``SHT_NOBITS``) The more important types of segments are: ``PT_LOAD`` "regular" segment: loads the area into memory ``PT_DYNAMIC`` marks the area as containing information for the dynamic linker ``PT_INTERP`` indicates the file name of the dynamic linker to be used The only architecture-independent / system-independent attributes are the access rights (rwx). During linking, ``PT_LOAD`` segments are created by merging all sections with the ``SHF_ALLOC`` flag with compatible access rights. All other segments that are used at runtime are contained within ``PT_LOAD`` segments. Symbols and references ====================== One of the main tasks of the ELF format is storing information about objects contained in the file and about references to external objects. By object, we mean a function or a (global) variable. From the ELF point of view, an object is simply an area within a section (``ET_REL``) or the address space of a program (``ET_EXEC``, ``ET_DYN``). Symbols are names assigned to objects. The symbol can be defined (assigned to an object in a given file) or undefined (it will be defined at the moment of linking with the file that defines it). The symbols are stored in the symbol table. The information stored about a symbol is: - name - value: position in the section (``ET_REL``) or memory (``ET_EXEC``, ``ET_DYN``) - the containing section - size (the size of the variable or size of the function code); it can be zero if we're only interested in the address - type: ``STT_OBJECT`` a global variable ``STT_FUNC`` a function ``STT_SECTION`` a special symbol representing the beginning of the section (used for internal references) - linking rules: ``STB_LOCAL`` local symbol (``static`` in C) -- will not participate in linking ``STB_GLOBAL`` global symbol ``STB_WEAK`` weak global symbol (``__attribute__((weak))`` in gcc) -- a special variant of a global symbol that automatically "loses" to the usual global symbol with the same name when both are defined - visibility rules -- used to bind symbols between modules (a module is an executable program or a dynamic library): ``STV_DEFAULT`` default rules -- the symbol is visible and can be shadowed by a symbol with the same name from another module ``STV_PROTECTED`` the symbol is visible, but references to it from within the containing module will not be shadowed ``STV_HIDDEN`` the symbol is not visible from outside the module -- like ``STB_LOCAL``, but at the module level, not the source file level ``STV_INTERNAL`` like ``STV_HIDDEN``, but when the symbol is a function, we also assume that it will never be called from outside the module (which would be possible by passing the pointer). It can be used to further optimize PIC code. These rules can be set in gcc by the appropriate ``__attribute__``. Symbols can be used in the code by references (called relocations). Relocation is an information for the linker, that in a given place of the section, instead of the bytes set at the time of compilation, it should insert the address of a symbol (or some other value unknown at compile time). Relocations are stored in the relocation tables (one for each section that requires it). The information stored for each relocation is: - index of the referenced symbol in the symbol table - the relocation position in the section - type of relocation - addend: an additional component to the value -- the exact interpretation depends on the type of relocation, most often it is simply a number added to the relocated value. It can be used, for example, when someone asks for the address ``a.y``, when we have the definition ``struct {int x, y; } a;`` There are two types of relocation tables: ``SHT_REL`` and ``SHT_RELA``. For ``SHT_RELA``, the addend is stored in the relocation table, whereas for ``SHT_REL``, the addend is stored as the initial content of the relocated space. ``SHT_REL`` allows you to reduce the file size, but ``SHT_RELA`` is required for architectures with complex relocation types (e.g., two-part relocations of 16 bits each). The i386 architecture always uses ``SHT_REL``, and the x86_64 architecture always uses ``SHT_RELA``. Relocation types are very dependent on architecture. Most types of relocations are used for dynamic linking. The basic types of relocation on i386 are: ``R_386_32`` A 32-bit field is relocated, the relocated value is the address of the symbol + addend. For example, the following code:: extern struct { int x; int y; } a; a.y = 13; will look like this in assembly:: movl $13, a+4 which translates into machine code as follows:: c7 05 XX XX XX XX 0d 00 00 00 where ``XX XX XX XX`` should be replaced with the address of ``a + 4``. The assembler will save this in the ELF file section as:: c7 05 04 00 00 00 0d 00 00 00 And in the relocation table for this section, it will make a relocation of type ``R_386_32`` referencing the symbol ``a`` at position 2 within the section (assuming that this code is at the very beginning of the section). ``R_386_PC32`` A 32-bit field is relocated, the relocated value is the symbol address - field address + addend. This type of relocation is used for jumps and calls instructions (I remind you that in x86 jump and call statements the destination is stored as the difference between the jump instruction end address and the destination address). The following code:: extern void f (void); f(); which, in assembly, is:: call f will be saved in the machine code as:: e8 XX XX XX XX . where ``XX XX XX XX`` is (address of ``f`` - address of the instruction ``.``). file section this will be saved as:: e8 fc ff ff ff And in the relocation table there will be a relocation of the ``R_386_PC32`` type referencing the `f`` symbol at position 1. Please note that the assembler has set the relocation addend to ``0xfffffffc`` (i.e., -4) -- this is a correction included because ``R_386_PC32`` is defined as an offset from the beginning of the relocated field, and the jump instruction uses the offset from the end of the jump instruction, i.e., from the end of the relocated field. The basic types of relocation on x86_64 are: ``R_X86_64_64`` A 64-bit field is relocated, analogous to ``R_386_32``. ``R_X86_64_32S`` Like ``R_X86_64_64``, but a signed 32-bit field is relocated. If the full 64-bit value cannot be represented by this field, a linking error occurs. On the x86_64 architecture, most immediate parameters for instructions can only contain 32-bit signed numbers -- so long as the finished program fits into the lower 2GB of the address space, this type of relocation is used for most code references. If the program becomes too large, it must be compiled with the ``-mcmodel=large`` option, which uses only ``mov`` instructions to load addresses, supporting the full 64-bit range and using relocation type ``R_X86_64_64``. ``R_X86_64_PC32`` Analogous to ``R_386_PC32``. Function calling convention on x86 architecture on Linux ======================================================== (This is not really part of the topic, but it probably will be useful.) The i386 architecture basically has 7 general-purpose registers: ``%eax``, ``%ecx``, ``%edx``, ``%ebx``, ``%ebp``, ``%esi`` , ``%edi``. In addition, the ``%esp`` stack pointer and the ``%eflags`` register are also available from user programs. The x86_64 architecture expands all of these registers to 64-bits (``%rax``, ``%rcx``, ``%rdx``, ``%rbx``, ``%rbp``, ``%rsi``, ``%rdi``, ``%rsp``, ``%rflags``), and adds 8 new general-purpose registers (``%r8`` - ``%r15``). The standard calling conventions for i386 architecture are as follows: - the stack grows down, ``%esp`` indicates the top of the stack, which is the smallest address currently in use by the program. Any address on the stack smaller than ``%esp`` can be destroyed at any time (e.g., by calling a signal service function). - at the entry point to a function (i.e., immediately after executing the call instruction), ``%esp`` = -4 (mod 16), and the word at the top of the stack (at ``%esp``) is the return address from the function - the function should return by removing the return address from the stack (increasing ``%esp`` by 4) and jumping to it. This is usually done with the ``ret`` instruction. - contents of registers ``%ebx``, ``%ebp``, ``%esi``, ``%edi`` after returning from the function must be equal to their contents at the moment it was called -- the function must either save and restore the value of these registers, or not use them at all - the contents of the registers ``%eax``, ``%ecx``, ``%edx``, ``%eflags`` can be changed by a function without any consequences - if the function uses parameters, they will be passed on the stack, starting with ``%esp+4`` (i.e., immediately after the return address). The function is to leave them there -- only the return address is removed from the stack - if the function returns a value, it is stored in ``%eax``. And for x86_64: - the stack grows down, ``%rsp`` indicates the top of the stack. 128 bytes below the top of the stack constitute the so-called red zone, i.e., an area that can be used and will not be overwritten, despite being located below the stack (only the area below the red zone can be overwritten by a signal service). This area is useful in functions that do not call other functions (so-called leaf functions), because it avoids moving the stack pointer if the function does not need a lot of space. - at the entry point to a function, ``%rsp`` = 8 (mod 16), and the word at the top of the stack (at ``%rsp``) is the return address from the function. - the function should return by removing the return address from the stack (increasing ``%rsp`` by 8) and jumping to it. This is usually done with the ``ret`` instruction. - the content of the stack below ``%rsp`` at the entrance to the function can be freely modified by it, and the stack above should not be modified. - the contents of the registers ``%rbx``, ``%rbp``, ``%r12`` - ``%r15`` after returning from the function must be equal to their contents at the moment it was called - contents of registers ``%rax``, ``%rcx``, ``%rdx``, ``%rsi``, ``%rdi``, ``%r8`` - ``%r11`` can be changed by the function without any consequences - parameters to the function are passed in the following registers (in order): ``%rdi``, ``%rsi``, ``%rdx``, ``%rcx``, ``%r8``, ``%r9``. If the function takes more than 6 parameters, they are passed on the stack starting at ``%rsp+8``. The function is to leave them there -- only the return address is removed from the stack. - if the function returns a value, it returns it in ``%rax``. The above list does not include passing parameters and returning values other than ints / pointers or stranger x86 registers. For more details, I refer to psABI-i386 and psABI-x86_64. Dynamic libraries ================= Global Offset Table ------------------- As previously mentioned, ELF's main design goal for ``ET_EXEC`` and ``ET_DYN`` was the ability to share code and data between processes. Because external references (i.e., relocations) obviously require modification of the memory content in relation to the "template" contained in the file, it was decided to gather them into one place, limiting the number of pages of memory that cannot be shared. This place is called GOT (Global Offset Table). There is one GOT for each module (library or main program) that needs it. It is simply a large array of external symbol addresses required by a given module. When we write a dynamic library (and we use PIC), the compiler automatically generates code that loads the appropriate address from the GOT every time it needs the address of an external object. For ``ET_EXEC`` files, several tricks are used so that the compiler does not have to explicitly use the GOT, but the GOT is still used in some form for external function calls. The linker automatically creates GOT when linking a program or a dynamic library. A special relocation table is created in the ``.rel.dyn`` section, in which relocations that fill the GOT are stored (as well as all other relocations required during the dynamic linking process). These relocations are of the type ``R__GLOB_DAT``, which (in the case of x86) works identically to ``R_386_32`` or ``R_X86_64_64``, but additionally identifies the purpose of the relocation as a GOT slot fill. PIC on i386 ----------- The position-independent code sequences (PIC) are often tricky and their degree of complexity depends on the architecture. The i386 architecture is quite average in this respect -- relative jump instructions are available, but there are no other ways of addressing memory relative to the instruction pointer. The basic code sequences used in the i386 architecture are: - finding the GOT position:: call _l1 _l1: popl %ebx addl $_GLOBAL_OFFSET_TABLE_+(.-_l1), %ebx In this sequence, the ``call`` instruction is used to store address of the label ``_l1`` (i.e., the 'return' address) to the stack. This address is then removed from the stack, and the GOT address is obtained by adding the difference between the GOT address and the ``_l1`` address. The dot in the ``addl`` statement (denoting the address of the current instruction) is caused by historical reasons -- ``_GLOBAL_OFFSET_TABLE_`` is a special symbol understood by the assembler as (GOT address - address of the current instruction). Using this symbol also emits a special relocation ``R_386_GOTPC`` (works like `` R_386_PC32``, but uses the GOT address instead of the destination symbol). After the sequence has been executed, the GOT address is in the register ``%ebx``. This is the standard register for the GOT address -- according to the calling conventions, it must be set to the GOT address whenever a PLT call is made (see below). - Finding the address of the local variable (``static int x;``) (having already determined the GOT address):: leal x@gotoff(%ebx), %ecx Since most functions need to find the GOT anyway, the fact that local variables have a fixed offset from the GOT is used -- the address of the variable is simply found by adding this difference to the GOT address. ``x@gotoff`` is a special assembler syntax for this difference. This corresponds to the relocation ``R_386_GOTOFF`` (value = symbol address + addend - GOT address). - Finding the address of an external variable (``extern int x;``) (having already determined the GOT address):: movl x@got(%ebx), %ecx ``x@got`` is a special syntax denoting (address of ``x`` address in GOT - the GOT address). This instruction simply loads the contents of the appropriate GOT slot. ``x@got`` corresponds to the relocation ``R_386_GOT32``. Using this relocation automatically creates a slot in the GOT for the corresponding symbol. PIC on x86_64 ------------- The x86_64 architecture always allows the use of memory addressing relative to the instruction pointer. Thanks to this, you can avoid the trick code sequence looking for the GOT address and directly address slots in the GOT by offset from the instructions that use them. For example, finding the address of an external variable (``extern int x;``) looks like this:: movq x@GOTPCREL(%rip), %rax This corresponds to the relocation ``R_X86_64_GOTPCREL``. Moreover, in order to get to a local variable, you do not have to use the GOT in any way -- you have to encode an offset between the instruction and the given variable and use relative addressing. It uses the relocation ``R_X86_64_PC32``, the same one that is used by jump and call instructions. PLT --- As an optimization in relation to the above mechanisms, a special mechanism for calling external functions was created: PLT (Procedure Linkage Table), allowing lazy binding of functions by a dynamic linker. PLT is a special table containing (on x86) code instead of data. Each external function called through the PLT has an entry in the PLT. The entry for function ``f`` looks like this (i386):: f@plt: jmp *f_GOT_PLT_OFF(%ebx) f_unbound: pushl $f_REL_OFF jmp plt0 Or like this (x86_64):: f@plt: jmpq *f_GOT_PLT(%rip) f_unbound: pushq $f_REL_OFF jmp plt0 And ``plt0`` is a single special entry that looks like this:: pushq _GLOBAL_OFFSET_TABLE_+8(%rip) jmpq *_GLOBAL_OFFSET_TABLE_+16(%rip) Calling a function in PIC code looks like this:: call f@plt And, in the case of i386, it assumes that ``%ebx`` contains the GOT address. The mechanism works as follows: - ``f_GOT_PLT_OFF`` is an offset in the GOT of a special slot for the given PLT entry - this slot works like a regular GOT slot, but uses ``R__JMP_SLOT`` instead of ``R__GLOB_DAT``, and is initially set (via the linker) to the offset of the ``f_unbound`` label relative to the library base. What's more, the relocation ``R__JMP_SLOT`` is placed in a special, separate relocation table ``.rel.plt`` - the dynamic linker, seeing this type of relocation, initially fills this slot with the address of the ``f_unbound`` label by adding the base address of the library, instead of looking for the symbol ``f`` - When the program calls ``f@plt`` for the first time, the ``jmp`` statement will be executed with the contents of the slot, leading to the ``f_unbound`` label - the offset of the ``R__JMP_SLOT`` relocation corresponding to this slot inside the ``.rel.plt`` section is placed on the stack. - ``plt0`` code pushes the contents of a special GOT slot with offset 4 (or 8 on x86_64) onto the stack -- this slot is previously filled by the dynamic linker and contains some kind of identifying handle for the given module - the control is transferred to a special function from another special GOT slot with offset 8 (or 16) -- this slot is also already filled by the dynamic linker and contains the address of a special function that binds symbols at runtime - the dynamic linker, using the two parameters on the stack, determines what symbol it is, and where to enter its address, after which the GOT slot is refilled with the correct address and control is passed to the function ``f`` - when the program calls ``f@plt`` next time, the slot will already be filled and the control will go straight to the ``f`` function ET_EXEC -- special tricks for dynamic linking --------------------------------------------- To ensure that compilation of the main program (``ET_EXEC`` file) does not require any knowledge of the GOT / PLT mechanisms in the compiler, two additional tricks are used: - if the program refers to an external function symbol, a PLT entry for this function within the main program is automatically created, and the address of this PLT entry becomes the "official" address of this function inside the whole process (this is required to ensure the address of this function is fixed while linking the program code, and ``&f`` returns the same value throughout the program) - if the program refers to an external variable symbol, the linker automatically creates a copy of this variable in the main program data segment and emits to ``.rel.dyn`` a special relocation ``R__COPY``, which will copy the initial contents of this variable from the module that originally defined it. The created copy of the variable becomes the "official" location of this variable at runtime, and the original variable in the defining library is no longer used. _DYNAMIC structure ------------------ The ``_DYNAMIC`` structure is a table of key:value consisting of information about the contents of the module for the dynamic linker. It contains mainly: - address and size of the symbol table involved in dynamic binding - address and size of the ``.rel.dyn`` and ``.rel.plt`` tables - GOT address - list of libraries required by this module - a list of library search paths The linker finds the ``_DYNAMIC`` structure by looking for the ``PT_DYNAMIC`` segment. Program launch sequence and the dynamic linker ============================================== Running statically linked programs ---------------------------------- In the case of programs connected statically, the entire program initialization process is performed by the kernel. The kernel reads the ELF header, program headers, and loads all segments into memory. Then it creates the initial state of the program: - the main thread stack is allocated - on the main thread stack, the following things are placed: - program arguments (``argc``, ``argv``) - environmental variables (``environ``) - auxiliary vector (``auxv``) - the instruction pointer is set to the beginning of the program (from the ELF header). With the standard compilation process, this field is set by the linker to the address of the ``_start`` symbol - the program starts running Note that ``_start`` is not a function -- it does not use the standard parameter passing convention, nor can it return. The standard implementation of ``_start`` passes the parameters to the ``main()`` function, and then executes ``exit()`` with the value returned by ``main()`` as a parameter. Dynamic linker -------------- Running a dynamically linked program is much more complicated -- the kernel cannot do it in its entirety. Instead, a special program called a dynamic linker is used. This program is also known as ``ld.so`` (from the name of the file in which it was originally located). On i386 in Linux, the dynamic linker is in the file ``/lib/ld-linux.so.2``, and on x86_64 -- ``/lib64/ld-linux-x86-64.so.2``. The kernel recognizes dynamically linked programs by the presence of a ``PT_INTERP`` segment, which contains the name of the file containing the dynamic linker. When it finds such a segment, instead of passing the control to the program after loading it, it additionally loads and runs the indicated dynamic linker (which is a file of the ``ET_DYN`` type). The dynamic linker starts working by finding its own ``_DYNAMIC`` section and completing its own relocation. In the next phase, the linker looks through the auxiliary vector (``auxv``) provided by the kernel. This is a list of key:value pairs describing the state of the process and its environment. It contains, for example, information about the location of the program headers of the main executable file in memory. After locating the executable file, the linker loads (recursively) its dependencies. Then, the linker fills all relocations from ``.rel.dyn``, stuffs ``.rel.plt`` relocations with stubs, and finally transfers control to the main program (by executing the entry point indicated in its ELF header). The dynamic linker remains in memory after the program has been loaded, and it is possible to continue using its functions to open additional libraries, search for symbols, etc. using the ``dlopen``, ``dlsym`` and other functions. These functions are available by linking with the ``libdl`` library. Useful commands =============== Compilation of a source file in PIC mode:: gcc -c x.c -fPIC Note: there are two different options to enable PIC: ``-fpic`` and ``-fPIC``. Their exact meaning depends on the architecture. If they differ, ``-fpic`` uses shorter code sequences, but is limited to smaller programs (e.g., there is a limit of 1021 GOT entries on SPARC). In x86, both versions generate the same code. Compilation and linking of a dynamic library:: gcc x.c -o libx.so -Wl,-soname=libx.so -shared -fPIC Dump code, section table, symbols, and other data about a binary file:: objdump -xtrds Dump information about ELF structures:: readelf -a Dump the symbol table:: nm Dump the dynamic symbol table:: nm -D List of libraries used by the program:: ldd .. _elf_en_tls: Thread-Local Storage ==================== TLS is a fairly complex extension of ELF and C language. This is a mechanism that adds a new class of variables to the language (in addition to local and global variables): thread-local variables. They are declared like this (outside the scope of a function):: _Thread_local int x; Thread variables behave similarly to global variables, but each running thread has its own instance. The full implementation is quite complicated (due to the possibility of dynamic creation of both threads and modules defining thread-local variables), so we will limit ourselves here to the general outline. The thread-local variables are defined at the compilation stage in the sections ``.tdata`` and ``.tbss``, with the additional flag ``SHF_TLS``. The variables themselves have the ``STT_TLS`` type. When linking, all such sections in the module are collected in one place and a ``PT_TLS`` segment describing the place is created. During execution, the thread-local variables can be stored in one of two places: - main TLS block: contains all TLS segments belonging to the main program and libraries loaded with it - additional TLS blocks: contain other TLS segments (i.e., those from libraries loaded by ``dlopen``) The pointer to the list of additional blocks is stored in the main block for a given thread. The pointer to the main block is stored in a processor register (``%gs`` on i386, ``%fs`` on x86_64). Additional blocks are allocated lazily. There are 4 models of access to thread-local variables, used depending on the situation: - global dynamic: the most general, loads the handle of the library containing the symbol and offset of the variable in the TLS segment of this library from the GOT, calls ``___tls_get_addr`` to get the address of this segment (perhaps allocating it) - local dynamic: as global dynamic, but assumes that we are in the same module as the variable -- in the case of access to several variables, the address of the TLS segment is computed only once - initial executable: used in the general case in ``ET_EXEC`` programs and other situations that guarantee that the variable is in the main TLS block -- loads the variable offset in the main TLS block from the GOT - local executable: used in ``ET_EXEC`` programs to access their own variables -- simply stores the offset in the main TLS block into the program code Debugging and exception handling: DWARF ======================================= .. _elf_en_dwarf: DWARF ----- A format closely associated with ELF is the DWARF (Debugging With Attributed Record Formats) debugger information format. It defines many special sections (with names beginning with ``.debug``), which contain information useful for debugging the program, e.g. - information on line numbers (``.debug_loc``) - information about the stack frame format (``.debug_frame``) - information about the types and locations of variables (``.debug_info``) - information about the macros used in the program (``.debug_macro``) This information is included by the compiler only when prompted (``-g``). Exception handling, unwind mechanism ------------------------------------ One of the most complex mechanisms required for full C++ implementation is exception handling. This mechanism has three tasks: - go through the list of all stack frames with exception handlers - determine which one contains the correct service function - restore the state of the registers from the appropriate frame and call the exception handling code The stack unwinding mechanism was created to solve the first and third tasks. It uses the ``.eh_frame`` section, which is very similar (but not identical) to ``.debug_frame``, to go through the entire stack and recover the processor state information at the time of each call. Literature ---------- 1. Linker and Libraries Guide, 2004, Sun Microsystems (chapters 7 and 8) - http://docs.oracle.com/cd/E19683-01/817-3677/817-3677.pdf 2. gabi: http://www.uclibc.org/docs/SysV-ABI.pdf 3. psABI-i386a: http://www.uclibc.org/docs/psABI-i386.pdf 4. man dlsym, dlopen 5. ELF handling for thread local storage: www.akkadia.org/drepper/tls.pdf 6. The DWARF debugging standard: http://www.dwarfstd.org/