Geliştirici Araçları
TEKNİK REHBER GELİŞTİRİCİ ARAÇLARI BİNUTILS 2026

nm —
symbol listesi.

ELF sembol tablosunu oku, tür kodlarını anla, C++ isim bozulmalarını çöz. "undefined reference" link hatalarından ABI uyumluluk kontrolüne — nm ile sembol düzeyinde debug.

00 nm nedir — sembol tablosu temelleri

nm, ELF object dosyalarının, shared library'lerin ve çalıştırılabilir dosyaların sembol tablolarını listeler. GNU binutils ailesinin en çok kullanılan araçlarından biridir.

Bir sembol, kaynak koddaki her fonksiyon, global değişken ve extern bildirimdir. Derleyici her .o dosyasına bir sembol tablosu ekler; linker bu tabloları birleştirerek referansları çözümler. nm bu sürecin her aşamasında çalışır.

bash
# Temel kullanım
nm myapp
nm main.o
nm libsensor.so

# ARM cross:
arm-linux-gnueabihf-nm myapp

# C++ binary (demangling ile)
nm -C mycppapp
nm çıktısı — temel format
# Format: [adres] [tür] [sembol_adı]
00010454 T main
00010490 T sensor_read
00020000 D g_sensor_value
         U printf
         U __libc_start_main
0001049c t helper_static
00020004 B uninit_global

Adres sütunu büyük harf semboller için onaltılık bellek adresi, küçük harfler yerel (local) sembolleri gösterir. U (Undefined) sembollerin adresi yoktur — başka nesne dosyalarından gelecekler.

01 Sembol tür kodları: T U W w B D ve diğerleri

nm'nin tek harfli tür kodları, sembolün hangi section'da yaşadığını ve bağlama kurallarını özetler. Büyük harf = global, küçük harf = local.

T / t
Text section — çalıştırılabilir kod (.text). T = global fonksiyon, t = static fonksiyon.
D / d
Data section — başlangıç değeri olan global değişkenler (.data).
B / b
BSS section — sıfır başlangıç değerli global değişkenler (.bss). Dosyada yer kaplamaz.
R / r
Read-only data section — string literaller, const global'lar (.rodata).
U
Undefined — bu dosyada tanımlı değil; başka bir nesne ya da kütüphaneden gelecek.
W / w
Weak symbol — link sırasında override edilebilir. W = tanımlı weak, w = tanımsız weak (hata vermez).
V / v
Weak object — weak referanslı nesne (C++ virtual table öğeleri gibi).
A
Absolute — adresi değişmez; linker script'te tanımlanan sabitler.
C
Common — tanımsız (uninitialized) global; link sonrası .bss veya .data'ya atanır.
I
Indirect function — GNU ifunc, çalışma zamanında fonksiyon adresi belirlenir (glibc optimizasyonları).
bash — tür bazlı filtreleme
# Sadece global fonksiyonlar (T tipli)
nm myapp | grep ' T '

# Tanımsız semboller (U tipli) — link bağımlılıkları
nm myapp | grep ' U '

# Weak semboller
nm myapp | grep -E ' [Ww] '

# BSS semboller (sıfır başlangıçlı global'lar)
nm myapp | grep ' B ' | sort -k1

02 -a, -u, -D bayrakları

nm'nin en sık kullanılan bayrakları: tüm sembolleri görme, sadece tanımsızları listeleme ve dinamik sembol tablosunu okuma.

bash
# -a: hata ayıklama sembollerini de dahil et (genellikle önemsizler)
nm -a myapp | head -20

# -u / --undefined-only: sadece tanımsız semboller
nm -u myapp
# Hangi fonksiyonları dışarıdan aldığını öğrenmek için ideal

# -D / --dynamic: dinamik sembol tablosunu oku (.dynsym)
# Strip edilmiş binary'lerde tek çalışan seçenek
nm -D /usr/lib/libpthread.so.0 | head -20

# -D ile bir kütüphanenin dışa aktardığı API'yi gör
nm -D libmysensor.so | grep ' T '
nm -u çıktısı
                 U __cxa_atexit@@GLIBC_2.4
                 U __libc_start_main@@GLIBC_2.4
                 U malloc@@GLIBC_2.4
                 U free@@GLIBC_2.4
                 U printf@@GLIBC_2.4
                 U pthread_create@@GLIBC_2.4
                 U sensor_driver_init
# @@ sonrası versiyon bağımlılığını gösterir
# sensor_driver_init: libsensor.so'dan gelmeli — bulunamamışsa link hatası

03 -C — C++ demangling

C++ derleyicileri, fonksiyon aşırı yüklemesini (overloading) ve namespace'leri kodlamak için sembol adlarını bozar (name mangling). -C bu bozulmayı çözer.

bash
# C++ binary'de mangled semboller
nm mycppapp | grep "_Z"
# 00010490 T _ZN6Sensor4readEi
# 000104d0 T _ZN6Sensor5resetEv
# 00010510 T _ZN6SensorC1Eii

# -C ile demangled
nm -C mycppapp | grep -v "^[0-9a-f]* [a-z] "
# 00010490 T Sensor::read(int)
# 000104d0 T Sensor::reset()
# 00010510 T Sensor::Sensor(int, int)

# c++filt ile bağımsız demangling
echo "_ZN6Sensor4readEi" | c++filt
# Sensor::read(int)
İPUCU — extern "C"

C++ kodundan C kütüphaneleriyle bağlantı kurmak için extern "C" blokları kullanılır. Bu blok içindeki fonksiyonlar mangling uygulanmadan düz isimle sembol tablosuna girer. nm -C çıktısında extern "C" fonksiyonları C gibi görünür.

04 --defined-only ve --undefined-only

Bu iki bayrak, nesne dosyaları ve kütüphaneler arasındaki API sınırlarını netleştirmek için kullanılır.

bash
# Bu kütüphanenin dışa aktardığı (defined) semboller
nm --defined-only libsensor.so | grep ' T '
# 00001234 T sensor_init
# 00001290 T sensor_read
# 000012d0 T sensor_close

# Bu binary'nin dışarıya bağımlı olduğu semboller
nm --undefined-only myapp
# Bağlama sırasında çözülmesi gereken her şey

# İki kütüphanenin ortak dışa aktardığı semboller (çakışma kontrolü)
comm -12 \
  <(nm --defined-only liba.so | awk '{print $3}' | sort) \
  <(nm --defined-only libb.so | awk '{print $3}' | sort)
# Çıktı: her iki kütüphanede de tanımlı semboller — potansiyel çakışma!

05 Sembol boyutu ve sıralama

nm ile sembolleri boyutlarına göre sıralayarak en büyük fonksiyonları veya değişkenleri tespit edebilirsiniz. Flash/RAM kullanım optimizasyonu için çok faydalıdır.

bash
# -S: boyut sütununu göster
nm -S myapp | head -20
# 00010454 0000003c T main
# 00010490 000000a8 T sensor_read   <-- 168 bayt
# 00010538 00000024 T sensor_close

# Büyükten küçüğe sırala (en büyük fonksiyon en üstte)
nm -S --size-sort myapp | grep ' T ' | tail -20

# Özellikle firmware boyut optimizasyonunda kritik:
arm-linux-gnueabihf-nm -S --size-sort firmware.elf | \
  grep ' T ' | tail -30
# En büyük 30 fonksiyonu gösterir — bunları küçültmek büyük kazanç sağlar

# BSS kullanımı: en büyük global değişkenler
nm -S --size-sort myapp | grep ' B ' | tail -20
DİKKAT — boyut sütunu

nm'nin boyut sütunu (-S) her zaman güvenilir değildir: bazı derleyici sürümleri fonksiyon boyutlarını hatalı raporlar. Kesin boyut için readelf -s veya size komutunu da kontrol edin.

06 nm vs readelf -s farkı

nm ve readelf -s aynı sembol tablosunu farklı biçimlerde sunar. Hangisini ne zaman kullanmalısınız?

bash — karşılaştırma
# nm çıktısı — kısa, insan dostu
nm myapp | head -5
# 00010454 T main
# 00010490 T sensor_read
# 00020000 D g_value
#          U printf

# readelf -s çıktısı — ayrıntılı, tüm alanlar
readelf -s myapp | head -10
# Num:  Value  Size Type   Bind  Vis   Ndx  Name
#  48: 00010454  60  FUNC  LOCAL DEFAULT  3  main
#  49: 00010490 168  FUNC  GLOBAL DEFAULT 3  sensor_read
nm kullan
Hızlı filtreleme ve grep için. Shell scriptlerinde daha kolay parse edilir. -C ile C++ demangling.
readelf -s kullan
Tam sembol bilgisi (Bind, Vis, Ndx, Size) gerektiğinde. BFD olmadan doğrudan ELF parse etmek için. Visibility kontrolü (DEFAULT/HIDDEN/PROTECTED).

07 Pratik: link hataları debug + ABI uyumluluk kontrolü

Gerçek dünya senaryoları: "undefined reference" link hatalarını nm ile bulmak ve iki kütüphane versiyonu arasında ABI kırılıp kırılmadığını kontrol etmek.

Senaryo 1: undefined reference hatasını debug etmek

bash
# Link hatası:
# main.o: undefined reference to `sensor_driver_init'

# 1. Hatayı veren object dosyasının hangi sembolleri istediğini gör
nm -u main.o
#          U sensor_driver_init   <-- bunu arıyoruz

# 2. Hangi kütüphane/object bu sembolü sağlıyor?
nm --defined-only libsensor.so | grep "sensor_driver_init"
# 00001234 T sensor_driver_init   <-- burada!

# 3. Ama belki imla farklıdır?
nm --defined-only libsensor.so | grep -i "sensor"
# 00001200 T Sensor_driver_Init   <-- büyük harf farkı!

# 4. Kütüphane link satırına eklendi mi?
gcc main.o -lsensor -o myapp        # doğru
gcc main.o -o myapp -lsensor        # yanlış sıra — GNU ld'de sorun çıkabilir

# 5. Birden fazla .a dosyasında ara
for lib in /usr/local/lib/*.a; do
    result=$(nm --defined-only "$lib" | grep "sensor_driver_init")
    [ -n "$result" ] && echo "$lib: $result"
done

Senaryo 2: ABI uyumluluk kontrolü

bash
# libsensor.so.1 ile libsensor.so.2 arasında hangi semboller kayboldu?

# v1 dışa aktarımları
nm --defined-only -D libsensor.so.1 | \
  awk '{print $3}' | sort > /tmp/symbols_v1.txt

# v2 dışa aktarımları
nm --defined-only -D libsensor.so.2 | \
  awk '{print $3}' | sort > /tmp/symbols_v2.txt

# v1'de olan ama v2'de olmayan semboller (kırılan API!)
comm -23 /tmp/symbols_v1.txt /tmp/symbols_v2.txt
# sensor_legacy_read   <-- v2'de kaldırıldı → ABI kırıldı!

# v2'de eklenen yeni semboller (ekleme sorun değil)
comm -13 /tmp/symbols_v1.txt /tmp/symbols_v2.txt
# sensor_read_async
# sensor_get_metadata

Senaryo 3: Embedded — firmware'de hangi fonksiyonlar kullanılıyor?

bash
# Firmware.elf boyut raporu
arm-none-eabi-nm --size-sort -S firmware.elf | \
  grep ' T ' | sort -rn | head -20

# RAM kullanımı: .data + .bss
arm-none-eabi-nm -S firmware.elf | grep -E ' [BD] ' | \
  awk '{sum += strtonum("0x"$2)} END {printf "RAM: %d bytes\n", sum}'

# Flash kullanımı: .text + .rodata
arm-none-eabi-nm -S firmware.elf | grep -E ' [TR] ' | \
  awk '{sum += strtonum("0x"$2)} END {printf "Flash: %d bytes\n", sum}'