segment 寄存器的真實結構

Segment Register 也能夠稱做 Selector Register,它在整個 x86/x64 體系下實在過重要了。html

這裏講解的是 user segment 寄存器,包括:spa

  • Code 段寄存器:CS
  • Data 段寄存器:ES, SS, DS, FS 以及 GS

這些段寄存器由 user segment descriptor 裝載進去。code

咱們頗有必要先去了解 segment 寄存器的真實結構:htm

上圖是我畫的 Segment Registers 內部結構圖,這分爲 4 個部分:seo

  • Selector
  • Attribute
  • Limit
  • Base

圖中已經標出灰色部分是 processor 內部使用的,僅僅只有 Selector 部分能夠由用戶使用ip

可是在 64 位模式下,它很特別內存

  • Base 64 位的,可是對於 CS, ES, DSSS 來講 Base 是無效的,而對於 FSGS 來講,它們是有效的,64 位的 Base address 能夠由用戶設定
  • Attribute 只有少數幾個屬性位是有效的
  • Limit 對於全部的 segment registers 來講都是無效的。

注意上面所說的是針對在 64 位模式下。ci

segment registers 結構體現了 protected mode 下的幾大元素:get

  • selector
  • descriptor
  • descriptor table

 

1. 提供基址

Segment registers 的 base 提供基址,不管是 real mode 下仍是 protected mode 下,其地址的計算方法都是:base + offsetit

若是有下面代碼:

     jmp next

next:

目標代碼地址在 CS.base + eip

 

2. Segment Registers 的 Attribute 部分

Segment Registers 的 Attribute 是描述段的屬性,它在裝載 segment descriptor 的時候加載進去設置的,它包括:

  • G 屬性:segment limit 的粒度,G = 1 4K bytes 粒度, G = 01 byte 粒度
  • D/B 屬性:有兩重意義:
    • D 屬性:對於 code segment 來講表明 default operandsD = 132default operandsD = 016default operands
    • B 屬性:對於 stack segment 來講表明 default stack sizeB = 132stack size, B = 016stack size
  • P 屬性:present 位,表示是否加載到內存中
  • DPL 屬性:Privilege-Level(權限級別)範圍從 0 - 3
  • S 屬性:S = 0 是系統數據 segment, S = 1 用戶 segment
  • Type 屬性:表明 segment 類別

在 real mode 下 segment registers 的 Attribute 通常狀況下是初始狀態。在 protected mode 下它隨着 descriptor 而改變

 

2.1 64 模式下的 segment registes attribute

64 位模式下大多數屬性位都是無效的,而且在 code segment register 和 data segment register 之間也存在差異:

2.1.1 CS 寄存器

CS 寄存器裏新增了一個屬性位:

  • L 屬性: 它用來指示 processor 當前處於 64 bit 模式仍是 compatibility 模式:
    • CS.L = 1 時,processor 處於 64 bit 模式
    • CS.L = 0 時,processor 處於 compatibility 橫式

64 位下 CS 寄存器只有下面的一些屬性位是有效的:

  • D 屬性
  • L 屬性
  • P 屬性
  • DPL 屬性
  • C 屬性

可是請注意:

必須要設置 CS 寄存器的 S 屬性和 C/D 屬性:

  • CS.S = 1 表示:用戶的段寄存器
  • CS.C/D = 1 表示:該 segment register 是代碼段寄存器

這也就是說:加載到 CS 寄存器的 code segment descriptor 你必須將它的 S 屬性設爲 1,C/D 屬性設爲 1 才能加載到 CS 寄存器中

S 屬性用來設置 system 仍是 user 的段寄存器,屬性 system 的段寄存器有:LDTR 寄存器和 TR 寄存器

C/D 屬性用來指示是 Code 仍是 Data 段。

CS 寄存器來講,您必須設置這兩個屬性位爲 1 代表它是用戶代碼段寄存器,不然會產生 #GP 異常

 

2.1.2 Data segment registers(ES, DS, FS 以及 GS 寄存器)

對於這幾個 data 段寄存器(SS 寄存器有些特別,除外)來講,只有下面這個屬性纔有效:

  • P 屬性

可是一樣須要注意:

必須設置 data 段寄存器的 S 屬性和 C/D 屬性(以 DS 寄存器爲例):

  • DS.S = 1 表示:用戶的段寄存器
  • DS.C/D = 0 表示:它是 Data 段寄存器

可是有一種例外

  • 使用 NULL selector 加載到 data segment register 是容許的,processor 將加載 invalid 不可用的 descriptor 到 segment registers 中

也就是說:除了使用 NULL selector 加載外,data segment descriptor 的 S 屬性需設爲 1C/D 屬性需設爲 0 才能加載到 data segment registers 中

DS 段寄存器來講,S 必須爲 1 而且 C/D0 代表它是用戶的數據段寄存器,不然會產生 #GP 異常

 

2.1.3 SS 寄存器

SS 寄存器是 data segment register 的其中一種,除了要遵循上面的 data segment register 規則外。

它還必須

SS 寄存器的 attribute 域的 type 裏:

  • W = 1(Writable)
  • E 忽略
  • A 忽略

這表示:由 SS 寄存器訪問的 stack segment必須是可寫的,所以:加載到 SS 寄存器的 data segment descriptor 它的 W 屬性必須設爲 1(表示可寫)

64 位模式下可使用 NULL selector 加載到 SS 寄存器中,processor 不會去讀取 GDT 表中的第 1 項,而是以 invalid unused 的 segment 到 SS 寄存器中

可是在 legacy x86 模式和 compatibility 模式下是不能使用 NULL selector 加載來 SS 寄存器的。

 

3. 更新 segment register 的 base

瞭解 segment registers 結構有什麼好處呢? 咱們來了解一下 segment register 的 base 更新狀況。

base 的更新在 real modeprotected mode 下是不一樣的。

 

3.1 real mode 下的 base 更新

咱們來看一下在 real mode 下 segment register 的情形

以上面的代碼爲例:

        mov ax, cs
        mov ds, ax

這兩條代碼是在 real mode 下,它有什麼奧祕呢?

  • 指令 mov ax, cs 中是將 CS 寄存器的 selector 賦給 ax,也就是:mov ax, CS.selector

指令 mov ds, ax 改變了 DS 寄存器的兩個值:

  • DS.selector = ax
  • DS.base = DS.selector << 4

DS 寄存器的 selector 獲得更新,同時 base 也獲得更新,這個 DS 寄存器的 base 的更新規則就是 real mode 下的尋址方式:

  • address = segment * 16 + offset

 

3.2 protected mode 下 base 的更新

protected mode 下,base 的更新是在加載 descriptor 進入 registers 時發生:base 被更新爲 segment descriptorbase

 

4. segment register 的 limit 值

在 real mode 下 segment register 的 limit 通常狀況下固定爲 0xFFFF(64K 大小),這個值在 processor 初始化時設定。

在 protected mode 下這個值最大範圍爲 0xFFFFFFFF(4G 大小)依賴於加載的 descriptor 的 limit 值。

 

5. Segment Registers 的裝載

下面代碼是在 protected mode 下執行:

        bits 32

code32_entry:
        mov ax, data32_sel
        mov ds, ax

將 data segment descriptor 的 selector 賦給 ax,下面的指令:

  • mov ds, ax

將會引起 processor 作一系列的工做:

  • 獲取 data segment descriptor
  • 檢查訪問權限
  • 更新 DS 寄存器內部結構

經過權限檢查後 processordata segment descriptor 相應的信息更新 DS 內部結構,包括:

  • selector 更新
  • attribulte 更新
  • limit 更新
  • base 更新

可是在 real mode 下若是不轉到 protected mode 下更新,segment register 的 attribute limit 是永遠得不到更新的。它的 limit 值固定爲 0xFFFF

關於real mode 下 segment 的 attrbiute 屬性,請參見:http://www.mouseos.com/arch/001.html 裏有詳細描述。

前面已經講過,selector 會獲得更新,而 base 會被更新爲 selector << 4

看看下面這段僞代碼:

struct SELECTOR selector = 8;                                /* selector = 0x08 */

struct DESCRIPTOR descriptor = get_descriptor(selector);     /* get segment descriptor */

/* update DS register ... */

DS.selector = selector;
DS.attribute = descriptor.attribute;
DS.limit = descriptor.G ? descriptor.limit * 4096 + 0xfff : descriptor.limit;
DS.base = descriptor.base;

使用了 selector 爲 8 得到 descriptor 後進行 DS 寄存器的更新,對 base 的更新要視乎 G 標誌位:

  • G = 1 時,表示粒度爲 4K,limit 乘 4K 後加上值 0xfff,這是爲了保證 0xFFFFFFFF 的最大 limit 值
  • G = 0 時,表示粒度爲 1 byte,limit 就等於 descriptor 的 limit 值

 

6. 描述 segment register 結構

下面是 C 代碼的描述:

struct SEGMENT_REGISTER {

         unsigned short selector; //16bit
         unsigned short attribute;
         unsigned int limit; //32bit

#ifdef __x64__
         unsigned long long base;
#else
         unsigned int base;
#endif

};

相關文章
相關標籤/搜索