内联汇编跳转错误

一旦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(); } 

`

你把堆栈从它下面移出 – 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东西调用函数之后的指令,但是lord知道接下来会发生什么。

新答案:

我前段时间已经遇到过这个问题,我发现使用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寄存器为空:指令使用该寄存器。

尝试在结构定义之前和之后放置以下pragma:

 #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_entry这些指令会更改gdt_ptr结构的内存布局。 编译器的默认行为是将结构元素对齐到32位。 因此,先前的定义将等同于:

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

从处理器的角度来看这是无效的。