ELF文件長什么樣子(1/2)

和JavaScript這樣的解釋執(zhí)行語言不同,編譯執(zhí)行的語言通過編譯、鏈接最終生成可執(zhí)行文件。

ELF(Executable and Linkable Format),指.o文件和可執(zhí)行文件,本文分析.o文件格式。

1. 示例程序

demo.s

#understanding .obj sections and .elf segments
.section .data
mydata1:
    .ascii "This is my data1 xxx."
mydata2:
    .ascii "This is my data2 xxx."
.section .text
.globl _start
_start:
    movl $mydata1, %edi #replace "xxx" with "1st"
    movb $49, 17(%edi)  #"1st"
    movb $115, 18(%edi)
    movb $116, 19(%edi)
 
    movl $mydata2, %edi #replace "xxx" with "2nd"
    movb $50, 17(%edi)  #"2nd"
    movb $110, 18(%edi)
    movb $100, 19(%edi)
 
    movl $1, %eax   #syscall exit
    movl $0, %ebx   #argument 0, ie. exit(0)
    int $0x80

上述程序的功能是將兩段字符串中"XXX"分別替換為"1st"和"2nd"
編譯
as -o demo.o demo.s
得到demo.o文件

鏈接
ld -o demo demo.o
得到demo可執(zhí)行文件。

2. 調(diào)試

加載到調(diào)試器中
gdb demo

設(shè)置斷點
break *_start
讓程序在_start處暫停。

查看當前指令
display/2i $eip
查看eip指針指向的兩條指令。顯示格式為I (instruction)

運行
r
運行,顯示輸出:

1: x/2i $eip 
=> 0x8048074 <_start>:    mov $0x80490a4,%edi 0x8048079 <_start+5>: 
                     movb $0x31,0x11(%edi)

0x80490a4即為mydata1在內(nèi)存中的起始地址。

查看mydata
display/5s 0x80490a4
顯示地址0x80490a4開始的5條字符串。輸出格式為s(string)

執(zhí)行下一條指令
ni
輸入ni,執(zhí)行next instruction。然后一直按回車,重復(fù)執(zhí)行ni。
最終顯示輸出:

2: x/5s 0x80490a4
0x80490a4 <mydata1>:     "This is my data1 1st.This is my data2 2nd."
0x80490cf:   ".symtab"
0x80490d7:   ".strtab"
0x80490df:   ".shstrtab"
0x80490e9:   ".text"

"This is…."存放.data節(jié)中,".sysmtab",".strtab"等字符串存放在.shstrtab 節(jié)中。最終加載到內(nèi)存中,.shstrtab剛好位于.data節(jié)后,所以將.shstrtab中的內(nèi)容也打印出來了。(結(jié)論:.shstrtab也是會被加載到內(nèi)存中的。)

3. 分析.o文件的結(jié)構(gòu)。

輸出elf文件內(nèi)容
readelf -a demo.o > elf.demo.o
用readelf工具將 demo3.o的內(nèi)容輸出到elf.demo3.o中。

反匯編.text段
objdump -d demo.o > objdump.demo.o
用objdump工具將.text段的代碼反匯編,輸出到objdump.demo.o中

說明:下面內(nèi)容中中括[]號內(nèi)表示文件中的起止位置(包括首尾),單位為byte,計數(shù)進制為16進制, 圓括號()表示不包含首尾字節(jié)。

(1) ELF Header [0,33](共52 bytes)

文件開始的52byte為ELF Header,這52字節(jié)按如下結(jié)構(gòu)進行解釋
elf32_hdr結(jié)構(gòu)體

typedef struct elf32_hdr{
  unsigned char e_ident[EI_NIDENT];
  Elf32_Half    e_type;
  Elf32_Half    e_machine;
  Elf32_Word    e_version;
  Elf32_Addr    e_entry;  /* Entry point */
  Elf32_Off e_phoff;
  Elf32_Off e_shoff;
  Elf32_Word    e_flags;
  Elf32_Half    e_ehsize;
  Elf32_Half    e_phentsize;
  Elf32_Half    e_phnum;
  Elf32_Half    e_shentsize;
  Elf32_Half    e_shnum;
  Elf32_Half    e_shstrndx;
} Elf32_Ehdr;

本實例顯示內(nèi)容為:
ELF Header

(2) .text [34,61](共46 bytes)

.text存放的是匯編代碼,共2e(即十進制的46) 字節(jié)。
.text

   0:   bf 00 00 00 00          mov    $0x0,%edi
   5:   c6 47 11 31             movb   $0x31,0x11(%edi)
   9:   c6 47 12 73             movb   $0x73,0x12(%edi)
   d:   c6 47 13 74             movb   $0x74,0x13(%edi)
  11:   bf 15 00 00 00          mov    $0x15,%edi
  16:   c6 47 11 32             movb   $0x32,0x11(%edi)
  1a:   c6 47 12 6e             movb   $0x6e,0x12(%edi)
  1e:   c6 47 13 64             movb   $0x64,0x13(%edi)
  22:   b8 01 00 00 00          mov    $0x1,%eax
  27:   bb 00 00 00 00          mov    $0x0,%ebx
  2c:   cd 80                   int    $0x80

(3) 填充字符 [62,63]

用00 00填充。

(4) .data [64,8D](共42 bytes)

.data段對應(yīng)匯編語言中的.data段,存放了兩個字符串,共2a (即十進制的42)字節(jié)。
.text段后有兩個空字符 00 00,然后才是.data段。
.data

This is my data1 xxx.This is my data2 xxx.

(5) 填充字符 [62,63]

用 00 00填充。

(6) .bss (90,90)(共0bytes)

.bss段,只有section header,沒有對應(yīng)section。所以它對應(yīng)的section大小設(shè)置為0字節(jié)。.bss段不占用.o文件中的空間,當進程被加載到內(nèi)存中時,才為.bss分配空間,并用0填充該空間。

(7) .shstrtab [90,BF](共48 bytes)

.shstrtab 即 section header string table,存放的是各個section名稱字符串。其中第一個字符串為'\0'. 此處為30 (即十進制的48)字節(jié)。
.shstrtab

""
".symtab"
".strtab"
".shstrtab"
".rel.text"
".data"
".bss"

上面字符串按照標準C語言格式表示的,即 “.symtab”實際由: '.' 's' 'y' 'm' 't' 'a' 'b' '\0' 字符組成。每個字符串用'\0'結(jié)尾,在文件視圖中顯示為一個點“.”(16進制00)。另外,名稱

注意:這些字符串之間并沒有用換行分隔,這里只是為了顯示清晰,每個字符串單獨占一行。

(8) Section Headers [C0,1FF](共320 bytes)

section headers 處存放的是用來描述各個section的section header,每個section header占40 bytes, 這里共有8個section header。
每個占用40個字節(jié)的section header根據(jù)如下結(jié)構(gòu)體解釋:
Elf32_Shdr

typedef struct {
  Elf32_Word    sh_name;
  Elf32_Word    sh_type;
  Elf32_Word    sh_flags;
  Elf32_Addr    sh_addr;
  Elf32_Off sh_offset;
  Elf32_Word    sh_size;
  Elf32_Word    sh_link;
  Elf32_Word    sh_info;
  Elf32_Word    sh_addralign;
  Elf32_Word    sh_entsize;
} Elf32_Shdr;

本示例中,各個section header的有效信息如下:
section headers

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .text             PROGBITS        00000000 000034 00002e 00  AX  0   0  4
  [ 2] .rel.text         REL             00000000 000288 000010 08      6   1  4
  [ 3] .data             PROGBITS        00000000 000064 00002a 00  WA  0   0  4
  [ 4] .bss              NOBITS          00000000 000090 000000 00  WA  0   0  4
  [ 5] .shstrtab         STRTAB          00000000 000090 000030 00      0   0  1
  [ 6] .symtab           SYMTAB          00000000 000200 000070 10      7   6  4
  [ 7] .strtab           STRTAB          00000000 000270 000018 00      0   0  1

第0條對應(yīng)ELF Header.

(9) .symtab [200,26F](共112 bytes)

.symtab 存放的是代碼中使用到的符號字符串索引以及它對應(yīng)的地址、屬性(local,global,weak global)等信息。在這里共占用0x70(即十進制的112)字節(jié)。
其中每一項占用16個字節(jié),這里共有7項,所以占用112字節(jié)。每項都通過如下結(jié)構(gòu)體進行解釋
Elf32_Sym

typedef struct elf32_sym{
  Elf32_Word    st_name;
  Elf32_Addr    st_value;
  Elf32_Word    st_size;
  unsigned char st_info;
  unsigned char st_other;
  Elf32_Half    st_shndx;
} Elf32_Sym;

在本示例中,.symtab中7項內(nèi)容的有效信息如下:

Symbol table '.symtab' contains 7 entries:
   Num:    Value  Size Type    Bind   Vis      Ndx Name
     0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 00000000     0 SECTION LOCAL  DEFAULT    1 
     2: 00000000     0 SECTION LOCAL  DEFAULT    3 
     3: 00000000     0 SECTION LOCAL  DEFAULT    4 
     4: 00000000     0 NOTYPE  LOCAL  DEFAULT    3 mydata1
     5: 00000015     0 NOTYPE  LOCAL  DEFAULT    3 mydata2
     6: 00000000     0 NOTYPE  GLOBAL DEFAULT    1 _start

1~3條分別表示索引號為1、3、4的section的名稱,即.text,.data和.bss。第0條默認為STN_UNDEF(undefined)類型

其中,后三項對應(yīng)代碼中的三個符號。

(10) .strtab [270,287](共24 bytes)

.strtab即 string table 用來存放在代碼中定義的符號的字符串。
和.shstrtab一樣,第一個字符串為'\0',每個字符串以'\0'結(jié)尾。

.strtab

""?"mydata1"?"mydata2"?"_start"

(11) .rel.text [288,297](共16 bytes)

.rel.text存放的是.text節(jié)的重定位信息,。其中每一項占8個字節(jié),這里共兩項所以占16字節(jié)。
PS: .rel.XXX節(jié)為.XXX節(jié)的重定位表,如:.data段的重定位表為.rel..data

每一項通過如下結(jié)構(gòu)解釋:
Elf32_Rel

typedef struct elf32_rel {
  Elf32_Addr    r_offset;
  Elf32_Word    r_info;
} Elf32_Rel;

本示例中,有效信息如下:
.rel.text

Relocation section '.rel.text' at offset 0x288 contains 2 entries:
 Offset     Info    Type            Sym.Value  Sym. Name
00000001  00000201 R_386_32          00000000   .data
00000012  00000201 R_386_32          00000000   .data

(12) 重定位

在源碼中定義的符號代碼一個地址,在.o文件中,該地址指向?qū)?yīng)符號所在文件中的相對位置。鏈接過程中需要將這個相對位置替換為虛擬內(nèi)存地址,才能使得程序正常運行。
匯編代碼

movl $mydata1, %edi 
movb $49, 17(%edi)      
movb $115, 18(%edi)
movb $116, 19(%edi)
 
movl $mydata2, %edi 
movb $50, 17(%edi)  
movb $110, 18(%edi)
movb $100, 19(%edi)
 
movl $1, %eax   #syscall exit
movl $0, %ebx   #argument 0, ie. exit(0)
int $0x80

.o文件中的.text段

00000000 <_start>:
   0:   bf 00 00 00 00          mov    $0x0,%edi
   5:   c6 47 11 31             movb   $0x31,0x11(%edi)
   9:   c6 47 12 73             movb   $0x73,0x12(%edi)
   d:   c6 47 13 74             movb   $0x74,0x13(%edi)
  11:   bf 15 00 00 00          mov    $0x15,%edi
  16:   c6 47 11 32             movb   $0x32,0x11(%edi)
  1a:   c6 47 12 6e             movb   $0x6e,0x12(%edi)
  1e:   c6 47 13 64             movb   $0x64,0x13(%edi)
  22:   b8 01 00 00 00          mov    $0x1,%eax
  27:   bb 00 00 00 00          mov    $0x0,%ebx
  2c:   cd 80                   int    $0x80

可執(zhí)行文件中的.text段

08048074 <_start>:
 8048074:   bf a4 90 04 08          mov    $0x80490a4,%edi
 8048079:   c6 47 11 31             movb   $0x31,0x11(%edi)
 804807d:   c6 47 12 73             movb   $0x73,0x12(%edi)
 8048081:   c6 47 13 74             movb   $0x74,0x13(%edi)
 8048085:   bf b9 90 04 08          mov    $0x80490b9,%edi
 804808a:   c6 47 11 32             movb   $0x32,0x11(%edi)
 804808e:   c6 47 12 6e             movb   $0x6e,0x12(%edi)
 8048092:   c6 47 13 64             movb   $0x64,0x13(%edi)
 8048096:   b8 01 00 00 00          mov    $0x1,%eax
 804809b:   bb 00 00 00 00          mov    $0x0,%ebx
 80480a0:   cd 80                   int    $0x80
        

觀察上面代碼中綠色陰影標記部分,在.o文件中,使用的是mydata1和mydata2在.data段中的相對位置。在可執(zhí)行文件中,使用的是mydata1和mydata2的虛擬內(nèi)存地址。

在.rel.text中可以看到,.o文件中將匯編源碼中使用了mydata1和mydata2的地方標記為需要重定位,重定位類型為:R_386_32
一個符號就代表一個地址,這兩個符號代表的值是它們在.data段中的偏移量,分別為0和15(即十進制 的21)。所以.o中兩條mov指令使用的分別是0x1和0x15。

已經(jīng)初始化的數(shù)據(jù)存放在.data section中。這些數(shù)據(jù)實際上引用的是.data的地址。例如,定義了一個變量static int x = 10. 我們知道符號實際上代表一個地址而已,在這里引用x時,實際上是這樣的:.symtab中記錄了x在.data section中的偏移量(假設(shè)是12),其他地方要用到x變量時,實際上是通過 .data地址+偏移量 來進行引用的。
對于上面的 movl $mydata1, %edi 指令也是如此。因此,在重定位表中記錄的需要重定位的項不是mydata1和mydata2而是.data。

 Offset     Info    Type            Sym.Value  Sym. Name
00000001  00000201 R_386_32          00000000   .data
00000012  00000201 R_386_32          00000000   .data
offset 表示要更改的地方在.text section中起始地址。要更改的內(nèi)容默認為為該處的4個字節(jié);
info 
    1. 表示更改的方式為:0X01 即:R_386_32
    2. 被引用的符號在.symtab中的索引為: 0x000002,即.data

需要重定位的地方如上表所示,offset為需要重定位的字段(四字節(jié))在.text section中的偏移量(.rel.text是.text section的重定位信息表)。被引用的symbol為.data(因為它是已經(jīng)初始化的數(shù)據(jù),因此被引用符號為.data而不是mydata1和mydata2). info字段中,低八位表示重定位類型(上面顯示的是十六進制),因此這里是01即R_386_32 . 高24位表示的是被引用字符在.symtab中的索引,這里高24位為2,查找上面的.symtab可以發(fā)現(xiàn)它對應(yīng)第三條(粗體顯示):

Symbol table '.symtab' contains 7 entries:
   Num:    Value  Size Type    Bind   Vis      Ndx Name
     0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 00000000     0 SECTION LOCAL  DEFAULT    1 
     2: 00000000     0 SECTION LOCAL  DEFAULT    3  
     3: 00000000     0 SECTION LOCAL  DEFAULT    4 
     4: 00000000     0 NOTYPE  LOCAL  DEFAULT    3 mydata1
     5: 00000015     0 NOTYPE  LOCAL  DEFAULT    3 mydata2
     6: 00000000     0 NOTYPE  GLOBAL DEFAULT    1 _start

1~3條分別表示索引號為1、3、4的section的名稱,即.text,.data和.bss。第0條默認為STN_UNDEF(undefined)類型 第三條中的Ndx字段為3,表示它是第三節(jié)(即.data)中的符號。

R_386_32重定位方式:
.text section中偏移量為0x1和偏移量為0x12處的四字節(jié)需要更改為.data section在內(nèi)存中的起始地址+它原來的值(分別為0x0和0x15)。(生成的.o文件中。匯編器(as命令)已經(jīng)將我們匯編代碼中引用到的mydata1和mydata2分別替換為了這兩個符號在.data中的偏移地址了)
細節(jié)情況如下:
R_386_32:重定位一個使用32位絕對地址的引用。其地址計算方法為.symtab中對應(yīng)的value值加上原始值。鏈接過程中先將.symtab中索引為2的項(即.data)的value設(shè)置為它的虛擬地址(本示例是:80490a4)。用該虛擬地址加上原值,便得到了新的值,這也就是之前所說的那個過程。

重定位的方式還有很多種,這里只討論其中之一,也是最常用的一種。注意:當被引用的符號是未初始化的變量時,它和上面的過程一樣,整個過程僅僅是把上面涉及的.data符號改為被引用的符號而已。

總結(jié):

編譯器將程序的入口地址設(shè)置為標號_start的地址。所以匯編程序必須提供_start符號,C程序由glibc自動提供,因此在C程序中不能定義新的_start全局符號。

image.png

附錄

輸出文件

elf.demo.o

ELF Header:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              REL (Relocatable file)
  Machine:                           Intel 80386
  Version:                           0x1
  Entry point address:               0x0
  Start of program headers:          0 (bytes into file)
  Start of section headers:          192 (bytes into file)
  Flags:                             0x0
  Size of this header:               52 (bytes)
  Size of program headers:           0 (bytes)
  Number of program headers:         0
  Size of section headers:           40 (bytes)
  Number of section headers:         8
  Section header string table index: 5

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .text             PROGBITS        00000000 000034 00002e 00  AX  0   0  4
  [ 2] .rel.text         REL             00000000 000288 000010 08      6   1  4
  [ 3] .data             PROGBITS        00000000 000064 00002a 00  WA  0   0  4
  [ 4] .bss              NOBITS          00000000 000090 000000 00  WA  0   0  4
  [ 5] .shstrtab         STRTAB          00000000 000090 000030 00      0   0  1
  [ 6] .symtab           SYMTAB          00000000 000200 000070 10      7   6  4
  [ 7] .strtab           STRTAB          00000000 000270 000018 00      0   0  1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings)
  I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)

There are no section groups in this file.

There are no program headers in this file.

Relocation section '.rel.text' at offset 0x288 contains 2 entries:
 Offset     Info    Type            Sym.Value  Sym. Name
00000001  00000201 R_386_32          00000000   .data
00000012  00000201 R_386_32          00000000   .data

There are no unwind sections in this file.

Symbol table '.symtab' contains 7 entries:
   Num:    Value  Size Type    Bind   Vis      Ndx Name
     0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 00000000     0 SECTION LOCAL  DEFAULT    1 
     2: 00000000     0 SECTION LOCAL  DEFAULT    3 
     3: 00000000     0 SECTION LOCAL  DEFAULT    4 
     4: 00000000     0 NOTYPE  LOCAL  DEFAULT    3 mydata1
     5: 00000015     0 NOTYPE  LOCAL  DEFAULT    3 mydata2
     6: 00000000     0 NOTYPE  GLOBAL DEFAULT    1 _start

No version information found in this file.

objdump.demo.o

demo.o:     file format elf32-i386
 
Disassembly of section .text:
 
00000000 <_start>:
   0:   bf 00 00 00 00          mov    $0x0,%edi
   5:   c6 47 11 31             movb   $0x31,0x11(%edi)
   9:   c6 47 12 73             movb   $0x73,0x12(%edi)
   d:   c6 47 13 74             movb   $0x74,0x13(%edi)
  11:   bf 15 00 00 00          mov    $0x15,%edi
  16:   c6 47 11 32             movb   $0x32,0x11(%edi)
  1a:   c6 47 12 6e             movb   $0x6e,0x12(%edi)
  1e:   c6 47 13 64             movb   $0x64,0x13(%edi)
  22:   b8 01 00 00 00          mov    $0x1,%eax
  27:   bb 00 00 00 00          mov    $0x0,%ebx
  2c:   cd 80                   int    $0x80

文件視圖

image.png
image.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容