Dinamik Bağlanma (Dynamic Linking) ve Hata Çözümleme

Haziran 29th, 2009 tarihinde Serkan Kenar tarafından

Önceki yazımda verdiğim örnek programı yazının başlığından da gelen ipucuyla bir iki arkadaşım çözmeyi başardı. Ama sorun kokeksibir’in yorumundakinden daha farklı. (Onu da anlatacağım ama sonra) Programı derleyip çalıştırdığınızda 1022 defa “Unable to connect to database” hatası vererek sonlandığını göreceksiniz. (Denemeyenler şimdi ortaya çıkacak :) ) İlginç, hiç bir yerde döngü yok aslında. (İlk ipucu, iç içe çağrılar var ama nerede?) Programa baktığımıza hiç sorun görünmüyor. Bir şeyleri yanlış yapıp yapmadığımızı anlamak için yine de bir iki sefer kodu kontrol ediyoruz.

Sonucun değişmemesi, çalışma zamanında yanlış giden bir şeylerin olduğu konusunda şüphelendiriyor. Hemen GDB’yi çalıştırıyoruz.


	gdb ./mysql_bug
	break connect

connect() metodunun çıktısını gördüğümüzden oraya bir breakpoint ekledim. run diyerek çalıştırıyoruz ve breakpoint’e takılıyor. Adım adım işletelim.

	next
	n
	<enter>

O da ne?? Tekrar connect()’e geldi. hmm, bt ile bir stack trace alalım.

	(gdb) bt
	#0  connect () at mysql_bug.c:16
	#1  0xb7e6f766 in my_connect () from /usr/lib/libmysqlclient.so.15
	#2  0xb7e70338 in mysql_real_connect () from /usr/lib/libmysqlclient.so.15
	#3  0x080485f0 in connect () at mysql_bug.c:16
	#4  0xb7e6f766 in my_connect () from /usr/lib/libmysqlclient.so.15
	#5  0xb7e70338 in mysql_real_connect () from /usr/lib/libmysqlclient.so.15
	#6  0x080485f0 in connect () at mysql_bug.c:16
	#7  0x08048644 in main () at mysql_bug.c:34

İlginç bir şekilde mysql_real_connect()’ten sonra, tekrar connect() metodumuza geliyor. MySQL kütüphanesi bizim yazdığımız bir fonksiyonu neden çağırsın? Artık mesele anlaşılmış gibi olsa da biraz daha debug etmeye devam edelim. “ignore 1 1010″ komutuyla GDB’nin breakpoint’imizi 1010 kez gözardı etmesini sağlıyor ve devam (cont) ediyoruz. Tekrar breakpoint’e takıldığımızda bir bt komutuna bakın.. Artık problem daha da kendini belli ediyor. Tüm stack tekrar tekrar çağırılmaktan dolmuş:

	#3034 0xb7e6f766 in my_connect () from /usr/lib/libmysqlclient.so.15
	#3035 0xb7e70338 in mysql_real_connect () from /usr/lib/libmysqlclient.so.15
	#3036 0x080485f0 in connect () at mysql_bug.c:16
	#3037 0xb7e6f766 in my_connect () from /usr/lib/libmysqlclient.so.15
	#3038 0xb7e70338 in mysql_real_connect () from /usr/lib/libmysqlclient.so.15
	#3039 0x080485f0 in connect () at mysql_bug.c:16
	---Type <return> to continue, or q <return> to quit---
	#3040 0x08048644 in main () at mysql_bug.c:34

Dinamik Bağlanma (Dynamic Linking)

Artık bulmacanın cevabını verebiliriz. Bir şekilde fonksiyonumuza verdiğimiz masum “connect” ismi, MySQL kütüphanesinde de tanımlanmış ve mysql_real_connect() fonksiyonu tarafından kullanılıyor. Biz mysql_real_connect’i çağırdıkça o da bizi tekrar çağırıyor. Bu iç içe durum 1022 kez tekrarladıktan sonra mysql_real_connect hata dönüyor ve o ana kadar çağırılan tüm metodlar hata vererek tek tek çıkıyor.

Peki, bizim kendi yazdığımız bir fonksiyon nasıl olur da MySQL kütüphanesi tarafından çağırılabilir? (Bence esas yanıtlanması gereken soru bu, yoksa problemi deneme yanılma yaparak çözmek ve bir daha arkanıza bakmadan kaçmak mümkün. Ama gerçek bir coder kaçmaz, üzerine gider.)

Programımız MySQL kütüphanesine dinamik olarak bağlanarak derleniyor. Dinamik bağlanma yönteminde çalıştırılan program hangi kütüphaneleri kullandığını bir tabloda ve bu kütüphanede kullandığı metodları da başka bir tabloda tutar. Bu listeyi görmek için “ldd” komutunu kullanabiliriz. Mesela bizim programımız için ldd çıktısı şu şekilde olmaktadır:

	# ldd mysql_bug
	linux-gate.so.1 =>  (0xb8092000)
	libmysqlclient.so.15 => /usr/lib/libmysqlclient.so.15 (0xb7e96000)
	libc.so.6 => /lib/tls/i686/cmov/libc.so.6 (0xb7d33000)
	libpthread.so.0 => /lib/tls/i686/cmov/libpthread.so.0 (0xb7d19000)
	libcrypt.so.1 => /lib/tls/i686/cmov/libcrypt.so.1 (0xb7ce7000)
	libnsl.so.1 => /lib/tls/i686/cmov/libnsl.so.1 (0xb7cce000)
	libm.so.6 => /lib/tls/i686/cmov/libm.so.6 (0xb7ca8000)
	libz.so.1 => /lib/libz.so.1 (0xb7c92000)
	/lib/ld-linux.so.2 (0xb8093000)

Programımızı çalıştırdığımızda Linux altında ld-linux.so kütüphanesi bu tablodaki kütüphaneleri bağlamaya başlar ve programımızı çalıştırır. (Aynı shell scriptlerinin ilk satırında hangi interpreter tarafından çalıştırılacağının yazması gibi, tüm çalıştırılabilir dosyaları da aslında ld-linux.so “çalıştırır”. /lib/ld-linux.so’ya programınızın adını parametre verin ve olanları görün :) ) Öncelikle tüm kütüphaneler araştırılır. ld-linux, /etc/ld.so.config dosyasındaki sıralamaya göre kütüphaneleri arar. Bulunamayan bir kütüphane olduğunda program çalışması hata ile sonlanır (hatta teknik olarak hiç başlamaz..) Genelde aksi belirtilmedikçe fonksiyonlar tembel (LAZY) bağlanır. Yani bir fonksiyon kullanılmadıkça performans açısında bu arama/bağlama işlemi masraflı bir işlem olduğu için bağlanmaz.

Program çalışması sırasında daha önce bağlantısı sağlanmamış bir fonksiyon kullanımı ile karşılaşıldığında hemen bir dinamik bağlanma işlemi başlar. Aşağıdaki sırayla fonksiyon bulunmaya çalışılır:

  1. mevcut program içinde
  2. LD_LIBRARY_PATH çevre değişkeniyle verilmiş olan kütüphaneler arasında
  3. programın dinamik bağlı olduğu kütüphaneler arasında (ld.so.cache’e bakarak)
  4. programın dinamik bağlı olduğu kütüphanelerin önce /lib’te sonra /usr/lib’te bulunanları arasında

Gördüğümüz gibi, bilinmeyen bir fonksiyon hangi kütüphaneye ait olursa olsun, bu sırayla araştırılıyor. MySQL kütüphanesine ait mysql_real_connect fonksiyonu bağlandıktan sonra, çalıştırıldığında içindeki connect() fonksiyonu için arama başlatılıyor ve ilk sırada bizim programımız içinde bir aday bulunduğu için bağlantı burada sonlanıyor. Ondan sonrası yukarıda karşılaştığımız duruma neden oluyor.

Yukarda kendi programımızı debug edebilmiştik. Peki, bu dinamik bağlantı işlemini nasıl görebiliriz ve hataları tespit edebiliriz? Kütüphanenin çevre değişkenleri sayesinde çalışmasının etkilenebildiğini LD_LIBRARY_PATH’ten biliyoruz. Aynı şekilde LD_DEBUG çevre değişkeniyle de debug çıktısını açabilmek mümkün. Burada kullanılabilecek çeşitli parametreleri man ld-linux ile veya LD_DEBUG=help <herhangi_bir_komut> ile görebilirsiniz. Şimdilik LD_DEBUG=all ./mysql_bug diyerek programımızı çalıştıralım ve yukardaki paragrafta anlattığımız süreci canlı canlı görebilirsiniz. Çok uzun bir çıktı veriyor ama ilk satırlarını okumak fikri anlamanız açısından çok öğretici ve faydalı.

MySQL kütüphanesinde neden böyle çok yaygın kullanılabilir bir fonksiyon ismi vardır, kim bilir? Ama özensiz alınan kararlar maalesef böyle sorunlara neden olabiliyor. Fonksiyonlara, değişkenlere isim vermek sorumluluk ister.

Aslında bu yöntem çok yaygın olarak programların hafıza tahsis (memory allocation) hatalarını tespitte kullanılabilmekte. Örneğin malloc/realloc/free fonksiyonlarını yeniden tanımladığımız bir kütüphane yazarak, diğer programları test etmek mümkün (üstelik tekrar derlemek gerekmeksizin..)

Sonuç: Ufak gibi görünen bir sorun, üzerine düşüldüğünde farklı kavramların tekrar üzerinden geçmemizi ve hafiften paslanmış debugging marifetlerimizi hatırlamamızı sağladı.

Bunun üzerine fonksiyon ismini my_connect() yaparak tekrar denedim. Bahtsızlığın bu kadarı, bu seferde derleme aşamasında bir hatayla karşılaştım. :) Bu fonksiyon yine (!!) MySQL tarafından tanımlanmış. Başlık dosyalarında daha farklı tanımlandığı için daha derleme aşamasında hatayı görmek mümkün olabildi. Bu sorun kokeksibir’in yorumunda bahsettiği durum..) Neyse bunu geçebilmek en azından daha kolay.

Keyifli kodlamalar..

Fonksiyonlara isim vermek sorumluluk ister..

Haziran 10th, 2009 tarihinde Serkan Kenar tarafından

Aşağıdaki kodu çalıştırdığınızda hiç beklenmeyen bir sonuç çıkıyor. Sorunun ne olduğunu bulabilir misiniz? (Cevabı daha sonra..)

Not: Wordpress’in başlık dosyalarının noktasından önce boşluk koymasını engelleyemedim. Muhtemelen noktadan önce boşluk bırakmayanlardan olduğumu sanıyor.

#include <stdio .h>
#include <stdlib .h>
#include <mysql /mysql.h>

MYSQL *mysql;

int connect()
{
    mysql = mysql_init(NULL);
    if (mysql_real_connect(mysql,
               "localhost",
               "username",
               "password",
               "database",
               0,
               NULL,
               0
              ) == NULL)
    {
        fprintf(stderr, "Unable to connect to database\n");
        return 0;
    }
    return 1;
}

int main()
{
    if (!connect()) return 1;
    // bir seyler...
    return 0;
}

Yeni Türkü konserinden

Mayıs 31st, 2009 tarihinde Serkan Kenar tarafından

Bu sene de şenlikteydik, ve yine bu sene de Yeni Türkü şenlikteydi.. Sahnenin arkasında, sahanın ortasındaydık. O konumdayken insan çok da iyi kareler yakalayamayacağını düşünüyor.. Ters ışığın avantajıyla, tripod olmamasına rağmen güzel kareler yakalamışım gibi..

Bunları çektikten sonra, bir yazıya denk geldim. “10 metre çapınızda en az 10 harika fotoğraf çekebilirsiniz” temalı bir çalışma ya da yeni bir ilham kaynağı. Aklınıza gelen bir fikir yoksa, çevrenize bakmak için bir sebep. Aşağıdakiler de buna benziyor. O anda konserin ortasında yerimi değiştirmeye uğraşmadan, gördüğüm açıdan çektim..

2009-ODTU.Yeni.Turku.konseri-6 2009-ODTU.Yeni.Turku.konseri-5 2009-ODTU.Yeni.Turku.konseri-4 2009-ODTU.Yeni.Turku.konseri-3 2009-ODTU.Yeni.Turku.konseri-2 2009-ODTU.Yeni.Turku.konseri-1

Albüm: Yeni Türkü konseri – ODTÜ Bahar Şenlikleri (2009)

Keyifle kodlamak

Nisan 29th, 2009 tarihinde Serkan Kenar tarafından

Burada daha önce de yazmıştım: Intellij IDEA tutkunuyum. Eclipse ile çalışamıyorum. Yıllar içinde defalarca Eclipse ile çalışmak zorunda kaldıysam da bir türlü alışamadım. Alışabilmeyi geçtim, vaadedilen özellikleri düzgün çalışsa sabredeceğim ama o kadarı bile mümkün değil.

Son projemizde de testlerimizi Eclipse altında geliştirmeye karar vermiştik. Burada uzun uzun yazmayı, hatta resmini/videosunu çekerek göstermeyi düşünmüştüm ama buna gerek olduğunu düşünmüyorum. Eğer siz de Eclipse altında çalışmak zorunda bırakılan acınacak durumdaki geliştiricilerdenseniz, verildiğini düşündüğünüz özelliklerin nasıl daha “kullanılabilir”, “hızlı” ve “akıllı” geliştirildiğini görmek için Intellij IDEA’yı bir deneyin.

Mesela bir auto-complete, benim yazma hızımdan daha yavaş çalışıyorsa, ve üstüne akıl edip kullanmayı düşünebileceğim metodları, sınıfları getirmiyorsa ne işe yarar? Veya, IDE dosyaların haricen değişikliklerinin otomatik olarak farkına varamıyor da eski halleriyle çalışmaya devam ediyor, illa F5 istiyorsa, ne kadar akıllıdır? Arayüz, menüler ve rasgele düzenlenmiş kısayollar -bir programcının elinden çıktığı çok belli-, kullanılabilirlik uzmanlarınca düzenlenmediyse, ne kadar kullanışlı olabilir? Bir kısayolları silsilesi düşünün, çalıştırmak veya debug etmek için kullanılan, her bağlam için ayrı bir tuş tanımlanmış.

Sanırım tüm bu başarısız kullanılabilirlik ve yetersizliklerin nedeni, Eclipse geliştiricilerinin OSGi framework’üne uyumluluk ve altyapıya verdikleri önemden kaynaklanıyor. Eclipse Framework’ünü yine benzer işlerimizden biri için incelemiştik ve uygulama geliştirmek için Java’nın yıllardır beklediği sağlam bir altyapıyı sağladığını görmüş, beğenmiştik. Plugin yapısı, otomatik ağ güncellemeleri, yardımlar ve daha bir çok hazır çözüm uygulama geliştirmeyi çok kolaylaştırıyor. Bir arkadaşın da dediği gibi, “yakında tüm IDE’ler Eclipse tabanlı olacak.” Aslında her tür uygulama için kullanılabilir ama Java’nın da bir yeri var.

Açık kaynak kodlu olması ve bedava olması, tüm bu kusurlarını kapatamıyor ve bu kadar kötü bir araçla çalışmayı kabullenilebilir yapmıyor. Eclipse’in yıllar içinde neden olduğu en büyük kötülük bedava olması ve her özelliği öyle veya böyle barındırması nedeniyle daha iyi araçlara ayrılabilecek “masrafları” yüksek gibi göstermesi. Halbuki Intellij IDEA geliştirici başına verilen parayı son kuruşuna kadar hakeden bir yazılım..

Eclipse’cilere tavsiyem, testerenizi bilemenin hatta motorlu testere kullanmanın tam zamanı..

Intel işlemcilerde açık ve güven

Mart 29th, 2009 tarihinde Serkan Kenar tarafından

Geçtiğimiz hafta The Invisible Labs‘taki uzmanlar Intel işlemcileri etkileyen ve SMRAM’e kod sokuşturmaya imkan sağlayan bir açık buldular. Normalde bu alan yazılamaz olarak biliniyordu ve burada çalıştırılan kod en yüksek öncelikli seviye olan Sistem Yönetim Modunda (SMM) çalışıyor.

Bu iki açıdan çok tehlikeli: 1. açık işletim sisteminde bağımsız olarak işlemciyi etkiliyor. 2. bir kere SMRAM’e bulaştıktan sonra işletim sistemi veya anti virüs yazılımları tarafından tespit edilmesi mümkün değil.

Aslında işlemcilere yönelik saldırılar ve SMRAM’e erişimle ilgili daha önce de açıklanmış açıklar bulunuyor. Black Hat 2008′de bu tür saldırılar ile ilgili bir sunum yapılmış ve yine SMM üzerinden bir rootkit çalıştırılabileceği kanıtlanmıştı.

Bu tür saldırıları görünce aklıma ACM Klasiklerinden, Ken Thompson’ın Reflections on Trusting Trust yazısı geldi. Ken Thompson’ı tanımıyorsanız, en azından Unix, Plan 9, B (C’nin öncülü olan dil) ve UTF-8′i duymuşsunuzdur. Ken, bu yazısında adım adım bir derleyicinin nasıl zehirlenebileceğini ve ondan sonra derlenen tüm programların (ve tabii ki derleyicilerin de) nasıl zehirlenmiş olarak üretileceğini şaşırtıcı derecede basit bir örnekle gösterir ve şu sonuca varır:

You can’t trust code that you did not totally create yourself. (Especially code from companies that employ people like me.) No amount of source-level verification or scrutiny will protect you from using untrusted code. In demonstrating the possibility of this kind of attack, I picked on the C compiler. I could have picked on any program-handling program such as an assembler, a loader, or even hardware microcode. As the level of program gets lower, these bugs will be harder and harder to detect. A well installed microcode bug will be almost impossible to detect.

Hiç bir şeye güvenemezsiniz. İşlemciye bile.

The Invisible Labs’in açığı açıkladığı makalesinde ilginç bir bilgi var. Intel aslında bu açığı biliyormuş ve hatta çalışanları nasıl düzeltilebileceğiyle ilgili bir patent başvurusunda bile bulunmuşlar. Yine de neden düzeltmedikleri belirli değil (?). Intel, araştırmacılar açığı bildirdikten sonra, hatayı kabul etmiş ve düzeltileceğini açıklamış.

Bu arada işlemcilerde açık var diye panik yapmaya gerek yok. Bu saldırılar çok düşük seviye çalışıldığı için ancak saldırılacak makineye göre çok hassas saldırılara imkan sağlıyor. İşletim seviyesi, uygulamalar hatta tarayıcılar seviyesindeki açıklar çok daha etkili, yaygın ve tehlikeli olmaya devam edecektir.

odtü matematik, 2003

Şubat 24th, 2009 tarihinde Serkan Kenar tarafından

Amsterdam, 2008

Ocak 28th, 2009 tarihinde Serkan Kenar tarafından

Geçen sonbaharda Stuttgart’taki eğitimden sonra Amsterdam’a yolum düşmüştü. İşte Amsterdam’da çektiğim fotoğraflar..

I Amsterdam Kanal

Demiştim

Temmuz 10th, 2008 tarihinde Serkan Kenar tarafından

Daha önce demiştim, 40 saat yeter diye. Aşırı yoğun ve stresli çalışırsanız, olacağı bu.