2009-09-09 29 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