00 GDB Python API temelleri
GDB, Python 3 API'si aracılığıyla tam programlanabilir bir debug ortamı sunar. Özel komutlar, otomatik analiz, olay tabanlı eylemler ve pretty-printer'lar bu API ile uygulanır.
gdb module'ü
# GDB Python etkileşimli oturumu
# GDB içinden: python import gdb
import gdb
# GDB komutunu çalıştır ve çıktıyı al
output = gdb.execute("info registers", to_string=True)
print(output)
# İfade değerlendir
val = gdb.parse_and_eval("sizeof(struct task_struct)")
print(f"task_struct boyutu: {int(val)} bayt")
# Değişken değeri oku
sp_val = gdb.parse_and_eval("$sp")
print(f"Stack pointer: 0x{int(sp_val):016x}")
# Sembol adresini bul
sym, is_field = gdb.lookup_symbol("main")
if sym:
print(f"main adresi: {sym.value().address}")
# Tüm frame'leri dolaş
frame = gdb.newest_frame()
while frame:
print(f" #{frame.level()} {frame.name()} @ {frame.pc():#x}")
frame = frame.older()
Olay kancaları (event hooks)
import gdb
def on_stop(event):
"""Hedef her durduğunda çağrılır"""
if isinstance(event, gdb.SignalEvent):
print(f"[!] Sinyal: {event.stop_signal}")
elif isinstance(event, gdb.BreakpointEvent):
bps = event.breakpoints
print(f"[*] Breakpoint: {[bp.location for bp in bps]}")
# O anki frame'i yazdır
frame = gdb.selected_frame()
print(f" PC=0x{frame.pc():x} {frame.name()}()")
def on_cont(event):
"""Hedef çalışmaya devam ettiğinde"""
print("[>] Devam ediyor...")
def on_exited(event):
"""Süreç sonlandığında"""
if hasattr(event, 'exit_code'):
print(f"[X] Çıkış kodu: {event.exit_code}")
# Kancaları bağla
gdb.events.stop.connect(on_stop)
gdb.events.cont.connect(on_cont)
gdb.events.exited.connect(on_exited)
print("Olay kancaları kuruldu")
.gdbinit otomatik yükleme
# ~/.gdbinit veya proje yerel .gdbinit
# python script dosyasını yükle:
# (gdb) source /path/to/my_helpers.py
# .gdbinit'e otomatik yükleme:
python
import sys, os
sys.path.insert(0, os.path.expanduser('~/.gdb'))
import my_helpers
end
01 Pretty-printer yazma
Pretty-printer'lar, GDB'nin ham bellek görünümü yerine veri yapılarını okunabilir biçimde göstermesini sağlar. C++ STL, Qt ve özel struct'lar için vazgeçilmezdir.
Basit pretty-printer
import gdb
import gdb.printing
class SensorDataPrinter:
"""struct SensorData için pretty-printer"""
def __init__(self, val):
self.val = val
def to_string(self):
"""Tek satır özet gösterim"""
channel = int(self.val['channel'])
raw = int(self.val['raw_value'])
unit = self.val['unit'].string()
# Ham değeri fiziksel değere çevir
physical = raw * 0.001
return (f"SensorData {{ channel={channel}, "
f"value={physical:.3f} {unit}, "
f"raw=0x{raw:04x} }}")
def children(self):
"""Alt alanları döndür (expand edildiğinde görünür)"""
yield ('channel', self.val['channel'])
yield ('raw_value', self.val['raw_value'])
yield ('unit', self.val['unit'])
yield ('timestamp', self.val['timestamp_ms'])
yield ('valid', self.val['is_valid'])
class SensorBufferPrinter:
"""struct SensorBuffer (dairesel tampon) printer"""
def __init__(self, val):
self.val = val
def to_string(self):
head = int(self.val['head'])
tail = int(self.val['tail'])
size = int(self.val['size'])
count = (head - tail) % size
return f"SensorBuffer [{count}/{size} öğe]"
def children(self):
head = int(self.val['head'])
tail = int(self.val['tail'])
size = int(self.val['size'])
buf = self.val['data']
idx = tail
i = 0
while idx != head:
yield (f'[{i}]', buf[idx])
idx = (idx + 1) % size
i += 1
def display_hint(self):
return 'array' # GDB bu bilgiyle array gibi gösterir
def build_sensor_printers():
"""Printer kaydedici"""
pp = gdb.printing.RegexpCollectionPrettyPrinter("sensor")
pp.add_printer('SensorData', '^SensorData$', SensorDataPrinter)
pp.add_printer('SensorBuffer', '^SensorBuffer$', SensorBufferPrinter)
return pp
# Globale kaydet — tüm inferiorlarda aktif
gdb.printing.register_pretty_printer(
None, # None = tüm objfiler
build_sensor_printers(),
replace=True
)
Kullanım
(gdb) source sensor_printers.py
(gdb) p sensor
$1 = SensorData { channel=2, value=23.450 °C, raw=0x5C3A }
(gdb) p sensor_buf
$2 = SensorBuffer [5/16 öğe]
# Alt alanları göster
(gdb) p sensor.channel
$3 = 2
# STL printer örneği (libstdc++ ile hazır gelir)
(gdb) p my_vector
$4 = std::vector of length 3, capacity 4 = {1, 2, 3}
02 Custom GDB komutu
GDB Python API'si ile özel komutlar tanımlanabilir. Bu komutlar, gdb.Command sınıfından türetilir ve invoke() metodu ile çalışır.
Basit komut
import gdb
import argparse
class SensorDumpCommand(gdb.Command):
"""
sensor-dump [kanal] — Sensör değerlerini güzel biçimde yazdır.
Kullanım:
sensor-dump # tüm kanallar
sensor-dump 2 # sadece kanal 2
sensor-dump --raw # ham değerler
"""
def __init__(self):
super().__init__("sensor-dump",
gdb.COMMAND_USER,
gdb.COMPLETE_NONE)
def invoke(self, args_str, from_tty):
# argparse ile argümanları ayrıştır
parser = argparse.ArgumentParser(prog='sensor-dump',
add_help=False)
parser.add_argument('channel', nargs='?', type=int,
default=None)
parser.add_argument('--raw', action='store_true')
try:
args = parser.parse_args(args_str.split() if args_str else [])
except SystemExit:
return
# Global sensör dizisini oku
try:
sensors = gdb.parse_and_eval("g_sensors")
count = int(gdb.parse_and_eval("g_sensor_count"))
except gdb.error as e:
gdb.write(f"Hata: {e}\n")
return
gdb.write(f"{'Ch':>3} {'Raw':>8} {'Value':>10} {'Unit'}\n")
gdb.write("-" * 35 + "\n")
for i in range(count):
s = sensors[i]
ch = int(s['channel'])
raw = int(s['raw_value'])
unit = s['unit'].string()
if args.channel is not None and ch != args.channel:
continue
if args.raw:
gdb.write(f"{ch:>3} {raw:#010x}\n")
else:
phys = raw * 0.001
gdb.write(f"{ch:>3} {raw:#010x} {phys:>10.3f} {unit}\n")
def complete(self, text, word):
# Tab tamamlama: kanal numaraları
try:
count = int(gdb.parse_and_eval("g_sensor_count"))
return [str(i) for i in range(count) if str(i).startswith(word)]
except Exception:
return []
# Komutu kaydet
SensorDumpCommand()
Komut kategorileri
| Kategori sabiti | Açıklama |
|---|---|
COMMAND_USER | Kullanıcı tanımlı genel komutlar |
COMMAND_DATA | Veri inceleme komutları (p, x gibi) |
COMMAND_BREAKPOINTS | Breakpoint/watchpoint komutları |
COMMAND_RUNNING | Çalıştırma komutları (run, step, next) |
COMMAND_FILES | Dosya/sembol yükleme komutları |
COMMAND_OBSCURE | Nadiren kullanılan komutlar |
(gdb) source sensor_cmd.py
(gdb) sensor-dump
Ch Raw Value Unit
-----------------------------------
0 0x00001f40 8.000 °C
1 0x00005c3a 23.450 °C
2 0x00009c40 40.000 %
(gdb) sensor-dump 1 --raw
1 0x00005c3a
03 TUI — Text User Interface
GDB TUI modu, terminal içinde çok bölmeli bir arayüz sunar: kaynak kodu, assembly, register ve komut panelleri. IDE olmayan ortamlarda (SSH, gdbserver) güçlü bir alternatiftir.
TUI'yi başlatma
# TUI modunda başlat
gdb -tui ./myprogram
# Çalışırken TUI aç/kapat
# Ctrl+X, Ctrl+A
# Belirli layout ile başlat
gdb -tui -ex "layout split" ./myprogram
Layout seçenekleri
| Komut | Görünüm |
|---|---|
layout src | Kaynak kodu + komut paneli |
layout asm | Assembly + komut paneli |
layout regs | Register + kaynak/assembly paneli |
layout split | Kaynak + assembly + komut (üç panel) |
tui new-layout custom src 1 regs 1 cmd 1 | Özel layout tanımla |
TUI kısayolları
TUI özelleştirme
# ~/.gdbinit — TUI başlangıç ayarları
set tui border-kind acs # ASCII karakter çerçevesi
set tui active-border-mode bold # Aktif panel kalın çerçeve
tui enable # Her zaman TUI aç
layout split # Split layout varsayılan
focus cmd # Komut paneline odaklan
# Renk ayarları (GDB 9.1+)
set style enabled on
set style address foreground cyan
set style function foreground yellow bold
04 Conditional breakpoint ve watchpoint
GDB'nin koşullu breakpoint ve watchpoint'leri, spesifik durumları yakalamak için güçlü araçlardır. Binlerce çağrıdan sadece belirli koşulda durmak development süresini dramatik kısaltır.
Koşullu breakpoint
# Temel koşullu breakpoint
(gdb) break process_data if len > 1000
(gdb) break sensor.c:147 if channel == 2 && value > 100.0
# Mevcut breakpoint'e koşul ekle
(gdb) break sensor_read
Breakpoint 3 at 0x401234: file sensor.c, line 89.
(gdb) condition 3 sensor_id == 0x42
# Koşulu kaldır
(gdb) condition 3
# Breakpoint sayacı — N kez geçtikten sonra dur
(gdb) ignore 3 99 # breakpoint 3'ü 99 kez atla, 100'de dur
# Python ile dinamik koşul
(gdb) break malloc
(gdb) commands 1
silent
python
size = int(gdb.parse_and_eval("$rdi"))
if size > 4096:
gdb.execute("frame")
print(f"Büyük malloc: {size} bayt")
gdb.execute("continue")
end
end
Watchpoint tipleri
| Komut | Tetiklenme |
|---|---|
watch expr | Yazma — değer değiştiğinde dur |
rwatch expr | Okuma — değer okunduğunda dur |
awatch expr | Her erişim — okuma veya yazma |
watch -l expr | Location watch — dereference edilmiş adres izler |
# Global değişkeni izle
(gdb) watch g_error_flag
Hardware watchpoint 2: g_error_flag
# Pointer ile erişilen belleği izle
(gdb) watch *((int *)0xffff8000dead0000)
# Struct alanı değişince dur
(gdb) watch sensor.raw_value
# commands block — watchpoint tetiklenince
(gdb) watch g_state
(gdb) commands 2
silent
printf "Durum değişti: %d -> %d\n", g_state_old, g_state
set g_state_old = g_state
backtrace 5
continue
end
# Koşullu watchpoint
(gdb) watch g_buffer_idx if g_buffer_idx > BUFFER_MAX
05 Reverse debugging
GDB'nin reverse debugging özelliği, programın çalışmasını kayıt altına alarak geriye doğru single-step ve continue yapılmasını sağlar. Hata oluşmadan önceki ana gitmek için idealdir.
record full modu
# Kayıt başlat
(gdb) run
(gdb) record full # Bu noktadan itibaren tüm yürütmeyi kaydet
# Forward (ileri)
(gdb) next # normal ileri adım
(gdb) continue # çalıştır
# Reverse
(gdb) reverse-next # bir önceki kaynak satırına dön
(gdb) reverse-step # bir önceki komuta dön (into)
(gdb) reverse-continue # geriye doğru bir önceki breakpoint'e koş
(gdb) reverse-finish # mevcut fonksiyona giriş noktasına dön
# Kayıt geçmişini gör
(gdb) show record full size
(gdb) info record
# Kayıt sınırları
(gdb) set record full insn-number-max 500000
(gdb) set record full memory-query off # hafıza sınırında sor
record btrace — donanım destekli
# Intel PT (Processor Trace) ile hardware-assisted recording
# Daha hızlı ama sadece ileri kayıt, çok daha geniş tarih
(gdb) record btrace pt # Intel PT
# veya
(gdb) record btrace bts # Branch Trace Store (eski CPU)
# Çalışma geçmişini göster
(gdb) record function-call-history /ilc
# i = instruction number
# l = source lines
# c = cycle count
# Instruction geçmişi
(gdb) record instruction-history 100,120
# 100 - 120 arası komutları göster
# btrace ile reverse stepping (sınırlı)
(gdb) reverse-step
(gdb) reverse-next
Pratik senaryo: Use-After-Free tespiti
# free() sonrası kullanımı tespit et
(gdb) break free
(gdb) commands 1
silent
set $freed_ptr = (void *)$rdi
continue
end
# Program crash'e gittiğinde:
(gdb) record full
(gdb) run
# Crash noktasında:
(gdb) reverse-continue # crash öncesine git
(gdb) watch *$freed_ptr # bu adresi izle
(gdb) reverse-continue # bu adrese son yazan yere git
06 Multi-process/thread debugging
GDB, birden fazla süreç ve thread'i eşzamanlı olarak debug edebilir. Embedded Linux'ta çok süreçli sistemler, fork'lanan daemon'lar ve çok thread'li uygulamalar için vazgeçilmezdir.
Thread yönetimi
# Tüm thread'leri listele
(gdb) info threads
Id Target Id Frame
* 1 Thread 0x7f... (main) sensor_read () at sensor.c:45
2 Thread 0x7e... (recv) poll () from libc.so.6
3 Thread 0x7d... (proc) pthread_cond_wait () from libpthread
# Thread'e geç
(gdb) thread 2
# Tüm thread'lerde komut çalıştır
(gdb) thread apply all backtrace
(gdb) thread apply all bt full
(gdb) thread apply 1 2 3 print g_error_flag
# Thread adı göster
(gdb) info threads
# Thread 2 "sensor-recv" ...
# Adı al (prctl/pthread_setname_np ile set edilmişse)
(gdb) thread 2
(gdb) call (char*)pthread_getname_np(pthread_self(), buf, 16)
Scheduler locking
# Sadece mevcut thread'i çalıştır (diğerleri dondurulur)
(gdb) set scheduler-locking on
# Tüm thread'ler çalışır (varsayılan)
(gdb) set scheduler-locking off
# Sadece step modunda kilitle (continue'da serbest)
(gdb) set scheduler-locking step
# Mevcut ayarı göster
(gdb) show scheduler-locking
Multi-inferior (çok süreç) debug
# Fork takip modu
(gdb) set follow-fork-mode child # fork sonrası çocuğu takip et
(gdb) set follow-fork-mode parent # ebeveynde kal (varsayılan)
# Her iki süreci de debug et
(gdb) set detach-on-fork off # fork sonrası her ikisi de durur
(gdb) info inferiors
Num Description Executable
* 1 process 12345 ./sensor-daemon
2 process 12346 ./sensor-daemon (child)
# Inferior'lar arasında geç
(gdb) inferior 2
# İnferior'a program ekle (running process)
(gdb) attach 9876
# Tüm inferior'lara komut uygula
(gdb) inferior apply all print g_running
# Bağlantıyı kes
(gdb) detach inferiors 2
07 gdb-dashboard ve pwndbg
gdb-dashboard ve pwndbg, GDB'yi daha okunabilir ve bilgi yoğun bir arayüze dönüştüren Python tabanlı eklentilerdir. Her ikisi de terminal içinde çalışır; kurulum basit, etki büyüktür.
gdb-dashboard kurulumu
# gdb-dashboard kurulumu
wget -P ~ https://github.com/cyrus-and/gdb-dashboard/raw/master/.gdbinit
# veya
curl -o ~/.gdbinit \
https://raw.githubusercontent.com/cyrus-and/gdb-dashboard/master/.gdbinit
# Python pwndbg gereksinimleri
pip3 install pygments
# GDB başlat — dashboard otomatik yüklenir
gdb ./myprogram
gdb-dashboard modülleri
# Tüm dashboard modüllerini göster
(gdb) dashboard
# Belirli modülleri aç/kapat
(gdb) dashboard source on
(gdb) dashboard assembly on
(gdb) dashboard registers on
(gdb) dashboard memory on
(gdb) dashboard stack on
(gdb) dashboard threads off # gerekmeyeni kapat
# Tek seferlik güncelleme
(gdb) dashboard -output /tmp/debug.txt # dosyaya yönlendir
# Renk teması
(gdb) dashboard -style prompt '(gdb) '
(gdb) dashboard -style style 'dark'
pwndbg kurulumu (güvenlik/heap analizi)
git clone https://github.com/pwndbg/pwndbg
cd pwndbg
./setup.sh # ~/.gdbinit güncellenir
pwndbg heap analizi
# pwndbg komutları
(gdb) heap # heap durumunu göster
(gdb) bins # serbest chunk'ları (tcache, fastbin vb.)
(gdb) chunks # tüm malloc chunk'ları listele
(gdb) vmmap # process bellek haritası
(gdb) canary # stack canary değerini göster
(gdb) checksec # ikili güvenlik bayrakları
# Gömülü sistemde bellek görüntüsü
(gdb) vmmap
# Permissions Address Range Size Offset File
# r-xp 0x00400000-0x00401000 4096 0 /app
# r--p 0x00401000-0x00402000 4096 4096 /app
# rw-p 0x00402000-0x00403000 4096 8192 /app
# rw-p 0x7ffd0000-0x7fff1000 135k 0 [stack]
# Custom context formatter
(gdb) set context-output auto
(gdb) context
gdb-dashboard özel modül
class SensorModule(Dashboard.Module):
"""gdb-dashboard özel modülü — sensör kanallarını gösterir"""
def label(self):
return 'Sensors'
def lines(self, term_width, term_height, style_changed):
try:
sensors = gdb.parse_and_eval("g_sensors")
count = int(gdb.parse_and_eval("g_sensor_count"))
except gdb.error:
return [ansi('Sensör verisi yok', R.style_low)]
result = []
for i in range(min(count, 8)): # max 8 göster
s = sensors[i]
raw = int(s['raw_value'])
ch = int(s['channel'])
phys = raw * 0.001
valid = bool(s['is_valid'])
color = R.style_high if valid else R.style_low
line = f" Ch{ch:02d}: {phys:8.3f} {'✓' if valid else '✗'}"
result.append(ansi(line, color))
return result
08 Pratik: Core dump analizi ve uzak debugging
Gömülü Linux sistemlerde crash sonrası üretilen core dump dosyaları ve gdbserver ile gerçek donanım üzerinde uzak debugging, production sorunlarını çözmenin temel yöntemidir.
Core dump etkinleştirme
# Embedded Linux'ta core dump aktifleştir
ulimit -c unlimited
# Core dosyası adlandırma
echo '/tmp/core.%e.%p.%t' > /proc/sys/kernel/core_pattern
# %e = program adı, %p = PID, %t = zaman
# systemd ile core dump (coredumpctl)
# /etc/systemd/coredump.conf:
# Storage=external
# ProcessSizeMax=2G
# ExternalSizeMax=2G
# Program çalıştır ve crash oluşsun
./sensor-daemon &
kill -SIGSEGV $! # test için
ls /tmp/core.*
Core dump analizi
# Host sistemde analiz (hedef ile aynı kütüphaneler gerekli)
# Cross-compile toolchain GDB'si ile
aarch64-linux-gnu-gdb \
/path/to/sensor-daemon \
/tmp/core.sensor-daemon.1234.1704067200
# GDB içinde:
(gdb) bt # backtrace — crash noktası
(gdb) info registers
(gdb) x/20x $sp # stack içeriği
# Tüm thread'lerin backtrace
(gdb) thread apply all bt full
# Yerel değişkenler
(gdb) frame 3 # ilgilenilen frame'e git
(gdb) info locals
(gdb) info args
Python ile core dump analizi
"""
GDB Python ile otomatik core dump analiz betiği.
Kullanım: gdb -batch -x analyze_core.py --args ./sensor-daemon core.*
"""
import gdb
def analyze_core():
gdb.execute("set pagination off")
gdb.execute("set print pretty on")
# Temel bilgiler
print("=" * 60)
print("CORE DUMP ANALİZİ")
print("=" * 60)
# Crash noktası
frame = gdb.selected_frame()
print(f"\n[CRASH NOKTASI]")
print(f" Fonksiyon : {frame.name()}")
print(f" PC : 0x{frame.pc():x}")
sal = frame.find_sal()
if sal.symtab:
print(f" Dosya : {sal.symtab.filename}:{sal.line}")
# Backtrace
print("\n[BACKTRACE]")
gdb.execute("bt 15")
# Tüm thread backtrace
print("\n[TÜM THREAD'LER]")
gdb.execute("thread apply all bt 5")
# Sensör durum değişkeni
try:
state = gdb.parse_and_eval("g_sensor_state")
print(f"\n[SENSÖR DURUMU] g_sensor_state = {state}")
except gdb.error:
print("\n[UYARI] g_sensor_state bulunamadı")
# Son hata kodu
try:
err = gdb.parse_and_eval("errno")
print(f"[errno] {int(err)} ({gdb.execute('call strerror(errno)', to_string=True).strip()})")
except Exception:
pass
print("\n" + "=" * 60)
analyze_core()
gdb.execute("quit")
gdbserver ile uzak debugging
# Hedef (gömülü Linux cihaz) üzerinde:
gdbserver :3333 ./sensor-daemon
# veya çalışan sürece bağlan:
gdbserver :3333 --attach $(pgrep sensor-daemon)
# Geliştirici makinesinde:
aarch64-linux-gnu-gdb ./sensor-daemon
(gdb) set sysroot /opt/sysroots/aarch64-linux-gnu
(gdb) set solib-search-path /opt/sysroots/aarch64-linux-gnu/usr/lib
(gdb) target remote 192.168.1.100:3333
# Breakpoint koy ve çalıştır
(gdb) break sensor_read
(gdb) continue
# Pretty-printer'ları yükle
(gdb) source ~/.gdb/sensor_printers.py
(gdb) source ~/.gdb/sensor_cmd.py
# Sensör durumunu analiz et
(gdb) sensor-dump
(gdb) watch g_error_flag
Otomatik uzak debug betiği
#!/bin/bash
# Hedef cihaza gdbserver yükle ve bağlan
TARGET_IP="${1:-192.168.1.100}"
TARGET_PORT=3333
BINARY="sensor-daemon"
SYSROOT="/opt/sysroots/aarch64-linux-gnu"
echo "[*] $TARGET_IP üzerinde gdbserver başlatılıyor..."
ssh root@"$TARGET_IP" "pkill gdbserver; gdbserver :$TARGET_PORT ./$BINARY &" &
sleep 1
echo "[*] GDB bağlanıyor..."
aarch64-linux-gnu-gdb "$BINARY" \
-ex "set sysroot $SYSROOT" \
-ex "set solib-search-path $SYSROOT/usr/lib" \
-ex "target remote $TARGET_IP:$TARGET_PORT" \
-ex "source ~/.gdb/sensor_printers.py" \
-ex "source ~/.gdb/sensor_cmd.py" \
-ex "layout split" \
-ex "break main" \
-ex "continue"
GDB Python API, tekrarlayan debug görevlerini otomatikleştirmek için güçlü bir altyapı sunar. Pretty-printer'lar ham bellek görünümünü anlamlı formatlara dönüştürür; custom komutlar proje spesifik debug araçları oluşturmanızı sağlar. Core dump analizi Python betiği ile bir CI pipeline'ına entegre edilebilir ve her regression otomatik olarak analiz edilebilir.