2009-09-09 31 views
0

爲什麼在Masm到達jmp時失敗?內聯程序集跳轉錯誤

struct gdt_entry 
{ 
    unsigned short limit_low; 
    unsigned short base_low; 
    unsigned char base_middle; 
    unsigned char access; 
    unsigned char granularity; 
    unsigned char base_high; 
}; 

struct gdt_ptr 
{ 
    unsigned short limit; 
    unsigned int base; 
}; 

struct gdt_entry gdt[3]; 
struct gdt_ptr gp; 


void gdt_flush() 
{ 
     __asm{ 
      lgdt [gp] 

      mov ax, 0x10 
      mov ds, ax 
      mov es, ax 
      mov fs, ax 
      mov gs, ax 
      mov ss, ax 

      ; push the address on the stack 
      push 0x08 
      mov eax, offset flush2 
      push eax 

      ; ret use the previous pushed address 
      _emit 0xCB ; far return 

     flush2: 
      ;ret 
    } 
} 


void gdt_set_gate(int num, unsigned long base, unsigned long limit, unsigned char access, unsigned char gran) 
{ 

    gdt[num].base_low = (base & 0xFFFF); 
    gdt[num].base_middle = (base >> 16) & 0xFF; 
    gdt[num].base_high = (base >> 24) & 0xFF; 
    gdt[num].limit_low = (limit & 0xFFFF); 
    gdt[num].granularity = ((limit >> 16) & 0x0F); 
    gdt[num].granularity |= (gran & 0xF0); 
    gdt[num].access = access; 
} 

void gdt_install() 
{ 
    gp.limit = (sizeof(struct gdt_entry) * 3) - 1; 
    gp.base = (int)&gdt; 
    gdt_set_gate(0, 0, 0, 0, 0); 
    gdt_set_gate(1, 0, 0xFFFFFFFF, 0x9A, 0xCF); 
    gdt_set_gate(2, 0, 0xFFFFFFFF, 0x92, 0xCF); 
    gdt_flush(); 
} 

`

+0

奇怪..這應該可能工作。如果你找到了,請在這裏發佈。我也有點感興趣。 :-) – 2009-09-09 07:32:02

+0

你如何初始化GDT描述符? – cedrou 2009-09-09 07:37:33

+0

我編輯了這個問題,所以你可以看到代碼現在看起來如何...雖然仍然失敗... – Fredrick 2009-09-09 10:21:45

回答

0

新的答案:

我已經遇到這個問題,前一段時間,我發現與MASM內聯彙編更新GDT的唯一方法是使用一個遠程返回指令,而不是遠程跳轉指令。

struct gdt_entry gdt[3]; 
struct gdt_ptr gp; 
void gdt_flush(){ 
    __asm{ 
      lgdt [gp] 

      mov ax, 0x10 
      mov ds, ax 
      mov es, ax 
      mov fs, ax 
      mov gs, ax 
      mov ss, ax 

      ; push the address on the stack 
      push 0x08 
      mov eax, offset flush2 
      push eax 

      ; ret use the previous pushed address 
      _emit 0xCB ; far return 

     flush2: 
      ;ret 
    } 
} 

至於我記得,有兩個問題:

  • 32位MASM聯彙編不能編譯遠的指示,所以你必須發出操作碼。
  • jmp指令沒有做正確的事情,你應該使用ret指令跳轉到下一行代碼。

此外,不要從內聯程序集中調用ret指令,否則會跳過編譯器放在函數結尾清理堆棧的epilog代碼。


我的第一個答案如下:

也許你的GDT描述符(GP)被嚴重初始化。

當您執行跳轉指令時,處理器嘗試切換到保護模式,然後需要GDT。如果GDT設置不正確,則會崩潰。

gp的前16位是gdt的大小(這裏是3 * 8 = 24個字節),後面的32個字節是gdt的地址(這裏是& gdt [0])。

此外,請在調用lgdt之前確保ds寄存器爲空:該寄存器被指令使用。

+0

編譯時我沒有得到任何錯誤,但當調用gdt_install()時崩潰 – Fredrick 2009-09-09 10:19:08

1

你轉移了堆棧右出其下 - 由RET使用的IP正指向的地方真的太瘋狂了

[編輯]

你還是揍棧 - 同樣一個由VC使用。 VC將更多東西推入堆棧,而不僅僅是返回IP。做一個你會看到的來源&的彙編列表。

一種可能性是在進行更改之前將返回地址從堆棧中複製出來,最後只是跳到它指向的地方。

創建一個標記DW來保存地址:

_asm { 
    oldip dd ?  ;this is in cs 
    pop eax   ;eip into eax 
    push eax  ;leave stack as found 
    mov oldip,eax  
    . 
    ..your stuff 
    . 
    jmp far cs:[oldip]  
} 

我可能失去了一些東西,但你的代碼看起來你重挫所有段值,除了CS,從而破壞所有訪問以前聲明的變量無處不在,以及任何返回地址等由您的程序放置在堆棧上...也許這就是你想要做的,跳到其他地方編碼,孤立你的當前程序...

上面的片段應該把你回到用_asm函數調用函數之後的指令,但是閣下知道那麼會發生什麼。

+0

我在哪裏放置上面的代碼? oldip ... – Fredrick 2009-09-09 11:00:31

+0

遠程返回指令替代遠程跳轉,而不是函數返回語句。遠端返回指令使用2個壓入值,並在之後恢復堆棧。函數的返回留給編譯器。 – cedrou 2009-09-09 11:15:05

0

嘗試把下面的編譯之前和之後的結構定義:

#pragma pack(push,1) 

struct gdt_entry 
{ 
    unsigned short limit_low; 
    unsigned short base_low; 
    unsigned char base_middle; 
    unsigned char access; 
    unsigned char granularity; 
    unsigned char base_high; 
}; 

struct gdt_ptr 
{ 
    unsigned short limit; 
    unsigned int base; 
}; 

#pragma pack(pop) 

雖然對gdt_entry沒有效果,這些指令改變gdt_ptr結構的內存佈局。編譯器的默認行爲是在32位上對齊結構元素。因此,以前的定義將相當於:

struct gdt_ptr 
{ 
    unsigned short limit; 
    unsigned short unused; 
    unsigned int base; 
}; 

從處理器的角度來看,這是無效的。

+0

沒有效果...仍然崩潰...或者我應該在我打電話給gdt之前激活一些東西?我在VirtualBox上測試 – Fredrick 2009-09-09 11:51:24

+0

我假設你正在編寫一個內核並在VirtualBox上測試它,我錯了嗎?您正在使用哪種引導程序,以及您的代碼何時運行? – cedrou 2009-09-09 12:07:29

+0

我正在使用isolinux.bin和mkisofs工具來啓動我的內核,實際上我的代碼在我使用Nasm(在Ubuntu上)時正在運行,現在我想將它轉換爲Masm,但卻被IDT,GDT,ISR卡住了。 ..嗯,你是什麼意思跑?截止日期? – Fredrick 2009-09-09 12:17:47