數據段描述符和代碼段描述符(一)——《x86匯編語言:從實模式到保護模式》讀書筆記10


一、段描述符的分類

在上一篇博文中已經說過,為了使用段,我們必須要創建段描述符。80X86中有各種各樣的段描述符,下圖展示了它們的分類。

段描述符的分類

看了上圖,你也許會說:天啊,怎么這么多段描述符啊!我可怎么記住呢?哭泣的臉 別擔心,我會在以后的博文中,跟隨原書的作者,為您逐步介紹。我們的學習是循序漸進的,所以不要求一下子掌握所有東西。我們的原則是:用到什么學什么。我們今天的重點是“存儲段描述符”。

二、段描述符的通用格式[1]

段描述符是GDT和LDT中的一個數據結構項,用於向處理器提供有關一個段的位置、大小以及訪問控制的狀態信息。每個段描述符的長度是8個字節,含有3個主要字段:

  • 段基地址
  • 段限長
  • 段屬性

段描述符通常由編譯器,鏈接器,加載器或者操作系統來創建,但絕不是應用程序。

下圖給出了所有類型的段描述符的一般形式。

捕獲

 

1.段限長字段Limit

用於指定段的長度。處理器會把段描述符中兩個段限長字段組合成一個20位的值,並根據顆粒度標志G來指定段限長Limit值的實際含義。

如果G=0,則Limit值的單位是B,也就是說Limit的范圍可以是1B到1MB;

如果G=1,則Limit值的單位是4KB,也就是說Limit的范圍可以是4KB到4GB。

根據段類型字段TYPE中的段擴展方向標志E,處理器可以以兩種不同的方式使用Limit。

E=0:表示向上擴展的段(簡稱上擴段),邏輯地址中的偏移值范圍可以從0到Limit;

E=1:表示向下擴展的段(簡稱下擴段),邏輯地址中的偏移范圍可以從Limit到0xFFFF(當B=0時)或者0xFFFF_FFFF(當B=1時)。關於B位,后面將解釋。

2.基地址字段Base

該字段定義在4GB線性地址空間中一個段的字節0所處的位置。也許你覺得這句話不好理解,我們換一種說法:對於一個邏輯地址,如果段內偏移為0,那么這個邏輯地址對應的線性地址就是Base;如果段內偏移為X,那么這個邏輯地址對應的線性地址就是Base+X;

段基地址可以是0~4GB范圍內的任意地址(這同實模式不同,實模式下段基地址要求16字節對齊),但是,為了讓程序具有最佳性能,還是建議段基地址對齊16字節邊界。

3.段類型字段TYPE

該字段用於指定段或者門(Gate)的類型、說明段的訪問種類以及段的擴展方向。該字段的解釋依賴於描述符類型標志S;TYPE字段的編碼對代碼段、數據段或者系統描述符都不同。

4.描述符類型標志S

S=0:表示存儲段描述符。所謂“存儲段”,就是存放可由程序直接進行訪問的代碼和數據的段。說白了,存儲段就是代碼段或者數據段。

S=1:表示系統描述符。

5.描述符特權級字段DPL

用於指明描述符的特權級。特權級范圍從0(最高)到3(最低)。DPL字段用於控制對段的訪問。

6.段存在標志P

用於指出一個段是在內存中(P=1)還是不在內存中(P=0).

7.D/B(默認操作數大小/默認棧指針大小和上界限)

對於代碼段,此位稱為“D”位;對於棧段,此位稱為“B”位。我們在后文會說。

8.顆粒度標志G

該字段用於確定段限長字段Limit值的單位。

如果G=0,則Limit值的單位是B;

如果G=1,則Limit值的單位是4KB;

注意:這個字段不影響段基地址的顆粒度,基地址的顆粒度總是以字節為單位。

9.可用和保留位

L位(就是上圖灰色的那個位):是64位代碼段標志,保留此位給64位處理器使用。目前,我們將此位置“0”即可。

AVL:是軟件可以使用的位,通常由操作系統來用,處理器並不使用它。

 

三、數據段描述符[1]

當S=1且TYPE字段的最高位(第2個雙字的位11)為0時,表明是一個數據段描述符。

下圖是數據段描述符的格式。

數據段描述符

1.B位(默認棧指針大小和上界限)

對於棧段(由SS寄存器指向的數據段)來說,該位用來指明隱含堆棧操作(如PUSH、POP或CALL)時的棧指針大小。

B=0:使用SP寄存器

B=1:使用ESP寄存器

同時,B的值也決定了棧的上部邊界。

B=0:棧段的上部邊界(也就是SP寄存器的最大值)為0xFFFF;

B=1:棧段的上部邊界(也就是ESP寄存器的最大值)為0xFFFF_FFFF.

2.A位(已訪問)

用於表示一個段最近是否被訪問過(准確地說是指明從上次操作系統清零該位后一個段是否被訪問過)。

當創建描述符的時候,應該把這位清零。之后,每當該段被訪問時(准確地說是處理器把這個段的段選擇符加載進段寄存器時,也許你不懂這句話,沒有關系,現在忽略就可以了。)它就會將該位置“1”;對該位的清零是由操作系統負責的,通過定期監視該位的狀態,就可以統計出該段的使用頻率。當內存空間緊張時,可以把不經常使用的段退避到硬盤上,從而實現虛擬內存管理。

3.W位(可寫)

指示段的讀寫屬性。

W=0:段不允許寫入,否則會引發處理器異常中斷;

W=1:允許寫入。

4.E位(擴展方向)

E=0:表示向上擴展的段(簡稱上擴段),邏輯地址中的偏移值范圍可以從0到Limit;

E=1:表示向下擴展的段(簡稱下擴段,通常是棧段),邏輯地址中的偏移范圍可以從Limit到0xFFFF(當B=0時)或者0xFFFF_FFFF(當B=1時)。

四、代碼段描述符[1]

當S=1且TYPE字段的最高位(第2個雙字的位11)為1時,表明是一個代碼段描述符。

下圖是代碼段描述符的格式。

代碼段描述符

1.D位(默認操作數大小)

用於指出該段中的指令引用有效地址和操作數的默認長度。

D=0:默認值是16位的地址和16位或者8位的操作數;

D=1:默認值是32位的地址和32位或者8位的操作數;

說明:指令前綴0x66可以用來選擇非默認值的操作數大小,指令前綴0x67可以用來選擇非默認值的地址大小。

2.A位(已訪問)

與數據段描述符中的A位相同。

3.R位(可讀)

R=0:代碼段不可讀,只能執行。

R=1:代碼段可讀,可執行。

也許有人會問,當R=0時,既然代碼段不可讀,那處理器怎么從里面取指令執行呢?事實上,這里的R屬性並非針對處理器,而是用來限制程序的行為。當常數或者靜態數據被放在了一個ROM中時,就可以使用一個可讀可執行的代碼段,然后通過使用帶CS前綴的指令,就可以讀取代碼段中的數據。

注意:

  • 在保護模式下,代碼段是不可寫的。
  • 堆棧段必須是可讀可寫的數據段。

4.C位(一致性)

C=0:表示非一致性代碼段。這樣的代碼段可以被同級代碼段調用,或者通過門調用;

C=1:表示一致性代碼段。可以從低特權級的程序轉移到該段執行(但是低特權級的程序仍然保持自身的特權級)。

注意:所有的數據段都是非一致性的,即意味着它們不能被低特權級的程序或過程訪問。然而與代碼段不同,數據段可以被更高特權級的程序或過程訪問,而無需使用特殊的訪問門。

有關特權級和特權級檢查我們以后再討論,這里對上面的概念了解即可,不用深究。

最后,補充一張圖表,也是引用自趙炯的《Linux內核完全剖析》。

 

表4-3

參考資料:

[1]:趙炯,《Linux內核完全剖析》,機械工業出版社. 4.3.4節


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM