Paylaşılan Kütüphane (shared library) nedir?
Paylaşılan kütüphaneler (shared libraries), programlar çalıştığında programlar tarafından yüklenen kütüphanelerdir. bu kütüphaneler, işletim sistemine bir kere kurulur, daha sonra kurulan programlar bu kurulan kütüphaneleri kullanır. Daha sonra kütüphanenin yeni sürümü kurulursa bile, programlar eski sürümü kullanmaya devam edebilir. programlar çalıştığı sırada dinamik olarak çağırılan bu kütüphaneler, çalışma hızını büyük ölçüde arttırır.
bu kütüphaneler “lib” harfleriyle başlar ve uzantıları “.so” olur. Eğer kendiniz bir paylaşılan kütüphane yarattıysanız, bu kütüphaneyi yüklemek için gereken dosyaları soğru yerlere kopyaladıktan sonra ldconfig komutunu çalıştırmalısınız.
ldconfig -n paylaşılan_kütüphanenin_yolu
Dinamik bağlama (dynamic linking) nedir?
Eğer programınız bir paylaşılan kütüphane kullanıyorsa, derlerken bunu belirtmeniz gerekir. Örneğin programınızda ssl kütüphanesini kullanmanız gerekiyorsa, libssl.so kütüphanesini kullanmalısınız. Bunu yapmak için de programınızı derlerken -lssl ifadesini eklemeniz gerekir.
gcc programismi.c -lssl
Bunu yaptığınız zaman, ssl kütüphanesindeki kodları yazılımınıza eklemediğiniz halde kullanabilirsiniz. Bu kütüphane, program çalıştığı sırada çağırıldığı için, libssl.so kütüphanesinin bulunduğu herhangi bir makinede programınızı çalıştırabilirsiniz.
LD_PRELOAD nedir?
LD_PRELOAD, kullanıcı tarafından belirlenmiş, boşluk karakteriyle ayrılmış, diğer kütüphanelerden önce yüklenen ELF paylaşılan kütüphaneler listesidir. Bu özellik, diğer paylaşılan kütüphanelerdeki fonksiyonların yerine geçen fonksiyonlar üretmek için kullanılabilir.
LD_PRELOAD ile çalıştırılan bir program X fonksiyonunu çağırıyorsa ve X fonksiyonu da LD_PRELOAD listesinde varsa, bu fonksiyon çağırılır. Eğer LD_PRELOAD listesinde X fonksiyonu yoksa, X genel arama yollarında aranır(/usr/lib/ , /usr/local/lib gibi)
.so objesi yaratmak
Daha önce de belirttiğim gibi, .so objesi, paylaşılan kütüphanelerin uzantısıdır. paylaşılan bir kütüphanenin diğer C ve C++ ile yazılmış programlar gibi bir 'main' fonksiyonuna ihtiyacı yoktur. Çünkü bu kütüphaneler kendi başına bir program değil, programlar tarafından kullanılan kodların paketidir.
LD_PRELOAD için .so dosyası yaratmak için tek ihtiyacınız yazdığınız kaynak kodu ve gcc derleyicisidir. Eğer her ikisine de sahipseniz, örnek olarak aşağıda verdiğim komutları çalıştırmanız yeterli olacaktır.
gcc -fPIC -rdynamic -c source_code.c
gcc -shared -o libsource_code.so source_code.o -lc -ldl
Tek tek hangi parametrenin ne işe yaradığını anlatmayacağım. Bunun için gcc'yi incelemenizi tavsiye ederim. Diğer tüm UNIX işlerinde olduğu gibi, bunda da “man gcc” size yol gösterecektir. Ama kısaca şunları söyleyebilirim. İlk satır kaynak kodunuzu derleyip bir .o uzantılı dosya oluşturmanızı sağlar (bu örnekte source_code.o) İkinci satırda ise bu .o uzantılı dosya sayesinde .so uzantılı dosyanızı üretirsiniz. Bu kadar basit.
LD_PRELOAD'a eklemek
.so uzantılı bir dosya ürettiniz. Peki bunu nasıl kullanacaksınız? Çok basit. bu dosyanın yolunu LD_PRELOAD listesine ekleyerek. Fakat burada dikkat edilmesi gereken çok önemli bir ayrıntı var. Bir fonksiyon hem LD_PRELOAD listesinde hem de başka herhangi bir yerde (örneğin /usr/lib/) varsa, LD_PRELOAD listesindeki fonksiyonun önceliği vardır. Yani örneğin siz fopen fonksiyonunu yeniden yazıp bunu da LD_PRELOAD listesine eklerseniz, artık fopen kullanılan bütün programlar bu listede belirtilen halini kullanır.
Bu uyarıyı yaptıktan sonra, bir kütüphanenin nasıl LD_PRELOAD listesine ekleneceğini yazıyorum. Burada yazdığım, listeye kalıcı olarak eklemek içindir. Bir sonraki bölümde tek kullanımlık halini yazacağım.
export LD_PRELOAD=/libx.so'nun/yolu/
LD_PRELOAD kullanımı
Öncelikle neden LD_PRELOAD kullanılır, bu sorunun cevabını verelim. En önemli avantajı, dinamik bağlanmış çalıştırılabilir programda hiçbir değişiklik yapmadan kullanılabilmesidir. Sadece dinamik kütüphaneyi değiştirerek, asıl yazılımınızda hiçbir değişiklik yapmadan programınızı yeniden düzenleyebilirsiniz.
LD_PRELOAD kullanmanın dezavantajı ise, kullanmak istediğiniz fonksiyona sahip kütüphanenin, program çalıştırıldığı sırada kurulu ve hazır olmasının gerekliliğidir.
Şimdi örnek bir LD_PRELOAD kullanımına geçelim:
LD_LIBRARY_PATH=. LD_PRELOAD=libsource_code.so ./program [varsa argüman]
Bu şekilde, programda eğer libsource_code.so'nun sahip olduğu bir fonksiyon çağırılıyorsa, bunu LD_PRELOAD ile çağıracaktır. Ama bu sadece yukardaki komutu yazdığımız zaman geçerlidir. Kalıcı bir kütüphane ekleme değildir.
Örnek
// target.c
#include <stdio.h>
#include <unistd.h>
int main() {
printf( "user id: %d\n", getuid() );
return 0;
}
Yukarıdaki kodu
gcc target.c -o target
komutuyla derleyip
./target
yazdığınızda, şuna benzer bir çıktı alırsınız:
$./target
user id: 1000
Buradaki 1000 değeri yerine getuid() fonksiyonunun döndüğü değer gelir. Eğer her zaman 0 dönmesini isterseniz
// libfake.c
int getuid() {
return 0;
}
kodunu yazıp,
$ gcc -shared libfake.c -o libfake.so
komutunu çalıştırın. Artık bir .so dosyanız var. bunu LD_PRELOAD listesine eklediğinizde
$ LD_PRELOAD=./libfake.so ./target
user id: 0
alırsınız.
shared library --> http://www.dwheeler.com/program-library/Program-Library-HOWTO/x36.html
ldconfig --> http://linux.die.net/man/8/ldconfig
LD_PRELOAD man page --> http://unixhelp.ed.ac.uk/CGI/man-cgi?ld.so+8
gcc --> http://gcc.gnu.org/
Tags: Linux ld preload shared object shared library