简体   繁体   English

内联汇编跳转错误

[英]Inline Assembly Jump Error

Why does this fail, once Masm reaches jmp? 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();
}

` `

New answer: 新答案:

I've already encounter this problem some time ago and the only way I found to update the GDT with MASM inline assembly is to use a far return instruction, instead of the far jump instruction. 我前段时间已经遇到了这个问题,我发现使用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
   }
}

As far as I remember, there are two problems: 据我所记得,有两个问题:

  • the 32 bits MASM inline assembly cannot compile far instructions, so you have to emit the opcode. 32位MASM内联汇编不能编译远指令,因此您必须发出操作码。
  • the jmp instruction does not do the right stuff and you should use instead the ret instruction to jump to the next line of code. jmp指令执行的操作不正确,您应该使用ret指令跳转到下一行代码。

Also, don't call the ret instruction from the inline assembly, otherwise you'll skip the epilog code that the compiler puts at the end of the function to clean the stack. 另外,不要从内联程序集中调用ret指令,否则您将跳过编译器在函数末尾放置的Epilog代码以清理堆栈。


My first answer below: 我的第一个答案如下:

Maybe your GDT descriptor (gp) is badly initialized. 也许您的GDT描述符(gp)初始化错误。

When you the jump instruction is executed, the processor try to switch to protected mode and the GDT is then required. 当执行跳转指令时,处理器将尝试切换到保护模式,然后需要GDT。 If the GDT is not set correctly, it crashed. 如果GDT设置不正确,则会崩溃。

The first 16 bits of gp are the size of the gdt (here 3*8 = 24 bytes) and the following 32 bytes are the address of the gdt (here &gdt[0]). gp的前16位是gdt的大小(此处3 * 8 = 24字节),其后的32个字节是gdt的地址(此处&gdt [0])。

Also, make sure the ds register is null before calling lgdt: this register is used by the instruction. 另外,在调用lgdt之前,请确保ds寄存器为空:指令使用该寄存器。

You shifted the stack right out from under it - the ip used by ret is now pointing somewhere really wild 您将堆栈从堆栈中移出-ret使用的ip现在指向某个真正荒谬的地方

[edit] [编辑]

You still clobber the stack - the same one used by VC. 您仍然会破坏堆栈-与VC使用的堆栈相同。 VC pushes more stuff onto the stack than just the return IP. VC不仅将返回IP推入堆栈,还将更多内容推入堆栈。 Do a assembler-listing of the source & you'll see. 对源进行汇编列表,您将看到。

A possibility is to copy the return-address off the stack before you make the changes, and at end to just jump to where it points. 一种可能是在进行更改之前将返回地址从堆栈中复制出来,最后只跳转到其指向的位置。

create a labeled dw to store the address: 创建一个标记的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]     
}

I may be missing something here, but by the looks of your code you are clobbering all the segment values except cs, thereby destroying all access to previously declared variables everywhere, as well as any return address etc placed on the stack by your program ... maybe that's what you want to do, jumping off to code somewhere else, orphaning your current program ... 我在这里可能会遗漏一些东西,但是从您的代码的外观来看,您正在破坏除cs之外的所有段值,从而破坏了对任何地方先前声明的变量的所有访问,以及程序在堆栈中放置的任何返回地址等。也许这就是您想要做的,跳到其他地方编写代码,孤立您当前的程序...

The above fragment should put you back at the instruction following the call to the function with the _asm stuff, but lord knows what's going to happen then. 上面的片段应该使您回到使用_asm的函数调用之后的指令,但是主知道接下来会发生什么。

Try to put the following pragma before and after the structure definitions: 尝试在结构定义的前后放置以下杂注:

#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)

Although it has no effect on gdt_entry , these instructions change the memory layout of the gdt_ptr structure. 尽管它对gdt_entry没有影响, gdt_entry这些指令会更改gdt_ptr结构的内存布局。 The default behavior of the compiler is to align structures elements on 32 bits. 编译器的默认行为是在32位上对齐结构元素。 Hence, the previous definition would be equivalent to : 因此,先前的定义将等效于:

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

which is invalid from the processor point of view. 从处理器的角度来看这是无效的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM