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

`

4

3 に答える 3

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
   }
}

私が覚えている限り、2つの問題があります。

  • 32ビットのMASMインラインアセンブリはfar命令をコンパイルできないため、オペコードを発行する必要があります。
  • jmp命令は正しいことを行わないので、代わりに命令を使用してコードretの次の行にジャンプする必要があります。

また、インラインアセンブリからret命令を呼び出さないでください。呼び出さないと、コンパイラがスタックをクリーンアップするために関数の最後に配置するエピローグコードをスキップします。


以下の私の最初の答え:

GDT記述子(gp)が正しく初期化されていない可能性があります。

ジャンプ命令が実行されると、プロセッサはプロテクトモードに切り替えようとし、GDTが必要になります。GDTが正しく設定されていないと、クラッシュしました。

gpの最初の16ビットはgdtのサイズ(ここでは3 * 8 = 24バイト)であり、次の32バイトはgdtのアドレス(ここでは&gdt [0])です。

また、lgdtを呼び出す前にdsレジスタがnullであることを確認してください。このレジスタは命令によって使用されます。

于 2009-09-09T07:44:14.110 に答える
0

スタックをその下からすぐに移動しました - 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 を使用した関数の呼び出しに続く命令に戻す必要がありますが、主はその後何が起こるかを知っています。

于 2009-09-09T07:52:02.873 に答える
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;
};

これはプロセッサの観点からは無効です。

于 2009-09-09T11:41:08.190 に答える