ELF學習--重定位文件


add.c

int data = 1;
int bss;
const int rodata = 1;
int add(int num1, int num2)
{
  int sum = 0;
  sum = num1 + num2;
  return sum;
}

編譯add.c成.o文件

gcc -c add.c(-c表示只編譯不鏈接)

file add.o輸出結果如下:

此結果表明add.o是個重定位文件。

查看elf header可查看到更詳細信息:readelf -h add.o

由於是重定位文件,ELF中並沒有program header,entry point為0x0, section header的字符串表在section 中的索引為9(在下面的section header中對應.shstrtable section).ELF header的size為52. 有12個section,每個section header的 size為40。

重定位文件的ELF格式的布局如下。

 

通過readelf -S add.o 查看section header。

 

Off這一列是表示section在ELF文件中的偏移量,.text的偏移量是0x34,轉換成十進制正好是52(ELF header大小),說明header后面緊接着是.text。

.text內容可以通過objdump來查看:objdump -s -d add.o(-s表示將內容以16進制打印出來,-d表示反匯編)

.text只包含add函數,其大小為0x1d.

 

.data保存了初始化了的全局變量和靜態變量。在add.c中.data只包含data這一個int型變量,所以其size為4。

.data的偏移量為0x34+0x1d=0x51,由於.data是4字節對其的,所以offset為0x54.

.rodata保存的是只讀變量(如const修飾)和字符串常量。在add.c中只讀變量為int型,大小為4.

 

.bss保存的是未初始化的全局變量和局部靜態變量。按道理int bss是應該存在.bss段中,但是.bss的size是0.這其實與編譯器相關,有些編譯器並不會將未初始化的全局變量放在.bss中,只是預留一個未定義的全局變量符號,等到最終鏈接成可執行文件的時候再分配.bss分配空間。這與強符號和弱符號相關。

 

我們在通過readelf -s add.o看一下ELF的符號表:

其中我們看到符號的value大部分是0;如果符號是函數和變量,那么符號的value就是函數和變量的地址。

符號的value分為以下幾種情況:

1.如果ELF文件是目標文件,符號不是“COMMON”類型,函數和變量的地址是不確定的,要等到鏈接后才知道,所以value值是符號其所在section中的偏移。

2.如果ELF文件是目標文件,符號是“COMMON”類型的,value表示該符號的對其屬性。

3.如果ELF文件是可執行文件,value是符號的虛擬地址。

 

add符號是在代碼段,所以Ndx項是1,從前面的section header可一看到.text的下標正好是1.其他符號如data,rodata以此類推。

其中名字沒有顯示,type是SECTION的符號,表示下標為Ndx段的section名字。比如Num 2的Ndx為1,那么他表示.text section名稱,即".text".可以通過objdump -t查看符號表,可以獲取所有符號名,即其說在的section名字。

 

我們在寫一個簡單的程序,准備調用add.c中的add函數,

#include <stdio.h>
int main()
{
  int sum = add(1,2);
  printf("sum is: %d\n", sum);
  return 0;
}

gcc -c main.c

查看section header:

多了一個section .rel.text,說明.text中有需要重定位的部分。

查看符號表和重定位表:

readelf -s main.o

 重定位表:objdump -r main.o

符號add和printf是undefined,需要重定位以確定地址。

add和printff分別在main.o的0x19和0x31的位置需要重新定位。

objdump -d main.o反編譯一下:

sum和printf的指令碼都為e8 fc ff ff ff.

e8是call的指令碼,通過file命令可以看出main.o是小端,所以在重定位前sum和printf的地址均為0xfffffffc,他是常量-4的補碼。

我們再將main.c和add.c靜態鏈接起來,

gcc main.c add.c -o test

可以看到形成可執行文件后,main.o中調用add函數的地址被重新修正。

現在的add函數的指令碼為e8 1f 00 00 00;

R_386_PC32是相對尋址。對於需要重定位的符號,他修正后的結果為S+A-P.

S是add的實際地址0x08048459

A是修正前的值0xfffffffc,即-4;

P是需要修正的位置,當鏈接成可執行文件后,這個值是被修正位置的虛擬地址。0x804841d +0x19;

0x8048459-4 -(0x804841d+0x19) = 0x1f 與上述修正后的指令碼“e8 0x1f 00 00 00”對應。

 在重定位表中,我們還可以看到有個R_386_32類型的重定位項。

R_386_32是絕對尋址方式,其修正后的值是S+A.

S為符號的實際地址,A是被修正的位置。

對於.rodata這個需要重新定位的項,是printf中的常量字符串。S為0x000000

我們查看可執行文件的16進制內容,objdump -x test

那么修正后的值即為0x8048510


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM