EXE文件的重定位
這份文檔基於EXE的文件格式 https://www.cnblogs.com/zxyLeaf/articles/14132526.html
前面在解釋文件頭的第[6-7] ,[18h-19h]字節含義時並沒有說明什么是重定位表,什么是重定位項。因為這涉及到一個重要的概念叫做重定位。 這一小節就來說明什么是重定位。
背景知識及定義
一個.asm
匯編文件,經過編譯器編譯后生成.obj
文件,再由鏈接器(linker)鏈接生成.exe
文件,也就是可執行程序,這個可執行程序被保存在硬盤(disk)中。當我們運行這個程序時,加載器(loader)會把在硬盤中的可執行文件加載到內存(memory)上的空余位置,這時運行中的程序會就是進程。
當編譯器,鏈接器在生成可執行文件時,並不會知道程序在真正運行時加載到哪一個物理地址上,所以編譯器對於段,變量,函數的編譯均采用相對地址的形式進行。但是程序真正在內存中運行時,CPU是需要知道段,變量等真正的物理地址的。
因此,在加載程序到內存時,誰來將編譯器產生的相對地址轉換成絕對物理地址呢?這個操作又是如何完成的呢?
上面的第一個問題就是重定位要解決的事情,這里我們給出重定位的定義:
操作系統根據EXE文件頭中的重定位表將程序中引用的段地址進行修正的過程稱為重定位
接下里的內容由兩大部分組成:第一部分在驗證在編譯完成后,EXE文件中的變量,段址記錄的都是相對值;第二部分用來說明重定位的具體過程。
儲存在硬盤中的EXE文件
hello2.asm
的具體代碼如下:
data segment; 1000:0000
abc db "Hello, abc!$"; 12字節=0Ch字節
db 10h dup(0); 16字節
data ends
code segment;
assume cs:code, ds:data, ss:stk
begin:
mov ax, data
mov ds, ax
mov ah, 9
mov dx, offset abc
int 21h
mov ax, code; 編譯時code被設成以下值
; (code段-首個段的距離)/10h
mov ds, ax
mov ah, 9
mov dx, offset xyz
int 21h
mov ah, 4Ch
int 21h
xyz db "Hello, xyz!$"
main:
jmp begin
code ends
stk segment stack
db 100h dup('$'); 為了方便查看,將堆棧中的初始值改為'$'
stk ends
end main
通過Qview打開hello2.exe
文件,跳過200h字節的文件頭,來到程序開始的地方
可以看到編譯后源文件中的段,變量被編譯成了相對地址
mov ax, data
--------------------mov ax, 0000
-----------------data與第一個段地址的差(data)=0mov dx, offset abc
---------mov dx, 0000
-----------------abc位置與該段段首的差=(0200Ch-0200h)=0hmov ax, code
-------------------mov ax, 0002
-----------------code與data段地址的差=(0220h-0200h)/10h=2hmov dx, offset xyz
---------mov dx, 001C
-----------------zxy位置與該段段首的差=(023Ch-0220h)=1Ch
根據上述結果,我們可以知道,在程序加載到內存中開始運行時,我們只要得到每個段的物理地址,我們就可以計算出每個變量的物理地址。那么要如何根據程序的代碼段來算出每個段的物理地址呢?這里涉及到兩個問題:
-
如何判斷編譯后的exe文件中指令中的立即數是一個地址?例如,
mov ax,0000
這條指令中0000
是一個相對地址還是一個立即數呢?這就是文件頭中重定位表項的作用了。重定位項一定指向需要重定位的段地址所在的地方
-
根據重定位項查到一個相對地址后,重定位的具體過程是怎樣的?
操作系統把需要重定位的段地址取出來,和首段的段地址相加后再寫回去
等重定位完成后,設置ds=es=psp設置ss:sp,再jmp cs:ip
重定位表
重定位表是由重定位項組成的,每一個重定位項指向需要重定位的段地址所在的地方。為了確定重定位項指向的具體地址,EXE文件頭采用Δ段地址:偏移量
的方式。
所以,每一個重定位項含有四個字節:其中前兩個字節是需要重定位的數據的偏移地址,后兩個字節是段地址的Δ值,即需要(重定位的段地址-首段段地)址。
讀懂文件頭中的重定位表
現在,我們通過具體的例子來說明重定位的過程。
- [6-7]字節:重定位表項的個數
- [18h-19h]字節:重定位表的相對文件頭的偏移位置
- 根據[18h-19h]字節找到重定位表,以及[6-7]字節確定重定位表的長度=
重定位表項的個數*4
個字節
繼續看hello2.exe的文件頭
00000000: 4d5a 5001 0200 0200 2000 0000 ffff 0500 MZP..... .......
00000010: 0001 e80e 2800 0200 1e00 0000 0100 0100 ....(...........
00000020: 0200 0d00 0200 0000 0000 0000 0000 0000 ................
加粗的部分均為在文件頭中和重定位有關系的字節
- [6-7]字節表示重定位表項的個數=0002h
- [18h-19h]字節表示重定位表的相對文件頭的偏移位置位001eh
- [1eh-25h]字節表示重定位表中的兩項
- 第一項:偏移量=0001h, Δ段地址=0002h
- 第二項:偏移量=000dh, Δ段地址=0002h
獲得程序首段地址:程序的首段地址=psp+10h
假設程序首段地址=1010h,第一項重定位項的重定位過程如下:
- 確定需要重定位的位置:
(1010+0002):0001=1012:0001
- 更改該位置中的段地址值:
word ptr 1012:[0001] += 1010h
具體例子
- 通過td調試hello2.exe文件
-
當前ds=es=psp的段地址,首段地址=psp+10h=5292h+10h=52A2h
-
根據重定位項的第一項:偏移量=0001h, Δ段地址=0002h,找到需要重定位的地方是在
52A4:0001
處 -
然后進行重定位:
word ptr 52A4:[0001]=0000h+首段地址=0000h+52A2h=52A2h
也就是
mov ax,52A2
這條指令中的52A2