2016-02-28 79 views
1

我目前遵循啓用GDT分段的指南。我使用GNU彙編程序,用Bochs進行仿真。無法理解啓用GDT分段,即更新CS寄存器

我知道我需要用GDT描述符加載GDT寄存器。我已經完成了,接下來的步驟是現在將所有帶有偏移量的段寄存器加載到代碼/數據段描述符的各個位置。這樣做的代碼表述爲以下幾點:

reloadSegments: 
    ; Reload CS register containing code selector: 
    JMP 0x08:reload_CS ; 0x08 points at the new code selector 
.reload_CS: 
    ; Reload data segment registers: 
    MOV AX, 0x10 ; 0x10 points at the new data selector 
    MOV DS, AX 
    MOV ES, AX 
    MOV FS, AX 
    MOV GS, AX 
    MOV SS, AX 
    RET 

我不能,但是,瞭解它是如何可能與不跳轉到的顯然是必然結果偏移隱式加載CS寄存器的任何存儲位置是尖通過CS:IP對 - 即,如果代碼段描述符位於GDT_start + 0x10處,並且我試圖將0x10加載到CS寄存器,則虛擬機跳轉到0x10:IP,並且我從不輸入.reload_CS標籤。

我日常的版本(在& T語法):

_start: 
    // Disable interrupts 
    cli 
    // Load GDT register with location of GDT 
    lgdt 0x3c 

    // Load location of Code segment descriptor into cs 
    ljmp $0x2c, $reload_cs 

reload_cs: 
    mov $0x34, %ax 
    mov %ax, %ds 
    mov %ax, %es 
    mov %ax, %fs 
    mov %ax, %gs 
    mov %ax, %ss 

loop: 
    jmp loop 

PS:我不知道爲什麼ljmp $0x2c, reload_cs不起作用 - 前綴reload_cs$編譯但標籤通常並不需要此語法我的經驗...

+0

這是一個很大的跳躍,不確定哪一部分會讓你感到困惑?它會跳轉到給定的地址,同時也重新加載'CS'。這是你的工作,建立'GDT',讓你跳到正確的位置。如果段基址爲'0',並且處於'org 0'模式,則它將是正確的。 PS:'lgdt 0x3c'很**可疑。 – Jester

+0

嗯,我讀到'cs'必須包含代碼段相對於GDT的偏移量,但'cs'也構成了指向下一條指令的指針,即'cs:ip' - 因此我最終跳到' 0x2c:reload_cs',它不是'reload_cs'的位置。到目前爲止,我能理解的唯一解決方案是設計GDT中代碼段的位置以保持'cs'不變,這很荒謬。我覺得我錯過了關於彙編程序的一些基本知識。 –

+2

'cs:ip'在保護模式下使用GDT條目中的基地址。您只需確保在指令中添加了偏移量的基地址將您帶到正確的位置。實現這一點最簡單的方法是在實模式中使用'org 0',並在描述符條目中使基址爲零。 – Jester

回答

5

您似乎對GDT的工作原理有一些誤解。

  • LGDT指令不會將GDTR加載到選擇器中。它的操作數是內存中的一個位置,它包含一個包含16位限制和一個32位線性基地址的結構。在進入保護模式之前,它通常在實模式下執行。

  • GDT只能在保護模式下工作。要使用它,必須通過將CR0中的PE位置1來從實模式切換到保護模式。

  • GDT是內存中的一個表,包含8個字節長的段描述符。如上所述,GDT在存儲器中的位置和限制由通過LGDT指令加載到GDTR中的基數和限制來確定。每個描述符包含各種類型和許可位,對於基本描述符類型,還包含段的基線的線性地址以及段的限制。

  • 保護模式尋址通過獲取相關段寄存器中包含的選擇器值並將其用作GDT或LDT中的索引來工作。索引的段描述符提供了正在尋址的段的基地址。該基地被添加到相關的偏移量以確定被引用的線性地址。您的遠程跳轉指令(ljmp $0x2c, $reload_cs)分別將值0x2creload_cs裝入CS和EIP。下一條要執行的指令是根據0x2c所提及的段描述符中的基數確定的,並將reload_cs的值加上它。

  • 段選擇器0x2c不是GDT的索引,它是LDT的索引。選擇器的最不重要的三位是特殊的。位0和位1是請求的特權級別,在這裏應該爲0。位2是表格指示符,如果其值爲0,則選擇器引用GDT,如果它是1,則它使用LDT。其餘的位3-15提供了GDT/LDT的索引。

  • 符號reload_cs的值由彙編器和/或鏈接器確定。你需要確保它的價值是正確的。由於您將它用作保護模式代碼段的偏移量,這意味着該段的偏移量必須位於reload_cs:後面的指令實際位於內存中的位置。彙編器和鏈接器不知道你的代碼加載到內存中的位置,他們不知道你是如何設置你的代碼段的。

由於您使用GNU彙編器想必GNU連接器,以確保reload_cs最簡單的方法具有正確的值是使用受保護模式的代碼段爲0的基礎上,告訴彙編器把一切都成.text部分,然後告訴鏈接器將.text部分定位到將其加載到內存中的實際線性地址。這樣0 + reload_cs將等於在reload_cs標籤後的指令的存儲器中的實際線性地址。