windows的exe、lib文件跟c運行時庫怎麼關聯的

2015-11-2 09:22:02 Am Monday
嗨,週一。

一個新的工作周的開始。

在Windows操作系統下,可執行文件的存儲格式是PE(Portable Executable)格式的;在Linux下,可執行文件的存儲格式是WLF格式的。它們都是COFF格式文件的變種。

在Windows下,目標文件(.obj)、靜態庫(.lib)使用COFF格式存儲;可執行文件(.exe),動態鏈接庫文件(.dll)使用PE格式存儲。靜態庫是一些目標文件的集合。

在 “winnt.h”頭文件裏定義了COFF格式文件和PE格式文件的數據結構。這些定義是一系列的結構體,枚舉和#define宏定義。在ImageHlp.dll裏定義了編輯讀取PE文件內容的Win32API。

在64位Windows操作系統下,PE格式文件被做了少部分修改。沒有新的字段定義被加入,並且去除了一些字段的定義,同時將字段的寬度從32位擴充到64位。64位windows操作系統下的PE格式文件被命名爲:PE32+。

COFF文件是什麼樣子的?
可以看看它的總體結構:
這裏寫圖片描述

從文件內容上來看,COFF文件由二進制數據組成。這些二進制數據從文件的零位置開始,依次存儲,直到文件末尾。從數據結構的角度來看,這些二進制數據又分別屬於不同的結構體或者結構體數組。這些結構體被定義在“winnt.h”頭文件中。

在COFF文件中,這些結構體或結構體數組分別表示不同的含義,記錄着COFF文件中的不同內容。結構體分成:文件頭、可選頭、段表、段數據、重定位表、行號表、符號表、符號表和字符串表。這些結構體數據之間存在關聯關係。比如:文件頭信息中存儲了符號表的開始位置,以及段表中數組元素的個數;在段表中存儲了各個段的位置,重定位表的位置,行號表的位置;重定位表中的項會關聯到符號表中的某個符號;而符號表中某個符號的名稱可能會存儲在字符串表中。

使用dumpbin.exe工具可以把目標文件的內容導出,它在“C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\bin”目錄裏面。
具體命令如下:

"C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\bin\dumpbin.exe" /all c_demo.exe > c_demo.exe.txt

在上面的命令中,將exe文件”c_demo.exe”的所有內容導出到文本文件”c_demo.exe.txt”。命令選項“/all”表示導出所有內容,命令選項“>”表示將導出的內容存儲到文件中。

文件頭以一個結構體的形式存儲在COFF文件的開始位置,佔20個字節的大小。每一個COFF格式的二進制文件都必須包含一個文件頭,它用來保存COFF文件的基本信息,如:文件標識,各個表的位置等。

使用dumpbin工具導出“c_demo.exe.txt”目標文件的內容後,文件頭部分的信息內容如下:

Dump of file c_demo.exe

PE signature found

File Type: EXECUTABLE IMAGE

FILE HEADER VALUES
             14C machine (x86)
               5 number of sections
        5636C776 time date stamp Mon Nov 02 10:16:22 2015
               0 file pointer to symbol table
               0 number of symbols
              E0 size of optional header
             10E characteristics
                   Executable
                   Line numbers stripped
                   Symbols stripped
                   32 bit word machine

OPTIONAL HEADER VALUES
             10B magic # (PE32)
            6.00 linker version
           1F000 size of code
            7000 size of initialized data
               0 size of uninitialized data
            14E0 entry point (004014E0)
            1000 base of code
            1000 base of data
          400000 image base (00400000 to 00426FFF)
            1000 section alignment
            1000 file alignment

在“winnt.h”頭文件中,文件頭被定義爲IMAGE_FILE_HEADER類型,具體的定義形式如下:

typedef struct _IMAGE_FILE_HEADER {
    WORD    Machine;
    WORD    NumberOfSections;
    DWORD   TimeDateStamp;
    DWORD   PointerToSymbolTable;
    DWORD   NumberOfSymbols;
    WORD    SizeOfOptionalHeader;
    WORD    Characteristics;
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;

#define IMAGE_SIZEOF_FILE_HEADER             20

可選頭,該數據結構爲可選數據,在目標文件中不存在此數據結構。只有當COFF文件作爲可執行文件存在的時候,該數據結構纔有意義。

段表是各個段的目錄,用於檢索各個段的信息。它以結構體數組的形式存儲在可選頭或者文件頭的後面。在段表中,每一項的大小是36個字節,數組元素的個數記錄在文件頭的“NumberOfSections”字段中。

段的劃分是基於各組數據的共同屬性,而不是邏輯概念。每段是一塊擁有共同屬性的數據,比如代碼/數據、讀/寫等。如果COFF文件中的數據/代碼擁有相同屬性,它們就能被歸入同一段中。

在段表中記錄了各個段在段數據區域中的位置(相對文件首位置的絕對偏移),以及各段重定位信息在重定位表中的位置。

在COFF格式的目標文件中,每一個函數形成一個.text段,因此會有多個名爲.text的段。在使用工具dumpbin導出“c_demo.exe”目標文件的內容後,除了列出.text段的同時,也將與該段相對應的重定位段一起列出。

在“winnt.h”頭文件中,文件頭被定義爲IMAGE_SECTION_HEADER類型。

重定位表
在編譯階段,將某些源文件編譯成目標文件的時候,在目標文件中,某些被調用函數或者數據的位置是無法確定的。這時候,編譯器將這些被調用的函數或者數據的地址設定爲一個默認的假值。在鏈接階段,當能夠確定這些被調用函數或數據的地址的時候,再用真實的地址來替換這些假值。我們將這個過程叫做重定位。

使用工具dumpbin將目標文件“c_demo.exe”的內容輸出爲彙編格式的文件後,可以觀察到這些假值的設定情況,以及需要重定位的位置。命令格式如下:

"C:\Program Files (x86)\Microsoft VisualStudio 12.0\VC\bin\dumpbin.exe" /disasm c_demo.exe > c_demo.exe.asm.txt

行號表

行號表描述了二進制代碼與源代碼行號之間的關係,調試階段使用。在“WinNT.h”頭文件中,文件頭被定義爲IMAGE_RELOCATION類型,

符號表
在編譯階段的詞法分析過程中,編譯器掃描整個C++源代碼,將源代碼中的函數名稱,變量名稱收集起來,然後寫入符號表中。在符號表中主要包含如下內容:函數名稱,變量名稱,段的名稱,以及一些常量信息,這些名稱被統稱爲符號。

符號表中的信息被用於靜態鏈接階段,用來進行被引用的函數或變量的地址重定位。每一個目標文件中都會包含一個符號表。在該符號表中的符號,要麼是在該目標文件中定義的函數名稱或變量名稱;要麼是被該目標文件引用的,定義於其他目標文件中的函數名稱或變量名稱。在靜態鏈接階段,多個目標文件進行鏈接的時候,存在於這些目標文件中的符號表會被合併到一起,形成一個全局符號表。在C++源代碼中出現的所有符號都應該能在全局符號表中被查找到。

將符號表中的符號進行分類,具體的分類情況如下:

定義在本目標文件中的全局符號,該符號可能會被其他目標文件引用;
在本目標文件中引用的全局符號,該符號定義在其他目標文件中,該符號被稱爲外部符號;
段的名稱,由編譯器加入到符號表中。該符號的值就是段的起始地址;
局部符號。在編譯單元內部可見,鏈接的時候忽略。
在執行鏈接的時候,只關注前兩種類型的符號。

如果符號的名稱小於8個字節,那麼將該符號的名稱直接存儲在符號表中;如果符號的名稱大於8個字節,那麼將符號的名稱存儲在字符串表中,原來符號表中存儲符號名稱的地方存儲了一個地址偏移量,該地址偏移量指向了字符串表中符號名稱的位置。

根據符號存儲類型以及符號在段中位置的不同,符號的值有不同的解釋。

字符串表

字符串表用來保存長度大於8個字節的符號名稱。字符串表的前4個字節表示字符串的長度,後面的緊跟字符串的內容,它以字節爲單位,以’\0’作爲字符串的結束符。這裏的字符串長度不僅僅是字符串自身的長度(字符串內容+’\0’),還包括前面4個字節的該數據自身的長度。

重定位表和符號表之間通過符號表的索引進行關聯;在文件頭中保存了可選頭的大小和段表所包含項目的數量,通過計算可以確定段表的起始位置和結束位置。段表起始位置=文件頭大小+可選頭大小;其他關係通過相對文件首位置的偏移表示。

Lib文件的結構
靜態鏈接庫就是一組目標文件的集合,當執行靜態鏈接的時候,被選定的目標文件的內容就會被合併到相關的Pe文件中去。靜態鏈接庫的總體結構如下圖所示:

這裏寫圖片描述

靜態鏈接庫以簽名開始,簽名的數據內容爲“(!\n”,長8個字節。緊跟在簽名後面的是三個特別成員,分別是第一鏈接器節,第二鏈接器節,以及長名稱節。在這三個特別成員之後,直到文件結束,存儲的都是目標文件節的內容。

第一鏈接器節,第二鏈接器節,長名稱節,以及目標文件節的數據結構都是由頭數據+節數據這樣的數據結構組成的。

第一鏈接器節。在靜態鏈接庫中必須存在該節,它包含了靜態鏈接庫中所有的符號名以及這些符號在靜態鏈接庫文件中的偏移;

第二鏈接器節。在靜態鏈接庫中該節可選,它包含了與第一鏈接器節相同的內容,但是它的內容是有序的,通過它查找符號要比在第一鏈接器節中查找的快;

長名稱節。在靜態鏈接庫中該節可選,它是一個字符串表,用於存儲名稱大於16個字節的目標文件的名稱。在目標文件節中,如果目標文件的名稱小於16個字節,那麼這個名稱會被存儲在頭文件的名稱域;如果這個名稱大於16個字節,那麼這個名稱就會被存儲到這裏。而在頭文件的名稱域存儲的則是該字符串在長名稱節的偏移。
目標文件節。該節是靜態鏈接庫的主要內容,節的數量不定,它存儲了若干各目標文件的內容,每一節都是頭信息+目標文件的結構。目標文件的結構與前面文件結構描述的一致。

PE文件的結構
總體結構圖
這裏寫圖片描述

從文件內容上來看,PE文件由二進制數據組成。這些二進制數據從文件的零位置開始,依次存儲,直到文件末尾。從數據結構的角度來看,這些二進制數據又分別屬於不同的結構體或者結構體數組。這些結構體被定義在“winnt.h”頭文件中。

在PE文件中,這些結構體或結構體數組分別表示不同的含義,記錄着PE文件中的不同內容。從文件的頂端開始,依次存儲了DOS頭,PE頭,段表,各段詳細數據等信息。在進行信息字段定位的時候,PE文件採用兩種方式:1利用指針。比如:在Dos頭中存儲一個指向PE頭的指針;2利用數據結構的大小。在PE的頭部信息中,一些數據結構的大小是固定的。在數據存儲的時候,各個數據結構緊湊存放,中間沒有空隙。在這種情況下,以一個數據結構的字段爲基點,通過計算數據結構佔用空間的大小,就可以定位另外一個數據結構的位置。

DOS頭
所有 PE文件都必須以DOS MZ header開始,它是一個IMAGE_DOS_HEADER的結構。有了它,一旦程序在DOS下執行,DOS就能識別出這是有效的執行體,然後運行緊隨MZ Header之後的DOS Stub。
在“winnt.h”頭文件中,DOS MZ 頭被定義爲IMAGE_DOS_HEADER類型

在DOS頭中,第一個域“e_magic”被稱爲魔術數字,它用於表示一個MS-DOS兼容的文件類型。所有MS-DOS兼容的可執行文件都將這個值設爲0x5A4D,表示ASCII字符MZ。MS-DOS頭部之所以有的時候被稱爲MZ頭部,就是這個緣故。

對於MS-DOS操作系統來說,許多其他的域都是有用的。但是對於 Windows NT來說,只有最後一個域e_lfnew是有用的,該域是一個指針,佔用4個字節,用於指明PE頭在文件中的位置。

DOS Stub

DOS Stub實際上是個有效的EXE,在不支持PE文件格式的操作系統中,它將簡單顯示一個錯誤提示,類似於字符串“This program requires Windows”,或者程序員可根據自己的意圖實現完整的DOS代碼。大多數情況下DOS Stub由彙編器/編譯器自動生成。

PE頭

PE頭緊跟在DOS MS頭以及實模式程序殘餘之後,在winnt.h頭文件中,PE頭被定義爲IMAGE_NT_HEADER類型.

在該結構體數據中,除了包含PE頭標識外,又嵌套了兩個結構體數據,分別是PE文件頭的信息,以及PE可選頭的信息。

PE頭標識

在一個有效的PE文件中,PE頭標識字段的值是0x00004550,用ASCII表示就是“PE00”。 #define IMAGE_NT_SIGNATURE定義了這個值。
PE中的文件頭與COFF中的文件頭定義一致。

可選頭

在PE文件頭之後,是PE可選頭。該頭224字節大小,它包含了許多重要的信息,例如:初始堆棧大小,程序的入口地址,首選加載基地址,操作系統版本,段對齊等。該頭並非可選,而是必須要有的頭。

在可選頭中,包含了三類主要信息,分別是:標準域信息,WinNT附加信息,以及數據目錄信息。

謂標準域就是指和UNIX可執行文件的COFF格式所公共的部分,雖然標準域中保留了COFF文件中定義的名稱,但是WindowsNT仍然將它用作了不同的目的。

在操作系統的加載器加載PE文件的時候,WinNT附件域的信息爲加載器提供了支持。

在可執行文件中有許多數據結構需要被快速定位,數據目錄提供了這種支持。數據目錄是一個指針列表,在該列表中保存了一系列的指針值,這些指針指向了其他的數據表。如:導入表,導出表,資源表,重定位表等。數據目錄以指針的形式提供了一種信息查找的方式。

使用工具dumpbin可以將PE文件的內容導出,在該內容中包含了描述可選頭的摘要信息。

在winnt.h頭文件中,可選頭被定義爲IMAGE_OPTIONAL_HEADER類型,具體的定義內容描述如下:

//數據目錄中數據元素的個數

#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES    16

//可選頭的定義

typedef struct _IMAGE_OPTIONAL_HEADER

{

    //標準域

    WORD    Magic;  //魔法數字

    BYTE    MajorLinkerVersion;//鏈接器的最大版本號

    BYTE    MinorLinkerVersion;//鏈接器的最小版本號

    DWORD   SizeOfCode;//可執行代碼長度

    DWORD   SizeOfInitializedData;//初始化數據的長度(data段)

    DWORD   SizeOfUninitializedData;//未初始化數據的長度(bss段)

    DWORD   AddressOfEntryPoint;//代碼的入口地址,程序從此處開始執行

    DWORD   BaseOfCode;//可執行代碼的起始位置

    DWORD   BaseOfData;//初始化數據的起始位置

    //NT附件域

    DWORD   ImageBase;//載入程序首選的相對虛擬地址

    DWORD   SectionAlignment;//段加載到內存以後的對齊方式

    DWORD   FileAlignment;//段在文件中的對齊方式

    WORD    MajorOperatingSystemVersion;//操作系統最大版本號

    WORD    MinorOperatingSystemVersion;//操作系統最小版本號

    WORD    MajorImageVersion;//程序最大版本號

    WORD    MinorImageVersion;//程序最小版本號

    WORD    MajorSubsystemVersion;//子程序最大版本號

    WORD    MinorSubsystemVersion;//子程序最小版本號

    DWORD   Win32VersionValue;//這個值一直爲零

    DWORD   SizeOfImage;//程序加載到內存以後,佔用內存的大小。

    DWORD   SizeOfHeaders;//文件頭部總大小

    DWORD   CheckSum;//校驗和

    WORD    Subsystem;//一個標明可執行文件所期望的子系統的枚舉值

    WORD    DllCharacteristics;//dll狀態

    DWORD   SizeOfStackReserve;//保留棧大小

    DWORD   SizeOfStackCommit;//啓動後實際申請棧數

    DWORD   SizeOfHeapReserve;//保留堆的大小

    DWORD   SizeOfHeapCommit;//啓動後實際申請堆數

    DWORD   LoaderFlags;

DWORD   NumberOfRvaAndSizes;

//數據目錄的定義

    IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];

} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;

在IMAGE_OPTIONAL_HEADER32可選頭文件結構裏BYTE類型字段MajorLinkerVersion創建可執行文件的鏈接器的主版本號。對於Microsoft的鏈接器生成的PE文件,這個版本號的Visual Studio的版本號相一致。
BYTE類型字段MinorLinkerVersion表示創建可執行文件的鏈接器的次版本號。

導入表

當一個可執行程序或者動態鏈接庫調用另外一個動態鏈接庫中的變量或者函數的時候,必須將這些被調用函數或者變量導入。這些被導入的變量或函數是必須是被調用動態鏈接庫導出表中的一個子集。也就是說,只有當這些變量或者函數被導出以後,才能被其他可執行程序或者動態鏈接庫導入。

在PE文件中,我們將這些變量和函數統稱爲符號,這些被導入的符號的信息被存儲在導入表中。在PE文件中,導入表位於.idata段中,該段可以單獨存在。

對於每一個可執行程序或者動態鏈接庫,只要它調用了其他動態鏈接庫中的符號,那麼就會有一個或多個IMAGE_IMPORT_DESCRIPTOR類型的數組與之對應。該數組元素的個數與該可執行程序或者動態鏈接庫所依賴的動態鏈接庫的數量有關。

在IMAGE_IMPORT_DESCRIPTOR類型的數據結構中,存儲的是與導入表相關的信息。該數據結構與其他數據結構發生關聯,與該數據結構發生關聯的數據實體包括:數據目錄,字符串表,導入地址表,導入名稱表。它們之間的關係如下圖所示:
這裏寫圖片描述

在可選頭中包含了數據目錄,在數據目錄的第二項中,存儲了指向導入表的位置的指針,以及該數據結構的大小。在數據目錄的第十二項中,存儲了指向第一個IAT的位置的指針,以及所有IAT數組的大小。

導入表是一個數組,該數據元素的類型是IMAGE_IMPORT_DESCRIPTOR。在該數組中,每一個數組元素都會對應一個被導入的DLL。在該數組的末尾,存儲了一個所有的域爲零的IMAGE_IMPORT_DESCRIPTOR類型的數組元素,表示該導入表結束。因此,在一個可執行程序或者動態鏈接庫的導入表中,至少會包含兩個數據元素。一個對應被導入符號的信息,一個所有域爲零。

在每個導入表的數組的元素中,擁有兩個地址字段,它們分別指向了導入地址表(IAT)和導入名稱表(INT)。在導入地址表和導入名稱表中,存儲了被導入符號的名稱或地址,它們是一個擁有多個數據元素的數組。因爲導入表的數組元素可能是多個(當前可執行程序或動態鏈接庫依賴多個其他的動態鏈接庫),所以在一個動態鏈接庫的導入信息中也會存在多個IAT數組以及INT數組。當PE文件被加載到內存以後,這些IAT數組會被存儲在一塊連續地內存區域中,它們首尾相連,中間沒有空隙。在數據目錄的第十二項中,存儲了第一個IAT的位置,以及所有IAT數組的大小。導入表的結構佈局如下圖所示:

這裏寫圖片描述
導入地址表和導入名稱表的數據結構是一致的,它們均爲IMAGE_THUNK_DATA類型,並以一個所有域均爲零的數組元素結尾。導入地址表用於動態鏈接,而導入名稱表用於符號地址綁定。在PE文件中,導入地址表和導入名稱表中的數據內容是一樣的,它們都存儲着被導入符號的名稱或者序號;在程序加載階段,導入名稱表的數據內容不會發生變化,而導入地址表中的被導入符號的序號或者名稱將會被替換爲該符號的真實虛擬內存地址。導入名稱表的數據內容永遠不會發生變化。
對於每一個動態鏈接庫,都會有一個IMAGE_EXEPORT_DIRECTORY類型的數據結構與之對應。該數據結構保存了與導出表有關的信息,並與其他數據結構發生關聯。與該數據結構有關聯的數據實體包括:數據目錄,字符串表,符號地址表,符號名稱表,名稱序號對應關係表。這些實體之間的關係如下圖所示:
這裏寫圖片描述

可選頭中包含了數據目錄,數據目錄的第一項指向了導出表。導出表是一個結構體類型,在該結構體的字段中保存了與導出表相關的信息,如:DLL名稱字段指向了一個字符串表,在該字符串表中保存了DLL的名稱;符號地址表地址字段指向了一個地址數組,該數組中保存了符號的地址;符號名稱表地址字段指向了符號名稱數組,該數組中保存了符號名稱字符串的地址;名稱序號對應關係表地址指向了名稱序號對應關係的數組,該數組中存儲了符號的名稱與序號之間的對應關係。

 符號地址表是一個DWORD類型的數組,在該數組中存儲了被導出符號的相對虛擬地址,數組中每一個非零的數組元素都指向了一個符號的相對虛擬地址;符號名稱表是一個DWORD類型的數組,數組中保存的是相對虛擬地址,該相對虛擬地址指向了字符串表中的相關位置,這些位置中保存了符號的名稱;名稱序號表是一個WORD類型的數組,該數組中保存了符號的序號。符號名稱表中的元素與名稱序號表中的元素通過數組下標對應。比如:在符號名稱表中,數組下標爲1的元素,它的序號存在在名稱序號表中,數組下標也爲1。

 序號的計算公式爲:序號 = 符號地址表數組下標 + Base字段值。在Windows16位時代,由於受到硬件大小的限制,在執行動態鏈接的時候,使用序號查找符號的地址,即:用序號的值減去Base的值,獲得符號地址表數組的下標,進而獲得符號的相對虛擬內存地址。這種方式節省了內存空間以及符號查找的時間,但是易讀性差。隨着時間的發展,當硬件的物理內存不在是問題的時候,開始使用符號名稱查找符號的地址,具體的查找過程是:通過符號名稱在名稱序號對應關係表中查找到符號的序號,然後再用符號的序號查找符號的地址。雖然引入了符號名稱表,但是這個表不是必須的,依然可以通過序號查找符號的地址。在一個DLL中,每一個導出符號都有一個唯一對應的序號,而導出符號名是可選的。

在動態鏈接的時候,可以通過兩種方式進行符號地址的查找,一種是直接利用符號的序號直接查找,另外一種是利用符號的名稱間接查找。在進行符號地址查找的時候,符號地址表,符號名稱表,名稱序號對應表之間的關係如下圖所示:

這裏寫圖片描述

基址重定位表

在目標文件中,所有符號的地址都是基於文件頭或者文件中某個位置的偏移地址。在將目標文件鏈接以後,在輸出的PE文件中,這些偏移地址會被轉化成相對虛擬內存地址,並且再加上一個默認內存加載位置的地址值,形成符號的基於默認內存加載位置的虛擬內存地址。基於默認內存加載位置的虛擬內存地址的計算公式爲:

符號虛擬內存地址 = 默認內存加載位置 + 相對虛擬內存地址

可執行程序默認加載到內存的0x0400000位置,而動態鏈接庫默認加載到內存的0x10000000位置。

當PE文件被加載到內存以後,如果該文件不能被加載到默認的內存位置,那麼在指令代碼中,所有使用絕對地址表示的符號的地址都需要被重定位。在Windows中,這一地址重定位的過程被叫做重定基地址。具體的操作過程是:在每一個需要進行地址重定位的符號處,將該符號當前地址的數值上再加上一個固定的數值,這個新獲得的地址值就是該符號正確的虛擬內存地址。

這個固定值的計算公式是:

固定值 = DLL當前內存加載的位置 – DLL默認內存加載位置(0x10000000)

地址重定位工作由操作系統的加載器來完成,在基地址重定位表中,記錄了每一個需要進行地址重定位的符號的地址。在地址重定位的時候,加載器讀取該表中的數值,然後查找到需要進行地址重定位的符號的位置,最後修正該符號的虛擬內存地址。

在PE文件被加載到內存以後,這些文件內容是以頁爲單位存儲在內存中,每個內存頁的大小是4KB。在基址重定位表中,數據表中的數據被分割成一個個數據塊,每一個數據塊會對應一個虛擬內存頁,表示在該虛擬內存頁中的符號的地址重定位信息。
基址重定位表的結構如下圖所示
這裏寫圖片描述

可選頭中包含了數據目錄,數據目錄的第五項數據中包含了指向了基址重定位表的指針,以及基址重定位表的大小。基址重定位表以內存頁的大小爲依據進行分塊,在每一個塊中,都以IMAGE_BASE_RELOCATION類型的數據結構開頭,後面跟隨着每個符號的基地址重定位信息。這些符號的重定位信息是一系列的WORD值。這些WORD值的高4位指出了重定位的類型,而低12位是一個地址偏移。將該地址偏移數值與數據塊的虛擬內存地址數值(即:IMAGE_BASE_RELOCATION. VirtualAddress)相加,可以得到該符號需要進行重定位的位置。
在基址重定位表的數據塊中,所包含的重定位信息的個數的計算公式爲:

重定位信息個數 = (塊大小 – sizeof(IMAGE_BASE_RELOCATION))/2

因爲塊大小以字節爲單位表示,而重定位信息以字爲單位表示,轉化成字需要除2

重定位地址的計算公式爲:默認加載位置 + 數據塊相對虛擬內存地址 + 偏移 = 0x10000000 + 0x11000 + 0x54F = 0x1001154F。處於虛擬內存地址0x1001154F處的地址值需要被重定位。

各數據結構之間的關係

在PE文件中,各個數據結構之間的關係如下圖所示:
這裏寫圖片描述

Windows裏的二進制可執行文件exe和dll文件是COFF/PE文件格式的文件,通過了解Windows的編譯、鏈接裏的COFF/PE文件結構,可以知道,COFF/PE文件結構裏有一個可選文件頭。
在IMAGE_OPTIONAL_HEADER32可選頭文件結構裏BYTE類型字段MajorLinkerVersion創建可執行文件的鏈接器的主版本號。對於Microsoft的鏈接器生成的PE文件,這個版本號的Visual Studio的版本號相一致。
BYTE類型字段MinorLinkerVersion表示創建可執行文件的鏈接器的次版本號。

使用vc6.0和vs2103編譯一樣的代碼,dumpbin.exe的結構比較如下圖:
這裏寫圖片描述

vs2013對應的c運行時庫是msvcr120.dll,
vc6.0對應的c運行時庫是msvcrt.dll。

exe或者dll根據entry point選擇要鏈接的c運行時庫來連接。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章