linux下elf重定位理解

準備:可重定位文件(Relocatable file),可執行文件(Executable file),共享文件(Shared object file)。


Relocatable file: an object file that holds code and data suitable for linking with other object files to create an executable or a shared object file. In other word, you can say that relocatable file is a foundation for creating executables and libraries.

This is kind of file you get if you compile a source code like this:

$ gcc -c test.c

That will produce test.o, which is a relocatable file.

Kernel module (either suffixed with .o or .ko) is also a form of relocatable file.

Executable file: object file that holds a program suitable for execution. Yes, that means, your XMMS mp3 player, your vcd software player, even your text editor are all ELF executable files.

This is also a familiar file if you compile a program:

$ gcc -o test test.c

After you make sure the executable bit of "test" is enabled, you can execute it. The question is, what about shell script? Shell script is NOT ELF executable, but the interpreter IS.

Shared object file: This file holds code and data suitable for linking in two contexts:

The link editor may process it with other relocatable and shared shared object file to create another object file.

The dynamic linker combines it with an executable file and other shared objects to create a process image.

In simple words, these are the files that you usually see with suffix .so (normally located inside /usr/lib on most Linux installation).



重定位:就是把符號的value進行重新定位,value可是地址也可是其他類型值.要重點理解

編譯器、連接器和裝載器各個職能,熟悉編譯、連接和加載過程、符號(symbol)、段(section)等概念


符號:高級語言經編譯處理後,在binary層面上,統一以符號表示各個變量、函數實體。是連接和重定位橋

樑。不同編譯器有不同的地符號影射,比如c語言中,以前有的編譯器固定變量前加下劃線 int a ==>_a


段:binary層面對指令和數據的組織,方便管理、理解。深入參考彙編中有關段概念。

符號和段都是屬於ELF表示範疇,是對binary層面的描述。深入可參考ELF或者coff相關文檔



重定位分爲兩大類:普通和動態。普通就是連接時候重定位,動態是指加載、運行時

重定位,一般有程序啓動時動態加載符號來重定位,和函數符號被調用時候動態加載。


重定位過程要用到一些表,來輔助定位,有.rel.text、 .rel.dyn和.rel.plt、.plt.下面根據他們來展開說明。


.rel.text

重定位的地方在.text段內,以offset指定具體要定位位置。在連接時候由連接器完成。注意比較.text段前後變化。指的是比較.o文件和最終的執行文件(或者動態庫文件)。就是重定位前後比較,以上是說明了具體比較對象而已。


.rel.dyn

重定位的地方在.got段內。主要是針對外部數據變量符號。例如全局數據。重定位在程序運行時定位,一般是在.init段內。定位過程:獲得符號對應value後,根據rel.dyn表中對應的offset,修改.got表對應位置的value。另外,.rel.dyn 含義是指和dyn有關,一般是指在程序運行時候,動態加載。區別於rel.plt,rel.plt是指和plt相關,具體是指在某個函數被調用時候加載。


.rel.plt

重定位的地方在.got.plt段內(注意也是.got內,具體區分而已)。 主要是針對外部函數符號。一般是函數首次被調用時候重定位。可看彙編,理解其首次訪問是如何重定位的,實際很簡單,就是初次重定位函數地址,然後把最終函數地址放到.got.plt內,以後讀取該.got.plt就直接得到最終函數地址(參考過程說明)。  所有外部函數調用都是經過一個對應樁函數,這些樁函數都在.plt段內。


過程說明:調用對應樁函數--->樁函數取出.got表(具體是.got.plt)表內地址--->然後跳轉到這個地址.如果是第一次,這個跳轉地址默認是樁函數本身跳轉處地址的下一個指令地址(目的是通過樁函數統一集中取地址和加載地址),後續接着把對應函數的真實地址加載進來放到.got.plt表對應處,同時跳轉執行該地址指令.以後樁函數從.got.plt取得地址都是真實函數地址了。


.plt段,存放重定位樁函數的。


重要區別

.rel.text屬於普通重定位輔助段 ,他由編譯器編譯產生,存在於obj文件內。連接器連接時,他 用於最終可執行文件或者動態庫的重定位。通過它修改原obj文件的.text段後,和並      到 最終可執行文件或者動態文件的.text段。

注:readelf -r a.o 查看 .rel.text。其類型一般爲R_386_32和R_386_PC32



.rel.dyn和.rel.plt是動態定位輔助段。由連接器產生,存在於可執行文件或者動態庫文件內。藉助這兩個輔助段可以動態修改對應.got和.got.plt段,從而實現運行時重定位。

.rel.dyn 對應地點在.got表內;.rel.plt  在.got.plt,注意不是在.text,這點和普通不同,也是重要點。


.rel.text由編譯器產生,然後在連接時候,由鏈接器負責根據.rel.text對.text段進行修改,從而達到重定位目的;


.rel.dyn和.rel.plt由連接器產生,然後在運行時候,動態加載符號地址。

對於數據,根據.rel.dyn找到.got中的offset位置;

對於函數則通過.plt樁函數和.rel.plt段來獲取函數真實地址,然後存在於.got.plt。

要理解動態連接中訪問外部符號是通過.got和.got.plt

注1:規律:.rel.plt和.got.plt偏移 有對應,另外和.dynsym好似有次序對應關係(不很確定)。通過這可方便解析函數符號地址,詳細可參考連接器解析函數(連接器一般是動態加載,像全局數據符號那樣,程序啓動時加載)。


注2:readelf -r a.out 查看 .rel.dyn 和.rel.plt.其中 .rel.dyn 類型一般爲R_386_GLOB_DAT和R_386_COPY;.rel.plt爲R_386_JUMP_SLOT



例子在此不舉了,分析這個重定位過程,最好還是自己動手寫個例子來,比單純看文章好理解。

分析沒多久,還有好多問題需要慢慢分析透徹,大家多多指正。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章