bu yazımızda shared object fileların LOAD-TIME da nasıl relocation işlemlerinin dynamic linker tarafından yapıldığından elimizden geldiğince bahseticez..
işletim sistemi program header table içerisinde INTERP segmentini görürse LOAD segmentlerini yükledikten sonra INTERP segmentinde belirtilen dynamic linkerın kendisinide programın address spacenin içerisine yerleştiriyor.
programın adress spacenin kendisini ---> cat /proc/proses_id/maps
ya da şu şekilde -----> pmap proses_id
görebilirsiniz..
shared.c adında bir dosya oluşturuyoruz şu şekilde :
int tut=1;
int multiply(int a,int b)
{
tut=tut+a;
tut=tut*b;
return tut;
}
şu parametrelerle derleme işlemlerini yapıyoruz ....
gcc -m32 -o shared.o -c shared.c
gcc -m32 -shared -o libshared.so shared.o
elimizdeki libshared.so dosyasını şu şekilde bir analiz ediyoruz.
root@kali:~/ss# readelf -r libshared.so
Relocation section '.rel.dyn' at offset 0x314 contains 13 entries:
Offset Info Type Sym.Value Sym. Name
000004f5 00000c01 R_386_32 000016a8 tut
000004ff 00000c01 R_386_32 000016a8 tut
00000504 00000c01 R_386_32 000016a8 tut
0000050d 00000c01 R_386_32 000016a8 tut
00000512 00000c01 R_386_32 000016a8 tut
bu tablo dinamik linkera ilk gerçek adres atamasını yapar yapmaz,yapılacak olanları söylüyor..eğer ilk adresi belirlersek bu offsetleride ilk adrese bağıl olarak yeni adreslere çevirir ve hesaplamalarıda buna göre yapıp shared libraryi yüklemiş oluruz diye düşünüyor dinamik linker..
ve buradan "tut" sembolüne ilişkin değerlere bakıyoruz.
R_386_32 --> bu relocation type dynamic linkera offsete belirtilen yere git oraya sembolün adresini yerleştir diyor.
peki o offset içerisinde ne var diye merak ediyoruz ve objdump ile disass ediyoruz..
root@kali:~/ss# objdump -d libshared.so
libshared.so: file format elf32-i386
000004f0 <multiply>:
4f0: 55 push %ebp
4f1: 89 e5 mov %esp,%ebp
4f3: 8b 15 00 00 00 00 mov 0x0,%edx
4f9: 8b 45 08 mov 0x8(%ebp),%eax
4fc: 01 d0 add %edx,%eax
4fe: a3 00 00 00 00 mov %eax,0x0
503: a1 00 00 00 00 mov 0x0,%eax
508: 0f af 45 0c imul 0xc(%ebp),%eax
50c: a3 00 00 00 00 mov %eax,0x0
511: a1 00 00 00 00 mov 0x0,%eax
516: 5d pop %ebp
517: c3
503: a1 00 00 00 00
burada gördüğümüz gibi 0x503 offsetinde a1 opcode var iken 0x504-0x507 offsetlerinde 00 00 00 00 değerlerini görüyoruz.
a1 opcodemuz mov instructionın kendisi...yine tut sembolüne ilişkin offsetleri kontrol ettiğimizde oralarda 00 değerlerinin static linker tarafından yerleştirildiğini görüyoruz.
dinamik linker bu sembol tablosu üzerinden işlemler yapıcak biz de 0x504 offseti üzerinden matematiksel bir işlem yapıcaz..ve dinamik linkerın anlamaya çalışıcaz..
[21] .data PROGBITS 000016a4 0006a4 000008 00 WA 0 0 4
global değişkenlerin data sectionı üzerinde tutulduğunu ve buradaki 0x16a8 adresinin data sectiona ait adres olduğunu görüyoruz.global değişkenimizin o halde ilk segmente göre uzaklığı 0x16a8 olacak.bunu aklımızda tutalım.
daha sonra bize bu libshared.so dosyasının memorye maplenirkenki ilk segmentinin adresi lazım yani orayı 0.konum olarak düşünücez.
ilk segment adresi + 0x16a8 ise bize "tut" global değişkenimizin konumunu vericek.
ilk segment adresi + 0x504 adresinden itibaren 4 bytelık kısımdada bu adresin yerleştirildiğini görücez..
o halde bu dosyayı(libshared.so) bir executable dosyasına bağlayalım ve bu hesaplamaları yapıp kontrol edelim.burada birşeyi gözden kaçırmıyoruz librarylerin ilk LOAD segmentine bakarsak orada belirtilmiş herhangi bir adresi göremeyiz dosyanın ilk nereye yüklenileceğine dair..bu gayet normal çünkü bu libshared.so dosyasını sadece bir executable dosyası değil birçok executable dosyası kendine bağlayabilir ve bu sebeple executable dosyanın adress spacelerine yerleşeceğinden önceden sabit bir ilk adres tanımlaması libraryler için yapılamaz..ve ayrıca her yüklemede libshared.so dosyasının adreside değişecektir.
fakat hesaplamaları yapabilmemiz için libshared.so librarysinin ilk(0.konumu) nereye yüklendiğini bilmemiz gerekiyor bunuda gnu debuggerımızla halletebiliyoruz..
önce içinde sadece main fonksiyonu olan bir execle.c dosyasını oluşturalım..
int main()
{
}
ve şu parametrelerle derlemeleri gerçekleştirelim..
gcc -c execle.c -o execle.o
gcc -o execle.out execle.o -L. -lshared
dinamik linkerın libraryleri ararken baktığı directorylerden biride LD_LIBRARY_PATH içinde belirtilmiş directorydir.
bu yüzden şu işlemide yapalım...
root@kali:~/ss# export LD_LIBRARY_PATH=.
daha sonra şöyle devam edelim..
root@kali:~/ss# gdb -q execle.out
Reading symbols from execle.out...(no debugging symbols found)...done.
(gdb) b main
Breakpoint 1 at 0x80484ee
(gdb) r
Starting program: /root/ss/execle.out
Breakpoint 1, 0x080484ee in main ()
(gdb) info sharedlibrary
From To Syms Read Shared Object Library
0xf7fd9860 0xf7ff261d Yes (*) /lib/ld-linux.so.2
0xf7fd13c0 0xf7fd1518 Yes (*) ./libshared.so
0xf7e14740 0xf7f3d4bd Yes (*) /lib32/libc.so.6
görüldüğü gibi libshared.so libraryimiz yüklenmiş ve bize libsharedın ilk segmenti lazım çünkü biz hesaplamalarımızı buna göre yapıcaz.
0xf7fd13c0 adresi libshared.so dosyamızın entrypoint adresi o halde biz gerçekte libshared.so dosyamızın entrypointinin 0.konumdan ne kadar uzakta olduğunu bilirsek bu adresden bu uzaklığı çıkartarak 0.konum adresini elde etmiş oluruz..
eğer readelf -h libshared.so dersek burada entrypoint adressinin---> 0x3c0 olduğunu görürüz o halde bu kadar byte uzakta olduğunu anlıyoruz,çünkü bu adres .text sectionın başlangıc adresi ve .text sectionın kendiside ilk LOAD segmentinde olduğundan entrypointimizin dosyanın başlangıcından işte bu kadar uzaktır diyebiliyoruz..
0xf7fd13c0-0x3c0 işlemi ------ > 0xf7fd1000 işte bu dosyamızın ilk segmentinin adresi.
o halde bu adrese 0x16a8 ekleyelim çünkü libshared libraryden bu kadar uzaktaydı tut global değişkenimiz.
0xf7fd1000+0x16a8-------> 0xf7fd26a8 basit hesap.şimdi bu adresde ne var ona bakalım..
(gdb) x 0xf7fd26a8
0xf7fd26a8 <tut>: 0x00000001
peki tut için tüm anlatılanlar doğru o halde 0x504 offsetinde tut global değişkenimiz kullanılmıştı o halde dinamik linkerın bu adrese yerleştirme(relocation) işlemini yapmış olmasını bekliyoruz..tabi bu adres ilk segmente göre göreceli bir adres ve yükleme esnasında değişti. ilk segmentin adresi elimizde o halde bununda hesapını yapalım..
0xf7fd1000+0x504 ------ > 0xf7fd1504 kolay hesap.şimdi de bu adresi inceleyelim..
(gdb) x 0xf7fd1504
0xf7fd1504 <multiply+20>: 0xf7fd26a8
evet görüldüğü gibi 00 00 00 00 değerleri yerine dinamik linkerımız 0xf7fd26a8 yani tut değişkenimizin adresini yerleştirmiş..
hatta diğer yerleştirmeleride görmek için gnu debuggerımız ile disass ediyoruz..
(gdb) disass /r multiply
Dump of assembler code for function multiply:
0xf7fd14f0 <+0>: 55 push %ebp
0xf7fd14f1 <+1>: 89 e5 mov %esp,%ebp
0xf7fd14f3 <+3>: 8b 15 a8 26 fd f7 mov 0xf7fd26a8,%edx
0xf7fd14f9 <+9>: 8b 45 08 mov 0x8(%ebp),%eax
0xf7fd14fc <+12>: 01 d0 add %edx,%eax
0xf7fd14fe <+14>: a3 a8 26 fd f7 mov %eax,0xf7fd26a8
0xf7fd1503 <+19>: a1 a8 26 fd f7 mov 0xf7fd26a8,%eax
0xf7fd1508 <+24>: 0f af 45 0c imul 0xc(%ebp),%eax
0xf7fd150c <+28>: a3 a8 26 fd f7 mov %eax,0xf7fd26a8
0xf7fd1511 <+33>: a1 a8 26 fd f7 mov 0xf7fd26a8,%eax
0xf7fd1516 <+38>: 5d pop %ebp
0xf7fd1517 <+39>: c3 ret
ve sembol tablosunda belirtilen offsetlere 0xf7fd26a8 adresinin yerleştirildiğini görüyoruz,görmesek bile hesaplamalarımızla zaten bunun olacağını biliyorduk..
yalnız burada a1 opcodemuz kafa karıştırıcı olabilir bu kodun karşılığını şu şekilde ..
--> 0xf7fd1503 <+19>: a1 a8 26 fd f7 mov eax,ds:0xf7fd26a8
bu opcodeun açıklamasıda şöyle ---->
A1 MOV EAX,moffs32 Move dword at (seg:offset) to EAX
yani asıl olarak a1 opcodenun yaptığı iş ---> mov eax, [0xf7fd26a8]
load-time da variable/değişkenlerimiz için relocation işleminin dinamik linker tarafından nasıl yapıldığını anladık.
fonksiyonlar için load-time da nasıl bir yerleştirme işlemi yapılıyor bunu inceleyelim bu yüzden shared.c dosyamızı şöyle yapıyoruz :
int tut=1;
int subtract(int x,int y)
{
return x-y;
}
int multiply(int a,int b)
{
tut=tut+a;
tut=tut*b;
int fark=subtract(a,b);
return tut;
}
bu dosyayı libshared_new.so haline getiriyoruz..ve sonra readelf ile relocationa ait bilgileri okuyoruz..
root@kali:~/ss# gcc -c shared.c -o shared_new.o
root@kali:~/ss# gcc -shared -o libshared_new.so shared_new.o
root@kali:~/ss# readelf -r libshared_new.so
Relocation section '.rel.dyn' at offset 0x334 contains 14 entries:
Offset Info Type Sym.Value Sym. Name
00000533 00000d01 R_386_32 00001720 tut
0000053d 00000d01 R_386_32 00001720 tut
00000542 00000d01 R_386_32 00001720 tut
0000054b 00000d01 R_386_32 00001720 tut
00000561 00000d01 R_386_32 00001720 tut
00000556 00000902 R_386_PC32 00000520 subtract
subtract fonksiyonumuzu burada görmekteyiz 0x556 offsetini inceleyelim..
555: e8 fc ff ff ff call 556 <multiply+0x2b>
55a: 83 c4 08 add $0x8,%esp
55d: 89 45 fc mov %eax,-0x4(%ebp)
560: a1 00 00 00 00 mov 0x0,%eax
call emrininin opcode karşılı e8 ve hemen sonrasında 0x556 adresinden itibaren fc ff ff ff adresi bulunmakta. (tabiki bu değer 2'ye tümlemeli yani bu ifade aslında -4),ve call emrininin çalışma biçimi ise bir sonraki adrese göre göreceli atlama yapması bu durumda 55a+(-4) --- > 0x556 yani kendi üzerine zıplama yapıcak.(call 556 ile bunu görmekteyiz..)
peki dinamik linker subtract function için static linker tarafından bırakılan R_386_PC32 relocation typedan ne anlıyor. şunu anlıyor..
offsette belirtilen değeri al --- > 0xfffffffc
sembolün adresini bununla topla --- > 0xfffffffc+0x520
daha sonra bu değerden offsetin kendisini çıkar ---> 0xfffffffc+0x520-0x556
elde edilen bu sonuçu al belirtilen offsete yerleştir..
bu dediklerimizi libraryimiz yüklendikten sonra gerçek değerler üzerinden yapmak için gnu debugger ile işe koyuluyoruz..
yalnız incelemeye başlamadan önce libshared_new.so ismini libshared.so olarak değiştiriyoruz.çünkü dynamic linker bu libraryi yükleyecek programın address alanına..
root@kali:~/ss# gdb -q execle.out
reading symbols from execle.out...(no debugging symbols found)...done.
(gdb) break main
Breakpoint 1 at 0x80484ee
(gdb) r
Starting program: /root/ss/execle.out
Breakpoint 1, 0x080484ee in main ()
(gdb) info sharedlibrary
From To Syms Read Shared Object Library
0xf7fd9860 0xf7ff261d Yes (*) /lib/ld-linux.so.2
0xf7fd13f0 0xf7fd1567 Yes (*) ./libshared.so
0xf7e14740 0xf7f3d4bd Yes (*) /lib32/libc.so.6
libshared.so nun entrypoint adresini dosyanın başlangıcından ne kadar uzakta olduğunu ELF headera bakarak öğreniyoruz ve entrypoint adresinden çıkartıyoruz..
şöyle -------------> 0xf7fd13f0-0x3f0 --------> 0xf7fd1000 --> ilk segmentin başlangıc adresi
0xf7fd1000+0x556---------->0xf7fd1556 relocationın offseti
0xf7fd1000+0x520---------->0xf7fd1520 subtract sembolümüzün adresi.
bunların üzerinde yapılanlar yukarıda bahsettiğimiz gibi..biz burada artık final adresler
üzerinde hesaplamalar yapıyoruz buna dikkat ediniz..!
offsette(0xf7fd1556) belirtilen değeri al..orada ilkin 0xfffffffc olduğunu biliyoruz..
sembolün adresini(0xf7fd1520) bununla(0xfffffffc) topla...
0xfffffffc+0xf7fd1520------->0x1f7fd151c
daha sonra bu değerden(0x1f7fd151c) offsetin(0xf7fd1556) kendisini çıkar..
0x1f7fd14e6-0xf7fd1556 ---------> 0xffffffc6 ve bu değeri offsetin belirtiği adrese yerleştir.
bakalım herşey load-time da bu şekilde olmuşmu ? yerleştirme yapılmış olan offsetimize bakıyoruz..
(gdb) x 0xf7fd1556
0xf7fd1556: 0xffffffc6 --------> hesapladığımız gibi..
bu değer -3a ya karşılık geliyor.call instruction bir sonraki adrese göre göreceli bir atlama yaptığından
0xf7fd155a+(-3a) --------> 0xf7fd1520 adresine atlama yapıcağı anlamına geliyor.
0xf7fd1555 <+42>:e8 c6 ff ff ff call 0xf7fd1520 <subtract>
0xf7fd155a <+47>:83 c4 08 add $0x8,%esp
sanırım fonksiyonlar içinde load-time esnasında relocation,yeniden yerleştirmeyi anladık.
eğer bu libraryinin içindeki tut değişkeni executable file içerisinden kullanılmak istenilirse ne olacak ?aynı şekilde fonksiyonun kendiside...
birincisi dinamik linkerdan daha önce executable dosyanın LOAD segmentleri yüklendiğinden daha sonra dinamik linkerın programın adres alanına yüklendiğinden ve kontrolü dinamik linker relocationlar için başlangıcda ele aldığından ve executable dosyanın .text sectionına müdahalede bulunulamayacağından(sonuçta yerleştirme,writable bir işlem) birşeylerin daha önceden static linker tarafından halledilmesi gerek.
son olarak bunuda inceleyip yazımızı bitirelim..
öncelikle execle.c şu şekilde olsun ...
#include <stdio.h>
extern int tut;
extern int multiply(int,int);
int main(){
printf("tutun adresi --- > %p",&tut);
}
root@kali:~/ss# gcc -o execle.o -c execle.c
root@kali:~/ss# gcc -o execle1.out execle.o -L. -lshared
root@kali:~/ss# ./execle1.out
tutun adresi --- > 0x804981c
static linker "tut" değişkeni uninitialized data olması sebepiyle bunun için bss sectionında yer ayarlıyor.ayrıca bu sectionın kendisi writable olduğundan dinamik linker tarafından load-time esnasında buraya yazma yapılabilir.
bu bilgilerden sonra önce readelf ve daha sonra gnu debugger ile dosyamızı şu şekilde inceliyoruz.
root@kali:~/ss# readelf -r execle1.out
Relocation section '.rel.dyn' at offset 0x3b4 contains 2 entries:
Offset Info Type Sym.Value Sym. Name
080497fc 00000306 R_386_GLOB_DAT 00000000 __gmon_start__
0804981c 00000d05 R_386_COPY 0804981c tut
R_386_COPY relocationı dinamik linkerımıza der ki git libshared.so içerisindeki tut değişkeninin değerini 0x804981c'ye yaz.
fakat bununla birlikte dinamik linkerımız birşey daha yapar,o da libshared.so içerisindeki tut değişkenin adresini bu değerle ezmesidir..
ne demek bu yani işte onuda şu şekilde görüyoruz..
(gdb) disass /r multiply
Dump of assembler code for function multiply:
0xf7fd152b <+0>: 55 push %ebp
0xf7fd152c <+1>: 89 e5 mov %esp,%ebp
0xf7fd152e <+3>: 83 ec 10 sub $0x10,%esp
0xf7fd1531 <+6>: 8b 15 1c 98 04 08 mov 0x804981c,%edx -----------> !
0xf7fd1537 <+12>: 8b 45 08 mov 0x8(%ebp),%eax
0xf7fd153a <+15>: 01 d0 add %edx,%eax
0xf7fd153c <+17>: a3 1c 98 04 08 mov %eax,0x804981c -----------> !
0xf7fd1541 <+22>: a1 1c 98 04 08 mov 0x804981c,%eax -----------> !
0xf7fd1546 <+27>: 0f af 45 0c imul 0xc(%ebp),%eax
0xf7fd154a <+31>: a3 1c 98 04 08 mov %eax,0x804981c -----------> !
0xf7fd154f <+36>: ff 75 0c pushl 0xc(%ebp)
0xf7fd1552 <+39>: ff 75 08 pushl 0x8(%ebp)
0xf7fd1555 <+42>: e8 c6 ff ff ff call 0xf7fd1520 <subtract>
0xf7fd155a <+47>: 83 c4 08 add $0x8,%esp
0xf7fd155d <+50>: 89 45 fc mov %eax,-0x4(%ebp)
0xf7fd1560 <+53>: a1 1c 98 04 08 mov 0x804981c,%eax -----------> !
0xf7fd1565 <+58>: c9 leave
0xf7fd1566 <+59>: c3 ret
End of assembler dump.
yukarıda ! koyduğumuz yerler libsharedin tut değişkenine baskın geliyor, ve 0x804981c adresi yerleştiriliyor.buraya normalde ne adres gelicekti derseniz bunun hesapını ilk örneğimizde(taaa yukarlarda) yapmıştık..
loadtime relocationları elimizden geldiğince anlatmaya/anlamaya çalıştık.
yalnız loadtime relocationların kendileri bu şekliyle bazı modern sistemler(x86-64) tarafından artık desteklenmiyor..
o zaman niye bundan bahsettik ? çünkü dynamic relocation optimizasyonu için işin içine position independent code (PIC) da giricek...
loadtime relocationı anlamakla position independent code shared librarylere uygulanmasını daha iyi anlıyacağız..neden böyle bir yönteme gereksinim duyulduğunu daha basit kavrıyacağız..
nınızay unos...---> little endian/big endian ?
Hiç yorum yok:
Yorum Gönder