鏈接學習之obj文件探索
Windows的gcc環境,往官網http://sourceforge.net/project/showfiles.php?group_id=2435 下載MinGW,安裝,安裝完畢後按照包
配置環境變量
a.在PATH的值中加入"C:\Program Files\MinGWStudio\MinGW\bin"。這是尋找gcc編譯器的路徑。如果PATH中還有其他內容,需要用英文狀態下分號進行分割
b.新建LIBRARY_PATH變量,在其值中加入"C:\Program Files\MinGWStudio\MinGW\lib"。這是標準庫存放的路徑。
c.新建C_INCLUDE_PATH變量,在其值中加入"C:\Program Files\MinGWStudio\MinGW\include"。這是Include查找頭文件的路徑。
先是一個及其簡單的C程序
Hello.c
預處理
C:\Users\居士\Desktop\Update\Link C>gcc -E hello.c -o hello.i
i文件局部
如上圖#後面的數字 210代表行號 stdio.h中能找到對應代碼
編譯
C:\Users\居士\Desktop\Update\Link C>gcc -S hello.i -o hello.s
編譯後的 內容仍然是文本,打開s文件仍然可以看懂其內容
學過匯編的人能看懂裏面表達啥,對於我而言只認得pushl,movl,andl等幾個指令,以及%ebp,%esp這幾個寄存器,還有常數$-16,$0。其余cif_offset這些就需要百度才知道了。總體來說還是看不懂的。
匯編
C:\Users\居士\Desktop\Update\Link C>gcc -c hello.s -o hello.o
匯編得到的是一個二進制的文件,是一個可重定向目標文件,裏面包含著機體代碼。這裏雖然表面上是一個o文件,由於本人使用的是Windows平臺,編譯出來的還是類似於通過VC編譯出來的obj格式的文件,而並非Linux平臺下的ELF文件。
在網上找的命令可以通過下面命令看到格式化後的o文件
C:\Users\居士\Desktop\Update\Link C>readelf -a hello.o
但是在windows下生成的實際上是obj文件,用VS的另一個工具可以打開,該工具在以下目錄
D:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\bin\dumpbin.exe
命令為dumpbin /all {目標文件文件名} > {輸出文件的文件名}
因此之前看書上面說的ELF文件的格式就對不上了,文件的結構需要找別的資料去參考。這裏借助了dumpbin以外還使用了CFF Explorer。
obj文件內容分析
在網上看過別人分析的obj文件是用在VC下寫的一段很簡單的C++代碼生成的,和我這個用MinGW的gcc下的C程序有出入。
總的來說無論是obj還是o文件,都是基於COFF(Common Object File Format)文件,它與我們平常寫的代碼不一樣,它是有一個一定格式的文件,鏈接器(或加載器)則按照這個結構來鏈接(或執行)這些文件,先羅列一下整個obj文件的結構
File Header 文件頭
Optional Header
Section Header Table 節頭部表
Section Raw Data 節的原始數據
Relocation Table 重定向表
Symbol Table 符號表
String Table 字符串表
再看通過dumpbin生成的文件
FILE HEADER:文件頭
這個文件頭在obj文件中占了0~13共14個字節。
這個文件頭的大小是固定的,它實際上是winnt.h裏面的一個結構體。細心的可以發現結構中一些值是直接以數值的形式存放在文件中,例如machine的值14C,存放為4C 01,符號表的地址指針1B8,存放成B8 01。我想表達的是因為windows是采用了小段法存放數據的機器,高低位間作了互換。Winnt.h文件在C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Include目錄中(我的是64位系統)。結構體定義如下
各個字段的解析可以參考MSDN上的內容
結構體解析 https://msdn.microsoft.com/zh-cn/library/windows/desktop/ms680313(v=vs.85).aspx
大體就是說了編譯時機器的情況,時間,還有與本文件關系大的符號表的位置,符號的數量,optional header的數量。
接下來就是各個節的說明了,先已第一個節作為例子,介紹完這個節的結構後再通過CFF Explorer對比o文件裏的內容。再去介紹各個節的作用。
這裏包含了節的頭部,節的原始數據,還有一個重定向表,節的頭部是這樣的一個結構
頭部的作用是描述整個節的信息,例如SizeOfRawData代表原始數據的數量,PointerToRawData即是原始數據起始的地址。NumberOfRelocations是重定向條目的數量。
節頭部數據結構詳情可參考MSDN中的以下位置 https://msdn.microsoft.com/en-us/library/windows/desktop/ms680341(v=vs.85).aspx
Raw Data則是節的原始數據,這裏其實沒啥結構的,不同的節就存放他們對應的數據。
Relocations是重定位信息,實際上它是重定位表的一部分,只屬於這個節的一部分。這個重定向的一個條目結構如下,
這個就是在鏈接時給引用外部的符號重定位時用的
以上僅是該o文件中最全的一個節的結構,當然還有可能會有其他的"節成員",只是當前這個C程序太簡單,以致其他成員未出現。但是不是每個節都有原始數據,有些節是空的,有些節就不帶重定向信息。
下面逐個看這個o文件裏面的每個節。
第一個節是.text節,圖不再截了,就之前的那個圖,.text節存放的是已編譯的機器代碼。利用objdump工具可以看到這節的原始數據(也就是機器代碼整理後的結果),命令如下
要看懂這些指令的話,還需要懂得處理器的指令集(參考《深入了解計算機系統》的第四章)。
第二節是是data節,按書上的介紹data節是存放已初始化的全局或靜態C變量,這個C程序中沒有用到全局變量或靜態變量,這節就沒有內容
第三節是bss節,是存放未初始化的全局或靜態C變量,以及被初始化為0的全局或C變量,但是這個姐實際不占用控件
第四個節是rdata節,放的就是只讀數據,這裏存在了一個宏定義,"hello c!"這樣的一個字符串,這部分數據就存放在這一節中
第五個節叫"/4",rdata$zzz估計是別名,不知具體的作用,貌似是存放了編譯器的信息,版本之類的
第六個節叫"/15",eh_frame,更加不知道他的作用,貌似是有對.text節的引用,難道是調用main函數外層的東西?
節部分完結了之後就是符號表
符號表的介紹直接引用MSDN的內容
對於以符號號碼開頭的行,下列說明描述了含有與用戶相關的信息的列:
- 開頭的 3 位數字是符號索引/號碼。
- 如果第三列包含 SECTx,則符號在對象文件的那一節中定義。 但如果出現 UNDEF,則它不在那個對象中定義並且必須在其他地方被解析。
- 第五列 (Static, External) 說明符號是否只在那個對象的內部可見,或者是否是公共的(外部可見)。 靜態符號 _sym 不會鏈接到公共符號 _sym;這些符號是名為 _sym 的函數的兩種不同實例。
編號行中的最後一列是符號名(修飾名和未修飾名)
來自 <https://msdn.microsoft.com/zh-cn/library/b842y285.aspx>
每條符號表的記錄是一下的結構
有幾條記錄是多出了一行的,那部分數據是對這個符號的補充說明,結構如下
在符號表之後就是字符串表了,但是在dumpbin生成d文件中只是列舉了字符串表的大小
但在其他資料中介紹,字符串表示存放著節名的字符串。聽過CFF Explorer中的內容查看,確實定義了4個字符川,就是重復的.rdata$zzz和.eh_frame
dumpbin如何分析得出這個o文件
那又有一個問題來了,編譯系統是如何根據這份二進制的o文件來讀取到這些信息呢,嘗試一下通過CFF Explorer來分析讀取到這個文件的信息,人為模擬一下dumpbin的工作。這個過程推導出前面那個obj文件結構這個結論。
首先是讀取固定大小的文件頭,通過文件頭的結構獲取到幾個與本個o文件有關的信息:
- 有6個節
-
符號表的起始位置是1B8,符號數量是12個
那麽就相當於把o文件已經分成了幾塊
文件頭 |
未知部分1 |
符號表 |
未知部分2 |
0~13 |
14~1B7 |
1B8~?? |
??~329 |
那麽節這部分有可能存放在未知部分1中。查看各個節的頭部信息,分析出節1到節6是從104~18F。可以查看各個節的原始數據得出
其他節就不一一列舉了
另外還有各個節頭部信息中提及到的重定位信息,整個文件有中重定位信息只有兩個節:節1的重定位是190開始;節6的重定位信息是1AE開始,節1的重定位項有3個,節6的重定位項有1個,粗略推斷節1的重定位信息是重190~1AD,節6的重定位信息是1AE~1B7,每條重定位信息的長度大概是10個字節
如
是對應
0A 00估計對應Offset,10 00對應Symbol Index,14 00對應Type,可視Applied To這個就解析不清了,因為節6的重定位信息如下
還有字符串表的信息,字符串的大小是一個4字節用小端法存儲的無符號整數,2E應該是2E 00 00 00,縱觀整個文件存放了這個信息的就在位置為2FC處,因此估計字符串表是從2FC處到329結尾處了
文件頭 |
未知部分1 |
節原始數據 |
重定向表 |
符號表 |
字符串表 |
0~13 |
14~103 |
104~18F |
190~1B7 |
1B8~2FB |
2FC~329 |
剩余的未知部分1應該就是存放節頭部信息的節頭部表,從14~103,也是通過數量以及數據的比對,節共有6個,此部分數據共240個,平均每個節頭部信息占40個字節,下面就拿了40個字節的信息
可以看到節的名稱.text 是2E 74 65 78 74;原始數據的起始地址104,就是04 01 00 00;重定向表190,就是90 01 00 00,其他成員就不列舉了。其他頭部也不列舉了。
最終得到的結構是
文件頭 |
節頭部表 |
姐原始數據 |
重定向表 |
符號表 |
字符串表 |
0~13 |
14~103 |
104~18F |
190~1B7 |
1B8~2FB |
2FC~329 |
再細分一下的就是
文件頭 |
0~13 |
節1頭部 |
14~3B |
節2頭部 |
3C~63 |
節3頭部 |
64~8B |
節4頭部 |
8C~B3 |
節5頭部 |
B4~DB |
節6頭部 |
DC~103 |
節1原始數據 |
104~127 |
節4原始數據 |
128~133 |
節5原始數據 |
134~157 |
節6原始數據 |
158~18F |
節1重定向表 |
190~1AD |
節6重定向表 |
1AE~1B7 |
符號表 |
1B8~2FB |
字符串表 |
2FC~329 |
鏈接學習之obj文件探索