ELF文件格式

来源:https://www.cnblogs.com/gatsby123/archive/2018/10/07/9750187.html
-Advertisement-
Play Games

ELF文件(Executable Linkable Format)是一種文件存儲格式。Linux下的目標文件和可執行文件都按照該格式進行存儲,有必要做個總結。 概要 本文主要記錄總結32位的Intel x86平臺下的ELF文件結構。ELF文件以Section的形式進行存儲。代碼編譯後的指令放在代碼段 ...


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

概要

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

未鏈接的目標文件結構

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命令查看目標文件的結構和內容。

ELF文件頭

可以用readelf -h查看文件頭信息。執行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:                             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:          832 (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:         13
  Section header string table index: 10

程式頭包含了很多重要的信息,每個欄位的含義可參考ELF結構文檔。主要看下:

  1. Entry point address:程式的入口地址,這是沒有鏈接的目標文件所以值是0x00
  2. Start of section headers:段表開始位置的首位元組
  3. Size of section headers:段表的長度(位元組為單位)
  4. Number of section headers:段表中項數,也就是有多少段
  5. Start of program headers:程式頭的其實位置(對於可執行文件重要,現在為0)
  6. Size of program headers:程式頭大小(對於可執行文件重要,現在為0)
  7. Number of program headers:程式頭中的項數,也就是多少Segment(和Section有區別,後面介紹)
  8. Size of this header:當前ELF文件頭的大小,這裡是52位元組

段表及段(Section)

段表

ELF文件由各種各樣的段組成,段表就是保存各個段信息的結構,以數組形式存放。段表的起始位置,長度,項數分別由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,重點關註.text, .data, .rodata, .symtab, .rel.text段。

代碼段

.text段保存代碼編譯後的指令,可以用objdump -s -d SimpleSection.o查看SimpleSection.o代碼段的內容。

SimpleSection.o:     file format elf32-i386

Contents 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...a
 0060 fcc3                                 ..              
...省略          

Disassembly of section .text:

00000000 <func1>:
   0:   55                      push   %ebp
   1:   89 e5                   mov    %esp,%ebp
   3:   83 ec 08                sub    $0x8,%esp
   6:   83 ec 08                sub    $0x8,%esp
   9:   ff 75 08                pushl  0x8(%ebp)
   c:   68 00 00 00 00          push   $0x0
  11:   e8 fc ff ff ff          call   12 <func1+0x12>
  16:   83 c4 10                add    $0x10,%esp
  19:   90                      nop
  1a:   c9                      leave  
  1b:   c3                      ret    

0000001c <main>:
  1c:   8d 4c 24 04             lea    0x4(%esp),%ecx
  20:   83 e4 f0                and    $0xfffffff0,%esp
  23:   ff 71 fc                pushl  -0x4(%ecx)
  26:   55                      push   %ebp
  27:   89 e5                   mov    %esp,%ebp
  29:   51                      push   %ecx
  2a:   83 ec 14                sub    $0x14,%esp
  2d:   c7 45 f0 01 00 00 00    movl   $0x1,-0x10(%ebp)
  34:   8b 15 04 00 00 00       mov    0x4,%edx
  3a:   a1 00 00 00 00          mov    0x0,%eax
  3f:   01 c2                   add    %eax,%edx
  41:   8b 45 f0                mov    -0x10(%ebp),%eax
  44:   01 c2                   add    %eax,%edx
  46:   8b 45 f4                mov    -0xc(%ebp),%eax
  49:   01 d0                   add    %edx,%eax
  4b:   83 ec 0c                sub    $0xc,%esp
  4e:   50                      push   %eax
  4f:   e8 fc ff ff ff          call   50 <main+0x34>
  54:   83 c4 10                add    $0x10,%esp
  57:   8b 45 f0                mov    -0x10(%ebp),%eax
  5a:   8b 4d fc                mov    -0x4(%ebp),%ecx
  5d:   c9                      leave  
  5e:   8d 61 fc                lea    -0x4(%ecx),%esp
  61:   c3                      ret

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

數據段和只讀數據段

.data段保存的是已經初始化了的全局靜態變數和局部靜態變數。前面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段存放的是只讀數據,包括只讀變數(const修飾的變數和字元串常量),這個例子中保存了"%d\n"正是調用printf的時候使用的字元常量。

符號表段

符號表段一般叫做.symtab,以數組結構保存符號信息(函數和變數),對於函數和變數符號值就是它們的地址。主要關註兩類符號:

  1. 定義在目標文件中的全局符號,可以被其他目標文件引用,比如SimpleSction.o裡面的func1, main和global_init_var。
  2. 在本目標文件中引用的全局符號,卻沒有定義在本目標文件,比如pritnf。

可以用readelf -s SimpleSection.o查看SimpleSection.o的符號:

Symbol table '.symtab' contains 16 entries:
   Num:    Value  Size Type    Bind   Vis      Ndx Name
     0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 00000000     0 FILE    LOCAL  DEFAULT  ABS SimpleSection.c
     2: 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.1488
     7: 00000000     4 OBJECT  LOCAL  DEFAULT    4 static_var2.1489
     8: 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_var
    12: 00000004     4 OBJECT  GLOBAL DEFAULT  COM global_uniit_var
    13: 00000000    28 FUNC    GLOBAL DEFAULT    1 func1
    14: 00000000     0 NOTYPE  GLOBAL DEFAULT  UND printf
    15: 0000001c    70 FUNC    GLOBAL DEFAULT    1 main

可以看到:

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

數據段0x00000000和0x00000004偏移處保存的正是global_init_var和static_var這兩個變數。

重定位表段

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

SimpleSection.o:     file format elf32-i386

RELOCATION 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對應的那行的OFFSET為0x00000012,表明.text段的0x00000012偏移處需要修改。我們objdump -s -d SimpleSection.o查看代碼段的0x00000012偏移,發現是”fc ff ff ff“是call指令的操作數。

00000000 <func1>:
   0:   55                      push   %ebp
   1:   89 e5                   mov    %esp,%ebp
   3:   83 ec 08                sub    $0x8,%esp
   6:   83 ec 08                sub    $0x8,%esp
   9:   ff 75 08                pushl  0x8(%ebp)
   c:   68 00 00 00 00          push   $0x0
  11:   e8 fc ff ff ff          call   12 <func1+0x12>
  16:   83 c4 10                add    $0x10,%esp
  19:   90                      nop
  1a:   c9                      leave  
  1b:   c3                      ret 

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

總結

ELF文件結構可以用下麵的圖表示:

可執行程式結構

和未鏈接的ELF文件結構一樣,只不過引入了Segment的概念(註意和Section進行區分)。Segment本質上是從裝載的角度重新劃分了ELF的各個段。目標文件鏈接成可執行文件時,鏈接器會儘可能把相同許可權屬性的段(Section)分配到同一Segment。Segment結構的起始位置,項數,大小分別由ELF頭中的Size of program headers,Number of program headers, Size of this header欄位指定。

參考資料:

  1. 《程式員的自我修養》第3,6章
  2. ELF結構文檔

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 在開發中經常有遇到因為程式執行的時間過長,而造成程式假死的情況,這是因為我們的程式是同步執行的,當執行到需要長時間的操作時,程式就會等待當前的操作完成,從而造成程式假死。C#的非同步與多線程就是為瞭解決這個問題的。 多線程編程示例 .Net提供了多種方式實現多線程的編程,包括線程池,Thread,Ta ...
  • 最近聽了一節Java公開課,講的Tomcat8.0的,老師分享的學習方法很好, 時間和精力要用對地方 1.學習一個知識的廣度和深度,先學主要的主流的,不要學了很多不該學,沒必要學的東西 2.要花時間總結,實踐下,練習下,整理下,避免沒有收穫到本該收穫到的內容 ...
  • 示例使用的是最新 SqlRepoEx 2.0.2 可在:https://github.com/AzThinker/SqlRepoEx2.0Demo 或:https://gitee.com/azthinker/SqlRepoEx2.0Demo 演示模塊:GettingStartedNorthwind ...
  • 封裝:把事物封裝成類,私有化屬性. 繼承:共同的特性封裝在父類,子類就有父類的屬性 父類已經實現了的方法,子類也有需要前面+New 隱藏父類的方法,就不報錯了,有這個語法,面試的時候問過,不推薦用. 父類; public void Test() 子類:public New void Test() 多 ...
  • xargs具有並行處理的能力,在處理大文件時,如果應用得當,將大幅提升效率。 xargs詳細內容(全網最詳細): "https://www.cnblogs.com/f ck need u/p/5925923.html" 效率提升測試結果 先展示一下使用xargs並行處理提升的效率,稍後會解釋下麵的結 ...
  • 1、vim 編輯器可以自定義配置:包含初始化命令的文件叫vimrc(h vimrc),version 查看版本號、系統-用戶配置文件所在位置。 2、 可視模式: 操作一塊區域代碼: 按v 插入模式:按i 3、游標移動篇 移動到行首 0 / shift 6 移動到行尾 shift4 4、移動單詞 作為 ...
  • 前言 多年未犯低級錯誤,今天犯了個不大不小的錯誤,記錄下生活點滴吧 今天早上腦海裡想了下,如果電腦掛了我要備份哪些東西?然後中午休息的時候就列了一下,沒想到晚上就悲劇了... 這個是中午寫的: If I to bak ~/桌面( 先push到github ) ~/下載(Linux軟體備份) ~/圖片 ...
  • 一, ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...