ELF檔案重定位

來源:互聯網
上載者:User


為了保持簡潔,本文省去了對部分細節的描述,例如weak型變數,和GOT等重定位類型。
本文假設讀者瞭解ELF檔案格式。代碼在附件中。
如果論壇不支援附件,可以發郵件給我要
1 ELF檔案的裝載
在ELF檔案中,使用section和program兩種結構描述檔案的內容。通常來說,ELF可重定位檔案採用section,ELF可執行檔使用program,可重連結檔案則兩種都用。
裝載檔案,其實是一個很簡單的過程,通過section或者program中的type屬性判斷是否需要載入,然後通過offset屬性找到檔案中的資料,將它讀取(複製)到相應的記憶體位置就可以了。  這個位置,可以通過program裡面的vaddr屬性確定;對於section來說,則可以自己定義裝載的位置。
2 ELF檔案的重定位
動態串連的本質,就是對ELF檔案進行重定位和符號解析。
重定位可以使得ELF檔案可以在任意的執行(普通程式在連結時會給定一個固定執行地址);符號解析,使得ELF檔案可以引用動態資料(連結時不存在的資料)。
從流程上來說,我們只需要進行重定位。而符號解析,則是重定位流程的一個分支。

先讓我們簡單的介紹一下重定位的原理。
假設我們寫出了一句彙編原始碼
jmp dxc
dxc:
……
假設dxc這個標號的地址1000h。
那麼在編譯連結之後,就變成了jmp 1000h。
如果我們在運行時移動了程式的位置,1000h就會指向錯誤的地址(因為我們已經不在那裡了)。而當我們使用了外部符號時(例如動態連結程式庫裡面的函數),在連結時根本就不知道這個符號在哪裡,所以也沒有辦法產生這個1000h的地址。
重定位的目的,就是在啟動並執行時候修改這個1000h地址,使其指向正確的地址。(連結時也需要重定位,暫且不提)。
為了進行重定位,我們需要三個資料。
1是進行重定位的地址,也就是jmp 1000h這條指令中運算元的地址,也就是1000h自己在記憶體中的地址。(你可以把它想象為C指標自己的儲存地址&point)
2是需要指向的符號,上例中就是dxc這個標號。通過對這個符號進行解析,就可以得到運行時該標號的正確地址。(你也可以把它想象成C指標point所指向的地址)
3是重定位的類型,例如R_386_32表示絕對位址的重定位;R_386_PC32表示對相對位址的重定位。前者可以直接使用符號的地址,後者則要用“符號地址-重定位地址”得出相對位址。 其它關於重定位類型,請參考ELF白皮書。

重定位表,是一個由許多重定位表項組成的數組。
下面是ELF裡面重定位項的結構
struct elf32_rel {
  Elf32_Addr  r_offset;
  Elf32_Word   r_info; //SYMBOL<<8+TYPE&0xff. 
} ;
r_offset是需要進行重定位的地址;
SYMBOL是重定位以後需要指向的符號;
TYPE是重定位的類型。

只要我們遍曆所有的“重定位節”,對其中的所有重定位項進行遍曆,就可以實現重定位了。
3 ELF檔案的符號解析
在上面的演算法中,我們提到 “2通過對符號進行解析,就可以得到運行時該符號的正確地址”,至於具體要怎麼做,就是“符號解析”的工作了。
所謂的“符號解析”,實際上就是:通過給定的符號名,找到該符號在記憶體中的正確地址。
在ELF檔案中,符號解析是通過“符號表”和“符號名表”實現的。

符號名表,是許多變長字串的合集,並且以兩個’\0’作為結尾。

符號表,是由許多符號表項組成的數組。
下面是“符號表項”的結構, 
struct elf32_sym{
  Elf32_Word st_name; //index into the symbol string table
  Elf32_Addr st_value;
  Elf32_Word st_size; //size of the symbol. 0 for no size or unkown size 
  unsigned char st_info; //BIND<<4+TYPE&0x0f
  unsigned char st_other; //0 for reserve
  Elf32_Half st_shndx; //relevant section table index, some indicates special meanings
};
St_name是符號的名稱的index(詳見下文)
St_value是該符號在記憶體中的地址(詳見下文)
st_size是該符號的大小,以位元組為單位
BIND說明符號是內部符號還是外部符號
TYPE說明符號的類型,是函數,還是變數,等等
st_other恒為0,保留位元組
st_shndx是符號所在的section的index(詳見下文)

符號的名稱,是由st_name和符號名表決定。St_name是一個指向符號名表的索引值,通過“符號名表基地址”+st_name就可以得到符號名的地址。
之所以採用這種方法,是為了處理“變長”的符號名。
我們知道,不同符號名的長度也會有很大的不同。例如int I;的符號名只有1個字元。而int mMyLinkTypeofDoubleLoaderProgram卻有34個字元。如果採用數組的方式,會浪費大量的空間,malloc出來的動態記憶體又不適合硬碟儲存。
這是一個非常值得學習的技巧――在涉及硬碟儲存時,可以採用索引+字串表的形式儲存變長的字串(或者其它變長資訊)。

符號的值(也就是符號在記憶體中的地址,我們要計算的東西)是由st_value和st_shndx決定的。
在relocatable檔案中,st_value是符號相對於某個section起始地址的位移,這個section是由st_shndex指定的(COMMON類型的section除外,它很少會被用到)。
在executable和shared object檔案中,st_value包含一個虛擬位址。這個地址是和ELF檔案的預定裝載地址聯絡在一起的。在進行動態連結時,我們需要計算“當前裝載地址”與“預定裝載地址”之間的差。
4 ELF裝載函數的設計
整體流程:
1遍曆section header table,擷取所有需要裝載的節,以及重定位節的資訊
2裝載所有需要裝載的節
3對所有重定位節,執行重定位。

重定位流程:
1遍曆某個重定位節,對其中的每一個重定位項,執行以下處理
a求出需要進行重定位的地址(P)
b求出重定位的目標地址(S),若該符號無法解析,則置S為0
c1若S不為0,則執行重定位:
R_386_32:*P = *P + S
R_386_PC32:*P=*P + S - P
C2若S為0,表明該符號解析失敗,要進行失敗處理。
在我的程式中,處理方法為:在REST RELOCATION數組中加入一個新項,待適當的時機再進行重解析。

原始碼中有些部分是和我的組件式作業系統設計有關的,在閱讀代碼時可跳過不看。
通常的動態連結程式,會採用“依賴性”載入的方式;而我採用的是“rest relocation list”的方式,請讀者閱讀代碼時注意,如果對這個技術不感興趣,也可跳過不看。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.