00 ELF format anatomy — magic bytes ve yapı
ELF (Executable and Linkable Format), Linux'ta çalıştırılabilir dosyalar, shared library'ler (.so) ve nesne dosyaları (.o) için standart ikili formattır. readelf bu formatı human-readable şekilde sunar.
Her ELF dosyası ELF header ile başlar (52 bayt 32-bit, 64 bayt 64-bit). Ardından isteğe bağlı olarak program header tablosu (runtime loader için) ve section header tablosu (linker için) gelir. Bu iki tablo birbirinden bağımsızdır: çalıştırılabilir dosyalarda ikisi de bulunur, paylaşımlı kütüphanelerde ikisi de, .o dosyalarında yalnızca section header tablosu vardır.
Magic bytes — her ELF dosyasının ilk 4 baytı
# xxd ile ilk 16 bayta bak
xxd myapp | head -1
# 00000000: 7f45 4c46 0101 0100 0000 0000 0000 0000 .ELF............
# ^^^^^^^ 7f 'E' 'L' 'F' = Magic
# Byte 4: 01=32-bit, 02=64-bit
# Byte 5: 01=little-endian, 02=big-endian
# Byte 6: ELF version (her zaman 1)
readelf yalnızca ELF formatını okur; BFD (Binary File Descriptor) kütüphanesini kullanmaz. Bu nedenle objdump'tan daha güvenilir ve deterministtir — özellikle alışılmadık ELF dosyalarında. Ayrıca readelf her zaman ham ELF görünümünü sunarken objdump bazen soyutlama ekler.
01 -h — file header
ELF file header, dosyanın türü, hedef mimarisi, entry point adresi ve header tablosu konumları gibi temel meta verileri içerir.
readelf -h myapp
ELF Header:
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: ARM
Version: 0x1
Entry point address: 0x10455
Start of program headers: 52 (bytes into file)
Start of section headers: 28916 (bytes into file)
Flags: 0x5000400, Version5 EABI, hard-float ABI
Size of this header: 52 (bytes)
Size of program headers: 32 (bytes)
Number of program headers: 9
Size of section headers: 40 (bytes)
Number of section headers: 30
Section header string table index: 29
Önemli alanlar
02 -S — section headers (flags: AX, WA)
Section header tablosu, linker'ın ve debug araçlarının kullandığı mantıksal bölümleri listeler. Her section'ın adresi, boyutu ve flag'leri mevcuttur.
readelf -S myapp
# Geniş çıktı için --wide ekle
readelf -SW myapp
There are 30 section headers, starting at offset 0x70f4:
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .interp PROGBITS 00010154 000154 000013 00 A 0 0 1
[ 2] .note.ABI-tag NOTE 00010168 000168 000020 00 A 0 0 4
[ 3] .text PROGBITS 00010454 000454 003a4c 00 AX 0 0 4
[ 4] .rodata PROGBITS 00013ea0 003ea0 0004f8 00 A 0 0 4
[ 5] .data PROGBITS 00020000 004000 000124 00 WA 0 0 4
[ 6] .bss NOBITS 00020124 004124 0008a0 00 WA 0 0 8
[ 7] .debug_info PROGBITS 00000000 004124 012345 00 0 0 1
[ 8] .symtab SYMTAB 00000000 0164c0 001234 10 29 50 4
[ 9] .strtab STRTAB 00000000 0176f4 000890 00 0 0 1
Flag anlamları
03 -l — program headers (LOAD / PT_DYNAMIC)
Program header tablosu, runtime loader'ın (ld.so) dosyayı belleğe nasıl yükleyeceğini tanımlar. Section header'lardan farklı olarak çalışma zamanına yöneliktir.
readelf -l myapp
Elf file type is EXEC (Executable file)
Entry point 0x10455
There are 9 program headers, starting at offset 52
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
PHDR 0x000034 0x00010034 0x00010034 0x00120 0x00120 R 0x4
INTERP 0x000154 0x00010154 0x00010154 0x00013 0x00013 R 0x1
[Requesting program interpreter: /lib/ld-linux-armhf.so.3]
LOAD 0x000000 0x00010000 0x00010000 0x043f8 0x043f8 R E 0x10000
LOAD 0x004000 0x00020000 0x00020000 0x00124 0x009c4 RW 0x10000
DYNAMIC 0x004000 0x00020000 0x00020000 0x000f8 0x000f8 RW 0x4
NOTE 0x000168 0x00010168 0x00010168 0x00044 0x00044 R 0x4
GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RW 0x10
GNU_RELRO 0x004000 0x00020000 0x00020000 0x00100 0x00100 R 0x1
ARM_EXIDX 0x003ea0 0x00013ea0 0x00013ea0 0x000b8 0x000b8 R 0x4
Segment tipleri
/lib/ld-linux-armhf.so.3. Statik binary'lerde bu segment yoktur.04 -s — symbol tables
readelf -s hem statik sembol tablosunu (.symtab) hem de dinamik sembol tablosunu (.dynsym) gösterir.
# Her iki sembol tablosu
readelf -s myapp
# Sadece dinamik semboller (strip sonrası da kalır)
readelf --dyn-syms myapp
# Belirli bir sembolü ara
readelf -s myapp | grep "sensor_read"
Symbol table '.symtab' contains 156 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 00000000 0 NOTYPE LOCAL DEFAULT UND
1: 00010154 0 SECTION LOCAL DEFAULT 1
48: 00010454 60 FUNC LOCAL DEFAULT 3 main
49: 00010490 168 FUNC GLOBAL DEFAULT 3 sensor_read
50: 00020000 4 OBJECT GLOBAL DEFAULT 5 g_sensor_value
51: 00000000 0 FUNC GLOBAL DEFAULT UND printf
52: 00000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main
Symbol table '.dynsym' contains 12 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 00000000 0 NOTYPE LOCAL DEFAULT UND
1: 00000000 0 FUNC GLOBAL DEFAULT UND printf@GLIBC_2.4
2: 00010490 168 FUNC GLOBAL DEFAULT 3 sensor_read
.dynsym, dinamik bağlama sırasında çözülecek sembolleri içerir ve strip sonrası da korunur. .symtab ise tüm sembolleri içerir; strip komutu bunu kaldırır.
05 -d — dynamic section (NEEDED / SONAME / RPATH)
Dynamic section, çalışma zamanında dinamik linker'ın ihtiyaç duyduğu tüm bilgileri içerir: bağımlı kütüphaneler, arama yolları, sembol tablosu adresleri.
readelf -d myapp
Dynamic section at offset 0x4000 contains 26 entries:
Tag Type Name/Value
0x00000001 (NEEDED) Shared library: [libpthread.so.0]
0x00000001 (NEEDED) Shared library: [libm.so.6]
0x00000001 (NEEDED) Shared library: [libc.so.6]
0x0000000e (SONAME) Library soname: [libmysensor.so.1]
0x0000000f (RPATH) Library rpath: [/opt/sysroot/arm-linux-gnueabihf/lib]
0x00000005 (STRTAB) 0x10230
0x00000006 (SYMTAB) 0x101b0
0x0000000a (STRSZ) 312 (bytes)
0x0000000b (SYMENT) 16 (bytes)
0x00000015 (DEBUG) 0x0
0x00000003 (PLTGOT) 0x20090
0x00000002 (PLTRELSZ) 64 (bytes)
0x00000017 (JMPREL) 0x103a8
0x00000000 (NULL) 0x0
Kritik tag'ler
libfoo.so.1.2.3 dosyası libfoo.so.1 SONAME'iyle yayınlanabilir.06 -r — relocations
Relocation tablosu, linker'ın veya dinamik yükleyicinin çözümlemesi gereken sembol referanslarını listeler.
# Tüm relocation'ları göster
readelf -r myapp
# Object dosyasındaki relocation'lar
readelf -r main.o
Relocation section '.rel.plt' at offset 0x3a8 contains 4 entries:
Offset Info Type Sym.Value Sym. Name
00020090 00000116 R_ARM_JUMP_SLOT 00000000 printf
00020094 00000216 R_ARM_JUMP_SLOT 00000000 malloc
00020098 00000316 R_ARM_JUMP_SLOT 00000000 free
0002009c 00000416 R_ARM_JUMP_SLOT 00000000 __libc_start_main
Relocation section '.rel.dyn' at offset 0x3c8 contains 2 entries:
Offset Info Type Sym.Value Sym. Name + Addend
00020004 00000115 R_ARM_GLOB_DAT 00000000 __gmon_start__
R_ARM_JUMP_SLOT: PLT (Procedure Linkage Table) girişleri — lazy binding için. R_ARM_GLOB_DAT: GOT (Global Offset Table) girişleri — global değişkenler için.
07 Pratik: shared lib bağımlılık analizi + stripped binary
Embedded sistemde yaygın iki senaryo: "hangi shared library'lere bağımlı?" ve "strip edilmiş binary'de sembol var mı?"
Senaryo 1: shared library bağımlılık ağacı
# Doğrudan bağımlılıkları listele
readelf -d /usr/bin/python3 | grep NEEDED
# (NEEDED) libpthread.so.0
# (NEEDED) libdl.so.2
# (NEEDED) libutil.so.1
# (NEEDED) libm.so.6
# (NEEDED) libc.so.6
# ldd ile tüm transitif bağımlılıklar (sysroot ile çalışmaz!)
ldd /usr/bin/python3
# Cross ARM için: sysroot içindeki binary
readelf -d /opt/sysroot/usr/bin/myapp | grep -E "NEEDED|RPATH|RUNPATH"
# Rootfs'te eksik kütüphane tespiti
SYSROOT=/path/to/rootfs
readelf -d "$SYSROOT/usr/bin/myapp" | grep NEEDED | \
awk '{print $5}' | tr -d '[]' | while read lib; do
find "$SYSROOT" -name "$lib" 2>/dev/null || echo "EKSİK: $lib"
done
Senaryo 2: stripped binary'de sembol durumu
# Strip edilmiş mi? file komutu ile kontrol
file /usr/bin/ls
# ELF 64-bit LSB pie executable, x86-64, ..., stripped
# readelf ile sembol tablosu durumu
readelf -S /usr/bin/ls | grep -E "symtab|dynsym"
# [11] .dynsym DYNSYM ... (strip sonrası hâlâ mevcut)
# .symtab yoktur — strip kaldırdı
# Dinamik semboller hâlâ görülebilir
readelf --dyn-syms /usr/bin/ls | head -20
# ELF version bilgisi (GLIBC_2.x bağımlılığı)
readelf -V /usr/bin/ls
# Version needs section '.gnu.version_r' ...
# GLIBC_2.34 (libc.so.6)
# GLIBC_2.33 (libc.so.6)
# Embedded hedef farklı GLIBC sürümü kullanıyorsa çakışma yaşanır!
# Çözüm: musl libc, statik derleme veya sysroot ile uyumlu cross-compile
ARM ABI kontrolü
# Hard-float / soft-float karışımı sorun yaratır
readelf -h libsensor.so | grep Flags
# Flags: 0x5000400, Version5 EABI, hard-float ABI
readelf -h myapp | grep Flags
# Flags: 0x5000200, Version5 EABI, soft-float ABI <-- UYUMSUZ!
# Doğru eşleşme: her ikisi de hard-float veya her ikisi de soft-float olmalı
# ARM hard-float: -mfloat-abi=hard, -mfpu=neon-vfpv4
# ARM soft-float: -mfloat-abi=soft veya softfp