日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

ELF文件格式

發(fā)布時間:2023/12/20 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 ELF文件格式 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

ELF文件格式

ELF文件(Executable Linkable Format)是一種文件存儲格式。Linux下的目標文件和可執(zhí)行文件都按照該格式進行存儲,有必要做個總結(jié)。

概要

本文主要記錄總結(jié)32位的Intel x86平臺下的ELF文件結(jié)構(gòu)。ELF文件以Section的形式進行存儲。代碼編譯后的指令放在代碼段(Code Section),全局變量和局部靜態(tài)變量放到數(shù)據(jù)段(Data Section)。文件以一個“文件頭”開始,記錄了整個文件的屬性信息。

未鏈接的目標文件結(jié)構(gòu)

SimpleSection.c

int printf(const char* format, ...);int global_init_var = 84; int global_uniit_var;void func1(int i) {printf("%d\n", i); }int main(void) {static int static_var = 85;static int static_var2;int a = 1;int b;func1(static_var + static_var2 + a + b);return a; }

對于上面的一段c代碼將其編譯但是不鏈接。gcc -c -m32 SimpleSection.c( -c表示只編譯不鏈接,-m32表示生成32位的匯編)得到SimpleSection.o。可以用objdump或readelf命令查看目標文件的結(jié)構(gòu)和內(nèi)容。

ELF文件頭

可以用readelf -h查看文件頭信息。執(zhí)行readelf -h SimpleSection.o后:

root@DESKTOP-2A432QS:~/c# readelf -h SimpleSection.o ELF Header:Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 Class: ELF32Data: 2's complement, little endianVersion: 1 (current)OS/ABI: UNIX - System VABI Version: 0Type: REL (Relocatable file)Machine: Intel 80386Version: 0x1Entry point address: 0x0Start of program headers: 0 (bytes into file)Start of section headers: 832 (bytes into file)Flags: 0x0Size of this header: 52 (bytes)Size of program headers: 0 (bytes)Number of program headers: 0Size of section headers: 40 (bytes)Number of section headers: 13Section header string table index: 10

程序頭包含了很多重要的信息,每個字段的含義可參考ELF結(jié)構(gòu)文檔。主要看下:

  • Entry point address:程序的入口地址,這是沒有鏈接的目標文件所以值是0x00
  • Start of section headers:段表開始位置的首字節(jié)
  • Size of section headers:段表的長度(字節(jié)為單位)
  • Number of section headers:段表中項數(shù),也就是有多少段
  • Start of program headers:程序頭的其實位置(對于可執(zhí)行文件重要,現(xiàn)在為0)
  • Size of program headers:程序頭大小(對于可執(zhí)行文件重要,現(xiàn)在為0)
  • Number of program headers:程序頭中的項數(shù),也就是多少Segment(和Section有區(qū)別,后面介紹)
  • Size of this header:當前ELF文件頭的大小,這里是52字節(jié)
  • 段表及段(Section)

    段表

    ELF文件由各種各樣的段組成,段表就是保存各個段信息的結(jié)構(gòu),以數(shù)組形式存放。段表的起始位置,長度,項數(shù)分別由ELF文件頭中的Start of section headers,Size of section headers,Number of section headers指出。使用readelf -S SimpleSection.o查看SimpleSection.o的段表如下:

    There are 13 section headers, starting at offset 0x340: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 000062 00 AX 0 0 1[ 2] .rel.text REL 00000000 0002a8 000028 08 I 11 1 4[ 3] .data PROGBITS 00000000 000098 000008 00 WA 0 0 4[ 4] .bss NOBITS 00000000 0000a0 000004 00 WA 0 0 4[ 5] .rodata PROGBITS 00000000 0000a0 000004 00 A 0 0 1[ 6] .comment PROGBITS 00000000 0000a4 000036 01 MS 0 0 1[ 7] .note.GNU-stack PROGBITS 00000000 0000da 000000 00 0 0 1[ 8] .eh_frame PROGBITS 00000000 0000dc 000064 00 A 0 0 4[ 9] .rel.eh_frame REL 00000000 0002d0 000010 08 I 11 8 4[10] .shstrtab STRTAB 00000000 0002e0 00005f 00 0 0 1[11] .symtab SYMTAB 00000000 000140 000100 10 12 11 4[12] .strtab STRTAB 00000000 000240 000065 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)

    總共有13個Section,重點關(guān)注.text, .data, .rodata, .symtab, .rel.text段。

    代碼段

    .text段保存代碼編譯后的指令,可以用objdump -s -d SimpleSection.o查看SimpleSection.o代碼段的內(nèi)容。

    SimpleSection.o: file format elf32-i386Contents of section .text:0000 5589e583 ec0883ec 08ff7508 68000000 U.........u.h...0010 00e8fcff ffff83c4 1090c9c3 8d4c2404 .............L$.0020 83e4f0ff 71fc5589 e55183ec 14c745f0 ....q.U..Q....E.0030 01000000 8b150400 0000a100 00000001 ................0040 c28b45f0 01c28b45 f401d083 ec0c50e8 ..E....E......P.0050 fcffffff 83c4108b 45f08b4d fcc98d61 ........E..M...a0060 fcc3 .. ...省略 Disassembly of section .text:00000000 <func1>:0: 55 push %ebp1: 89 e5 mov %esp,%ebp3: 83 ec 08 sub $0x8,%esp6: 83 ec 08 sub $0x8,%esp9: ff 75 08 pushl 0x8(%ebp)c: 68 00 00 00 00 push $0x011: e8 fc ff ff ff call 12 <func1+0x12>16: 83 c4 10 add $0x10,%esp19: 90 nop1a: c9 leave 1b: c3 ret 0000001c <main>:1c: 8d 4c 24 04 lea 0x4(%esp),%ecx20: 83 e4 f0 and $0xfffffff0,%esp23: ff 71 fc pushl -0x4(%ecx)26: 55 push %ebp27: 89 e5 mov %esp,%ebp29: 51 push %ecx2a: 83 ec 14 sub $0x14,%esp2d: c7 45 f0 01 00 00 00 movl $0x1,-0x10(%ebp)34: 8b 15 04 00 00 00 mov 0x4,%edx3a: a1 00 00 00 00 mov 0x0,%eax3f: 01 c2 add %eax,%edx41: 8b 45 f0 mov -0x10(%ebp),%eax44: 01 c2 add %eax,%edx46: 8b 45 f4 mov -0xc(%ebp),%eax49: 01 d0 add %edx,%eax4b: 83 ec 0c sub $0xc,%esp4e: 50 push %eax4f: e8 fc ff ff ff call 50 <main+0x34>54: 83 c4 10 add $0x10,%esp57: 8b 45 f0 mov -0x10(%ebp),%eax5a: 8b 4d fc mov -0x4(%ebp),%ecx5d: c9 leave 5e: 8d 61 fc lea -0x4(%ecx),%esp61: c3 ret

    可以看到.text段里保存的正是func1()和main()的指令。

    數(shù)據(jù)段和只讀數(shù)據(jù)段

    .data段保存的是已經(jīng)初始化了的全局靜態(tài)變量和局部靜態(tài)變量。前面SimpleSection.c中的global_init_varabal和static_var正是這樣的變量。使用objdump -x -s -d SimpleSection.o查看:

    Contents of section .data:0000 54000000 55000000 T...U... Contents of section .rodata:0000 25640a00 %d..

    最左邊的0000是偏移,不用看,后面跟著的0x00000054和0x00000055正是global_init_varabal和static_var的初始值。
    .rodata段存放的是只讀數(shù)據(jù),包括只讀變量(const修飾的變量和字符串常量),這個例子中保存了"%d\n"正是調(diào)用printf的時候使用的字符常量。

    符號表段

    符號表段一般叫做.symtab,以數(shù)組結(jié)構(gòu)保存符號信息(函數(shù)和變量),對于函數(shù)和變量符號值就是它們的地址。主要關(guān)注兩類符號:

  • 定義在目標文件中的全局符號,可以被其他目標文件引用,比如SimpleSction.o里面的func1, main和global_init_var。
  • 在本目標文件中引用的全局符號,卻沒有定義在本目標文件,比如pritnf。
  • 可以用readelf -s SimpleSection.o查看SimpleSection.o的符號:

    Symbol table '.symtab' contains 16 entries:Num: Value Size Type Bind Vis Ndx Name0: 00000000 0 NOTYPE LOCAL DEFAULT UND 1: 00000000 0 FILE LOCAL DEFAULT ABS SimpleSection.c2: 00000000 0 SECTION LOCAL DEFAULT 1 3: 00000000 0 SECTION LOCAL DEFAULT 3 4: 00000000 0 SECTION LOCAL DEFAULT 4 5: 00000000 0 SECTION LOCAL DEFAULT 5 6: 00000004 4 OBJECT LOCAL DEFAULT 3 static_var.14887: 00000000 4 OBJECT LOCAL DEFAULT 4 static_var2.14898: 00000000 0 SECTION LOCAL DEFAULT 7 9: 00000000 0 SECTION LOCAL DEFAULT 8 10: 00000000 0 SECTION LOCAL DEFAULT 6 11: 00000000 4 OBJECT GLOBAL DEFAULT 3 global_init_var12: 00000004 4 OBJECT GLOBAL DEFAULT COM global_uniit_var13: 00000000 28 FUNC GLOBAL DEFAULT 1 func114: 00000000 0 NOTYPE GLOBAL DEFAULT UND printf15: 0000001c 70 FUNC GLOBAL DEFAULT 1 main

    可以看到:

  • func1和main的Ndx對應(yīng)的值是1,表示在.text段(.text段在段表中的索引是1),類型是FUNC,value分別是0x00000000和0x0000001c,表明這兩個函數(shù)指令字節(jié)碼的首字節(jié)分別在.text段的0x00000000和0x0000001c偏移處。
  • printf的Ndx是UND,表明這個符號沒有在SimpleSection.o中定義,僅僅是被引用。
  • global_init_var和static_var.1488兩個符號的Ndx都是3,說明他們被定義在數(shù)據(jù)段,value分別是0x00000000和0x00000004,表示這個符號的位置在數(shù)據(jù)段的0x00000000和0x00000004偏移處,翻看上一節(jié)
  • Contents of section .data:0000 54000000 55000000 T...U...

    數(shù)據(jù)段0x00000000和0x00000004偏移處保存的正是global_init_var和static_var這兩個變量。

    重定位表段

    重定位表也是一個段,用于描述在重定位時鏈接器如何修改相應(yīng)段里的內(nèi)容。對于.text段,對應(yīng)的重定位表是.rel.text表。使用objdump -r SimpleSection.o查看重定位表。

    SimpleSection.o: file format elf32-i386RELOCATION RECORDS FOR [.text]: OFFSET TYPE VALUE 0000000d R_386_32 .rodata 00000012 R_386_PC32 printf 00000036 R_386_32 .data 0000003b R_386_32 .bss 00000050 R_386_PC32 func1

    printf對應(yīng)的那行的OFFSET為0x00000012,表明.text段的0x00000012偏移處需要修改。我們objdump -s -d SimpleSection.o查看代碼段的0x00000012偏移,發(fā)現(xiàn)是”fc ff ff ff“是call指令的操作數(shù)。

    00000000 <func1>:0: 55 push %ebp1: 89 e5 mov %esp,%ebp3: 83 ec 08 sub $0x8,%esp6: 83 ec 08 sub $0x8,%esp9: ff 75 08 pushl 0x8(%ebp)c: 68 00 00 00 00 push $0x011: e8 fc ff ff ff call 12 <func1+0x12>16: 83 c4 10 add $0x10,%esp19: 90 nop1a: c9 leave 1b: c3 ret

    也就是說,在沒有重定位前call指令的操作”fc ff ff ff“是無效的,需要在重定位過程中進行修正。func1那行也同理。

    總結(jié)

    ELF文件結(jié)構(gòu)可以用下面的圖表示:

    可執(zhí)行程序結(jié)構(gòu)

    和未鏈接的ELF文件結(jié)構(gòu)一樣,只不過引入了Segment的概念(注意和Section進行區(qū)分)。Segment本質(zhì)上是從裝載的角度重新劃分了ELF的各個段。目標文件鏈接成可執(zhí)行文件時,鏈接器會盡可能把相同權(quán)限屬性的段(Section)分配到同一Segment。Segment結(jié)構(gòu)的起始位置,項數(shù),大小分別由ELF頭中的Size of program headers,Number of program headers, Size of this header字段指定。

    參考資料:

  • 《程序員的自我修養(yǎng)》第3,6章
  • ELF結(jié)構(gòu)文檔
  • posted @ 2018-10-07 15:12? gatsby123? 閱讀(...)? 評論(...)? 編輯? 收藏

    刷新評論刷新頁面返回頂部

    ?

    ?

    ?

    ?

    ?

    ?

    Copyright ? 2020 gatsby123
    Powered by .NET Core on Kubernetes

    總結(jié)

    以上是生活随笔為你收集整理的ELF文件格式的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。