18 Ağustos 2016 Perşembe

LOAD-TIME RELOCATION

LOAD-TIME RELOCATION

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