ELF Format Overview
ELF: Executable and Linkable Format is the standard binary format on Linux/Unix. It describes object files (.o), executables, and shared libraries (.so).
# View ELF header
readelf -h program
# View sections
readelf -S program
# View program headers
readelf -l program
# View symbols
readelf -s program
ELF Sections
.text Section (Code)
section .text
global _start
_start:
; Executable code goes here
; Read-only at runtime
.data & .rodata Sections
section .data ; Initialized read-write data
counter dq 0
message db "Hello", 0
section .rodata ; Initialized read-only data
const_pi dq 3.14159
fmt db "Result: %d", 10, 0
.bss Section
section .bss ; Uninitialized data (zero-filled)
buffer resb 1024 ; Reserve 1024 bytes
array resq 100 ; Reserve 100 quadwords
Symbol Tables
# View symbol table
nm program
objdump -t program
# Symbol types:
# T/t = text (code)
# D/d = initialized data
# B/b = BSS (uninitialized)
# U = undefined (external reference)
Relocation
Relocation is how the linker patches addresses in object files to create a runnable executable:
# View relocation entries
readelf -r program.o
objdump -r program.o
# Common relocation types (x86-64):
# R_X86_64_64 - Absolute 64-bit address
# R_X86_64_PC32 - PC-relative 32-bit (RIP-relative)
# R_X86_64_PLT32 - PLT entry for function call
# R_X86_64_GOTPCREL - GOT entry, PC-relative
Position-Independent Code (PIC)
; Non-PIC (absolute addressing)
mov rax, [my_var] ; Needs relocation at runtime
; PIC (RIP-relative) - preferred for shared libraries
mov rax, [rel my_var] ; Uses RIP-relative addressing
lea rdi, [rel my_var] ; Get address of my_var (PIC)
; For external/global data in shared libraries, use GOT:
mov rax, [rel external_var wrt ..got] ; Load via GOT
Why PIC? Position-independent code can load at any address, enabling shared libraries, ASLR (Address Space Layout Randomization), and memory sharing between processes.
Static Linking
Static linking combines all object files into one standalone executable:
# Create static library from object files
ar rcs libmylib.a file1.o file2.o file3.o
# Link with static library
ld -o program main.o -L. -lmylib
# Or with gcc:
gcc -static -o program main.o -L. -lmylib
# View archive contents
ar -t libmylib.a
# Extract member
ar -x libmylib.a file1.o
Static Link Process
1. Collect all object files and libraries
2. Resolve symbol references
3. Merge sections (.text, .data, .bss)
4. Apply relocations (patch addresses)
5. Write executable with all code embedded
Pros: No runtime dependencies, faster startup
Cons: Larger executables, no library updates without recompile
Dynamic Linking
Dynamic linking defers symbol resolution until runtime:
GOT (Global Offset Table):
- Table of pointers to global data/functions
- Filled in by dynamic linker at load time
- Code references GOT entries, not absolute addresses
PLT (Procedure Linkage Table):
- Stub code for calling external functions
- First call: resolve symbol, patch GOT, call function
- Subsequent calls: jump directly via GOT (fast path)
call printf@PLT ; First call flow:
└─→ PLT stub: jmp [GOT entry]
└─→ GOT initially points back to PLT
└─→ PLT: push index; jmp dynamic_linker
└─→ Linker resolves, patches GOT
└─→ Function runs
; Calling external function (dynamic)
section .text
extern printf
global _start
_start:
lea rdi, [rel fmt]
xor eax, eax ; No float args
call printf wrt ..plt ; Call via PLT
; Or let NASM figure it out:
call printf ; NASM generates PLT reference
# View dynamic symbols
readelf -d program # Dynamic section
readelf --dyn-syms prog # Dynamic symbol table
objdump -R program # Dynamic relocations
ldd program # List shared library dependencies
Creating Shared Libraries
# Assemble as PIC (position-independent)
nasm -f elf64 -o mylib.o mylib.asm
# Create shared library (.so)
ld -shared -o libmylib.so mylib.o
# Or with gcc (adds libc):
gcc -shared -o libmylib.so mylib.o
# Link against shared library
ld -o program main.o -L. -lmylib -dynamic-linker /lib64/ld-linux-x86-64.so.2
# Run with library path
LD_LIBRARY_PATH=. ./program
# Install system-wide
sudo cp libmylib.so /usr/local/lib/
sudo ldconfig # Update library cache
Exported Symbols
; mylib.asm - Shared library source
global my_function:function ; Export as function
global my_variable:data ; Export as data
section .data
my_variable: dd 42
section .text
my_function:
mov eax, [rel my_variable]
ret
Exercise: Create and Use a Shared Library
- Write
mathlib.asm with add_numbers and multiply_numbers functions
- Create
libmath.so shared library
- Write a main program that calls both functions
- Link and run with
LD_LIBRARY_PATH