學習一門新語言需要瞭解的基礎-03 可執行文件結構

来源:http://www.cnblogs.com/lyj/archive/2017/10/17/foundation_03_coff_free.html
-Advertisement-
Play Games

本節內容 - 通用可執行文件結構(COFF)(readelf -h) - COFF用段(section)存儲不同類型數據(readelf -S) - 常用段 - 演示:使用readelf、xxd、objdump、gdb查看可執行文件結構信息 - 演示:objcopy -add-section;st... ...


03 可執行文件結構

本節內容

  • 通用可執行文件結構(COFF)(readelf -h)
  • COFF用段(section)存儲不同類型數據(readelf -S)
  • 常用段
  • 演示:使用readelf、xxd、objdump、gdb查看可執行文件結構信息[付費閱讀]
  • 演示:objcopy -add-section;strip -remove-section;readelf -p[付費閱讀]

編譯完成了,鏈接完成了,我們現在得到可執行文件了,接下來問題是可執行文件是什麼樣子?

通用可執行文件結構(COFF)(readelf -h)

$ cat hello.c

這是很簡單的c語言代碼,有兩個引入標準庫的頭,三個全局變數,x有初始化值,y沒有初始化值,s是個字元串。

#include <stdio.h>
#include <stdlib.h>

int x = 0x1234;
int y;
char *s = "x = %x, y = %x\n";

int main()
{
    printf(s, x, y);

    return 0;
}

編譯成可執行文件,包含調試信息,禁止優化

$ gcc -g -O0 -o test hello.c
$ ./test

這個可執行文件到底什麼樣子?先看可執行文件是怎麼描述自己的,使用readelf -h查看可執行文件頭部信息。

$ readelf -h test #輸出可執行文件頭部信息

輸出

ELF Header:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF64
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           Advanced Micro Devices X86-64
  Version:                           0x1
  Entry point address:               0x400430
  Start of program headers:          64 (bytes into file)
  Start of section headers:          7512 (bytes into file)
  Flags:                             0x0
  Size of this header:               64 (bytes)
  Size of program headers:           56 (bytes)
  Number of program headers:         9
  Size of section headers:           64 (bytes)
  Number of section headers:         36
  Section header string table index: 33

Magic標識用於判斷是否是標準ELF文件。
Class代表是64位可執行文件。
Entry point address代表第一條執行指令地址,正好指向.text段。
下麵描述可執行文件結構的,一共有多少節,各自尺寸,從哪開始。

我們把這種看上去像資料庫這種格式的可執行文件通常稱之為COFF,COFF是通用可執行文件結構,不同的公司對它做具體的一些定製,比如說Linux把它的改進版本稱之為ELF,Windows的改進版本稱之為PE,其實它們都屬於COFF範疇,只不過每家公司對它細節處理不一樣,但是它格式基本上類似的。

COFF用段(section)存儲不同類型數據(readelf -S)

$ readelf -S test #輸出可執行文件段信息

輸出

There are 36 section headers, starting at offset 0x1d58:

Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .interp           PROGBITS         0000000000400238  00000238
       000000000000001c  0000000000000000   A       0     0     1
  [ 2] .note.ABI-tag     NOTE             0000000000400254  00000254
       0000000000000020  0000000000000000   A       0     0     4
  [ 3] .note.gnu.build-i NOTE             0000000000400274  00000274
       0000000000000024  0000000000000000   A       0     0     4
  [ 4] .gnu.hash         GNU_HASH         0000000000400298  00000298
       000000000000001c  0000000000000000   A       5     0     8
  [ 5] .dynsym           DYNSYM           00000000004002b8  000002b8
       0000000000000060  0000000000000018   A       6     1     8
  [ 6] .dynstr           STRTAB           0000000000400318  00000318
       000000000000003f  0000000000000000   A       0     0     1
  [ 7] .gnu.version      VERSYM           0000000000400358  00000358
       0000000000000008  0000000000000002   A       5     0     2
  [ 8] .gnu.version_r    VERNEED          0000000000400360  00000360
       0000000000000020  0000000000000000   A       6     1     8
  [ 9] .rela.dyn         RELA             0000000000400380  00000380
       0000000000000018  0000000000000018   A       5     0     8
  [10] .rela.plt         RELA             0000000000400398  00000398
       0000000000000030  0000000000000018  AI       5    24     8
  [11] .init             PROGBITS         00000000004003c8  000003c8
       000000000000001a  0000000000000000  AX       0     0     4
  [12] .plt              PROGBITS         00000000004003f0  000003f0
       0000000000000030  0000000000000010  AX       0     0     16
  [13] .plt.got          PROGBITS         0000000000400420  00000420
       0000000000000008  0000000000000000  AX       0     0     8
  [14] .text             PROGBITS         0000000000400430  00000430
       00000000000001a2  0000000000000000  AX       0     0     16
  [15] .fini             PROGBITS         00000000004005d4  000005d4
       0000000000000009  0000000000000000  AX       0     0     4
  [16] .rodata           PROGBITS         00000000004005e0  000005e0
       0000000000000014  0000000000000000   A       0     0     4
  [17] .eh_frame_hdr     PROGBITS         00000000004005f4  000005f4
       0000000000000034  0000000000000000   A       0     0     4
  [18] .eh_frame         PROGBITS         0000000000400628  00000628
       00000000000000f4  0000000000000000   A       0     0     8
  [19] .init_array       INIT_ARRAY       0000000000600e10  00000e10
       0000000000000008  0000000000000000  WA       0     0     8
  [20] .fini_array       FINI_ARRAY       0000000000600e18  00000e18
       0000000000000008  0000000000000000  WA       0     0     8
  [21] .jcr              PROGBITS         0000000000600e20  00000e20
       0000000000000008  0000000000000000  WA       0     0     8
  [22] .dynamic          DYNAMIC          0000000000600e28  00000e28
       00000000000001d0  0000000000000010  WA       6     0     8
  [23] .got              PROGBITS         0000000000600ff8  00000ff8
       0000000000000008  0000000000000008  WA       0     0     8
  [24] .got.plt          PROGBITS         0000000000601000  00001000
       0000000000000028  0000000000000008  WA       0     0     8
  [25] .data             PROGBITS         0000000000601028  00001028
       0000000000000020  0000000000000000  WA       0     0     8
  [26] .bss              NOBITS           0000000000601048  00001048
       0000000000000008  0000000000000000  WA       0     0     4
  [27] .comment          PROGBITS         0000000000000000  00001048
       0000000000000034  0000000000000001  MS       0     0     1
  [28] .debug_aranges    PROGBITS         0000000000000000  0000107c
       0000000000000030  0000000000000000           0     0     1
  [29] .debug_info       PROGBITS         0000000000000000  000010ac
       00000000000000de  0000000000000000           0     0     1
  [30] .debug_abbrev     PROGBITS         0000000000000000  0000118a
       000000000000005c  0000000000000000           0     0     1
  [31] .debug_line       PROGBITS         0000000000000000  000011e6
       000000000000003e  0000000000000000           0     0     1
  [32] .debug_str        PROGBITS         0000000000000000  00001224
       00000000000000c4  0000000000000001  MS       0     0     1
  [33] .shstrtab         STRTAB           0000000000000000  00001c06
       000000000000014c  0000000000000000           0     0     1
  [34] .symtab           SYMTAB           0000000000000000  000012e8
       0000000000000708  0000000000000018          35    52     8
  [35] .strtab           STRTAB           0000000000000000  000019f0
       0000000000000216  0000000000000000           0     0     1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), l (large)
  I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)

假如可執行文件是個資料庫的話,這裡面一大堆的表,這些表看上去有點暈,不知道裡面到底什麼東西。

我們知道所謂的段就是這些,每個段存儲不同類型的數據,這裡面有些信息可以看到,每一節的具體描述:Name名稱、Type類型、Address起始地址、Offset相對於當前可執行文件的偏移量、Size長度、Flags許可權開關。這些數據表裡面有幾個東西需要註意的,這些數據信息的數量和名字不是固定的,每種編譯器都有自己的規則,這個信息對於編譯器、反彙編有用,CPU不關心這些,CPU只關心地址從哪開始讀多長數據到哪結束。至於你這東西屬於哪個數據表叫什麼名字跟CPU一點關係也沒有,這些東西實際上是給編譯器用的,但是這些名字雖然每種編譯器有各自的習慣,也不太一樣,但是有幾個是約定俗成的。

常用段

有幾個名字是約定俗成的

  • .text存儲的全部是機器碼。
  • .data存儲的是有初始化值的全局變數,靜態局部變數。
  • .bss存儲沒有初始化值的全局變數。
  • .rodata存儲的是只讀數據,比如字面量。
$ readelf -x .text test #查看.text段機器指令內容

輸出

Hex dump of section '.text':
  0x00400430 31ed4989 d15e4889 e24883e4 f0505449 1.I..^H..H...PTI
  0x00400440 c7c0d005 400048c7 c1600540 0048c7c7 [email protected]..`[email protected]..
  0x00400450 26054000 e8b7ffff fff4660f 1f440000 &[email protected]..
  0x00400460 b84f1060 0055482d 48106000 4883f80e .O.`.UH-H.`.H...
  0x00400470 4889e576 1bb80000 00004885 c074115d H..v......H..t.]
  0x00400480 bf481060 00ffe066 0f1f8400 00000000 .H.`...f........
  0x00400490 5dc30f1f 4000662e 0f1f8400 00000000 ][email protected].........
  0x004004a0 be481060 00554881 ee481060 0048c1fe .H.`.UH..H.`.H..
  0x004004b0 034889e5 4889f048 c1e83f48 01c648d1 .H..H..H..?H..H.
  0x004004c0 fe7415b8 00000000 4885c074 0b5dbf48 .t......H..t.].H
  0x004004d0 106000ff e00f1f00 5dc3660f 1f440000 .`......].f..D..
  0x004004e0 803d610b 20000075 11554889 e5e86eff .=a. ..u.UH...n.
  0x004004f0 ffff5dc6 054e0b20 0001f3c3 0f1f4000 ..]..N. ......@.
  0x00400500 bf200e60 0048833f 007505eb 930f1f00 . .`.H.?.u......
  0x00400510 b8000000 004885c0 74f15548 89e5ffd0 .....H..t.UH....
  0x00400520 5de97aff ffff5548 89e58b15 1c0b2000 ].z...UH...... .
  0x00400530 8b0d020b 2000488b 05030b20 0089ce48 .... .H.... ...H
  0x00400540 89c7b800 000000e8 b4feffff b8000000 ................
  0x00400550 005dc366 2e0f1f84 00000000 000f1f00 .].f............
  0x00400560 41574156 4189ff41 5541544c 8d259e08 AWAVA..AUATL.%..
  0x00400570 20005548 8d2d9e08 20005349 89f64989  .UH.-.. .SI..I.
  0x00400580 d54c29e5 4883ec08 48c1fd03 e837feff .L).H...H....7..
  0x00400590 ff4885ed 742031db 0f1f8400 00000000 .H..t 1.........
  0x004005a0 4c89ea4c 89f64489 ff41ff14 dc4883c3 L..L..D..A...H..
  0x004005b0 014839eb 75ea4883 c4085b5d 415c415d .H9.u.H...[]A\A]
  0x004005c0 415e415f c390662e 0f1f8400 00000000 A^A_..f.........
  0x004005d0 f3c3                                ..

.bss在文件內沒有存儲,可查看offset確認

為何全局變數需要分開存儲?全局變數實際上是要寫到可執行文件里,.data和.bss,如果這個全局變數本身有初始化值是不是要找地方存起來,要不然程式執行時候根本不知道初始化值是多少,.data用來存儲這些初始化具體的值,.bss雖然也是全局變數,但沒有初始化值,沒有初始化值沒必要寫到可執行文件裡面,因為你根本沒有數據,只有在運行期才初始化,你在編譯時候根本沒有值幹嘛去寫,所以它只是保留了.bss表用於映射地址信息,但是不存儲裡面任何數據,你會註意到它相對文件的偏移量與下個段節點的偏移量一樣,就說明這裡面根本沒有數據,否則兩個偏移量不可能相等。因為像int y;的值只有在運行期才被初始化或者給它一個莫名其妙隨機值。既然沒有初始化值我編譯時候根本不需要類似1234這樣的值保存起來,你不要忘了在沒有執行之前這些有初始化值的數據必須保存起來,你保存在哪,肯定在可執行文件裡面有個表存這些數據,否則數據執行時候數據從哪裡來。

go區分有指針和沒指針data(data、.noptrdata)、bss,便於GC操作[付費閱讀]

演示:使用readelf、xxd、objdump、gdb查看可執行文件結構信息[付費閱讀]

演示:objcopy -add-section;strip -remove-section;readelf -p[付費閱讀]

這個系列的每篇文章有大半篇幅內容屬於付費閱讀。提供微信支付支付寶支付打賞50元備註留言手動提供付費文章訪問密碼。


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

-Advertisement-
Play Games
更多相關文章
  • maven repository :maven的依賴查詢Alt+回車 導入包,自動修正Ctrl+N 查找類Ctrl+Shift+N 查找文件Ctrl+Alt+L 格式化代碼Ctrl+Alt+O 優化導入的類和包Alt+Insert 生成代碼(如get,set方法,構造函數等)Ctrl+E或者Alt+ ...
  • 昨天發佈了 Hibernate 學習筆記第一篇後,今天第二篇來襲~ 此篇筆記是 Hibernate 學習的重點和難點,包括 Hibernate 中的映射關聯關係、Hibernate 的檢索策略與檢索方式(HQL、QBC)、Hibernate 的二級緩存,還包括管理 Session ,如何使 Sess... ...
  • 分類 功能點 Eclipse快捷鍵 IDEA快捷鍵 搜索 搜索文本 Ctrl + F Ctrl + F Ctrl + R 查找替換 Alt + P/A 逐個/全部替換 Alt + F3 查找當前選中詞 繼續搜索 Ctrl + K 向前 Ctrl + Shift + K 向後 F3 Shift + F ...
  • 參考鏈接:http://www.python(tab).com/html/2017/pythonhexinbiancheng_0904/1170.html(去除括弧) http://blog.csdn.net/eastmount/article/details/51082253 首先本文參考了上述兩 ...
  • Lambda表達式(匿名函數)實現了一次執行且無污染的函數定義,是拋棄型函數並且不維護任何類型的狀態。閉包在匿名函數的基礎上增加了與外部環境的變數交互,通過 use 子句中指定要導入的外部環境變數。C字元串以空字元('\0')為結束標誌,這使得C字元串不能保存像圖片、音頻、視頻、壓縮文件這樣的二進位... ...
  • PS:本系列內容進度節奏會放的很慢,每次知識點都儘量少一點,這樣大家接觸的知識點少了,會更容易理解,因為少即是多。另外,對於後面代碼部分,雖然儘量不用那些複雜的封裝和類,但它並不表示看了就能全部記住,並懂得每個函數的用法,在什麼時候去調用,清楚它輸入的參數類型、能處理的參數類型和輸出的結果是什麼。它 ...
  • 使用 JPA 和 Hibernate 的好處之一是它提供了資料庫特定方言和功能抽象。 因此,理論上,您可以實現一個應用程式,將其連接到一個受支持的資料庫,並且它可以在不用更改任何代碼的情況下運行。Hibernate 真的很好。 但老實說,您沒有想過您的應用程式能與每個支持的資料庫完美運行,是嗎? ...
  • #include #include #include using namespace std; int main(){ double a,b,c; cout>a>>b>>c; if(a+b>c&&b+c>a&&c+a>b){ double s,area; s=(a+b+c)/2; area=sqrt... ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...