DEBUGGING | GDB'ye Giriş

01:00 ,
GDB (GNU Debugger) GNU ekosistemi içinde bulunan debugger'dır. Unix tabanlı pek çok sisteminde, C/C++ gibi birçok programlama dilinde hata ayıklamak için kullanılmaktadır. Biz de C dilini baz alarak nasıl kullanılacağını birkaç blog yazısında anlatmaya çalışacağız. 

GDB birçok unix tabanlı sistemde varsayılan olarak kurulu gelmektedir. Komut satırından gdb komutunu girdiğinizde aşağıdaki gibi bir çıktı alıyorsanız sistemizinde GDB 'nin var olduğu anlamına gelir. GDB ile sisteminizde var olan halihazırda çalışan programları debug edebileceğiniz gibi, uzak bir sistemde çalışan bir programı da remote debug ile debug edebiliriz. Fakat bu iki durum bu yazının konusu olmayacaktır.


Eğer bir sistemde gdb kurulu ise, gdb'nin sunduğu tüm olanaklar elimizin altında demektir. Artık bize sunduğu özellikleri öğrenmeye başlayabiliriz. 

GDB'yi burada bırakıp tekrar başa dönecek olursak yazdığımız programın hatalarını ayıklayabilmek için programı -g seçeneği ile derlemememiz sonrasında daha kolay bir hata ayıklama ortamı sağlayacaktır. Derleyici debugger için gerekli olan bilgileri bu derleme parametresi sayesinde çalıştırılabilir programın içine gömebilecek ve debugger bize daha fazla olanak sağlayabilecektir.


Yukarıda görüldüğü gibi programımız Segmentation Fault veriyor ve bizim bunu çözmemiz gerekmekte. Çalıştığımız ortam Unix environment bir ortam ise başta da dediğimiz gibi kullanabileceğimiz en büyük yardımcı gdb. Doğrudan gdb'yi çalıştırıp ilgili dosyayı içeriden verebileceğimiz gibi gdb a.out ile gdb'yi başlatma seçeneğini de kullanabiliriz. 

start

  • start komutu geçici olarak main'e breakpoint koyar ve programın GDB altında debug sürecinin başlamasını sağlar. 
  • start [Arguments] programa argüman geçilmesini sağlamak için start komutu yanına argümanları yazmamız yeterlidir.

file

gdb'yi doğrudan herhangi bir dosya vermeden çalıştırırsak, gdb içinde debug edilmek istenen binary dosyayı belirtmemiz gerekmektedir.


 Bunun için file dosyaismi şeklinde olan komutu kullanmamız gerekmektedir.

run & continue

  • r[un] ile bir sonraki breakpoint'e kadar veya breakpoint yoksa programın sonuna kadar çalışmasını sağlar.
  • r[un] [Arguments] : Programa arguman geçilmesini sağlamak için run komutu yanına argümanları yazmamız yeterlidir.
  • c[ontinue] ile devam eden debug işleminin durduğu yerden bir sonraki breakpoint'e kadar veya programın sonuna kadar devam etmesini sağlar.

list

List komutu ile programımızın kaynak kodunu görmek mümkündür.

break

Eğer debugger kullanım deneyiminiz varsa debug işleminin en kritik noktası, uygun yerlere breakpoint konulmasını sağlamaktır. Breakpoint ne işe yaramaktadır dersek? Debugger program kodlarını çalıştırmaya başlar ve bir breakpoint'e geldiğinde durur ve kontrolü bize bırakır. Ta ki biz devam komutunu verene kadar. Bu noktada kritik ve hata olabilecek noktalara breakpoint konulması gerektiğini söyleyebiliriz. Biz de öncelikle main'e ve malloc işleminin yapıldığı noktaya breakpoint koyalım.

  • info b[reakpoints] komutu ile tüm breakpoint'lerimizi listeleyebilirsiniz.
  • d[elete] komutuyla tüm breakpoint'leri silebilirsiniz.

step&next

  • s[tep] Tek adım ilerler, fonksiyon içerisine dallanabilir.
  • n[ext] Tek adım ilerler, fonksiyon içerisine dallanmaz.

finish

fin[ish] ile girilen fonksiyonun, döngünün veya buna benzer olayların sonlanmasını sağlarız.

whatis

Herhangi bir değişken, fonksiyon gibi ifadelerin türünü öğrenmek için kulanabileceğimiz bir komuttur. Örneğin
  • (gdb) whatis main dediğimizde
  • type = int ()
şeklinde bir çıktı alınacaktır.

where

Programın o an nerede olduğunu söyler. Program çalıştıktan sonra herhangi bir yerde çakılırsa sıkıntılı yerleri bulmamıza yardımcı da olur. Aşağıda segmentation fault aldıktan sonra where komutunu çalıştırırsak karşımıza olası üç stack frame listelenmektedir.

frame

where komutu çıktısından elde edilen verilerden sonra frame komutu ile ilgili stack frame noktasına  gidilebilmektedir. #0, #1, #2, #3 şeklinde listelenen frame'lerden olası nokta olduğunu düşündüğümüz yere yani frame 3 gidebiliriz.

info functions

Binary dosya içindeki fonksiyonların listelenmesi için info function komutunu girmemiz yeterlidir.


Bu komut ile olası breakpoint koyulacak fonksiyonları görmemiz mümkündür.

layout asm

layout asm ile pencere ikiye bölünerek assembly komutlarının görüleceği bir ekran daha karşımıza çıkacaktır.


Bu sayede rahatlıkla OpCode'ları izlemek mümkün hale gelecektir. Bu noktada bilmemiz gereken assembly çıktıları için AT&T ve Intel notasyonu şeklinde çıktı almak mümkün. Bu ayarı; set disassembly-flavor intel or att şeklinde ayarlamak mümkündür. 


Genellikle Intel notasyonu yaygın olduğu için biz de o notasyonu kullanacağız. Assembly çıktılarını görebileceğimiz bir diğer komut ise dissassemble fonksiyon ismi ile istediğimiz fonksiyonun assembly çıktılarını almak mümkün. Yukarıda main fonksiyonu için çıktıyı görmek mümkündür.

info locals

local değişkenlerin tamamının durumunu gözlemlemek için kullanılması gereken komut info locals 'dir.

info registers

register'ların tamamının durumunu gözlemlemek için kullanılması gereken komut info registers 'dir.

info source

debug edilen dosyaya ilişkin bilgilerin detaylı olarak görüntülendiği komut info source'dur.

print

Herhangi bir değişkenin değerinin görüntülenebildiği komut print'dir. print değişken ismi, register ismi şeklinde farklı biçimlerde kullanılabilir.


print işlemini /x, /d, /o ve /t gibi çıktıları istediğimiz formatta biçimlendirebiliriz.


Tüm çıktı formatlarını help x komutuyla rahatlıkla görebiliriz.


Note: GDB öğrenme bakımından zor ama kullanması kolay bir debuggerdır. Yolun başında biraz sabır gerekmektedir. Bu nedenle yolun başında, öğrenme sürecinde olanlara yolun zorlu ama aydınlık olacağını söyleyebilirim.