2012-04-25 14 views
0

gccのインラインアセンブリを使用して、レジスタの内容、特にgdtrをC変数に読み込もうとしています。私はhereのようにいくつかのコードを変更していますが、コードは32ビットプロセッサ用に書かれています。したがって、命令を64ビットに適応させる際に、私は誰かが私に説明できると思っていたいくつかの奇妙な動作に遭遇しています。レジスタの内容をC変数に読み込むときに奇妙な動作が発生する

まず、gdtrレジスタの構造をモデル化すると考えられるgdtr構造体。

struct gdtr64 { 
    uint16_t limit; 
    uint64_t addr; 
}; 

十分に単純です。私が手

struct gdtr64 gdtr64 = {0xcccc,0xa2a2a2a2a2a2a2a2}; 
printf("gdtr64 limit: %x\ngdtr64 addr: %llx\n", gdtr64.limit, gdtr64.addr); 
printf("<--asm call-->\n"); 
__asm__ volatile("sgdt %0\n" : :"m"(gdtr64)); 
printf("gdtr64 limit: %x\ngdtr64 addr: %llx\n", gdtr64.limit, gdtr64.addr); 

を:私は実行を経て、このような構造に出力するレジスタの内容をしようとすると

gdtr64 limit: cccc 
gdtr64 addr: a2a2a2a2a2a2a2a2 
<--asm call--> 
gdtr64 limit: a0 
gdtr64 addr: a2a2a2a2a2a2ffff 

私が変更されているもの伝えることができるので、呼び出し前の値がちょうどジャンク値です。制限がccccから00a0に更新され、gdtr64.addrの最後の2バイトが変更されていることがわかります。これは私には分かりません。

実験として、私は、アセンブリセクションにgdtr64.addrを通過した以外、同じコードを実行:出力は私を驚か

struct gdtr64 gdtr64 = {0xcccc,0xa2a2a2a2a2a2a2a2}; 
printf("gdtr64 limit: %x\ngdtr64 addr: %llx\n", gdtr64.limit, gdtr64.addr); 
printf("<--asm call-->\n"); 
__asm__ volatile("sgdt %0\n" : :"m"(gdtr64.addr)); 
printf("gdtr64 limit: %x\ngdtr64 addr: %llx\n", gdtr64.limit, gdtr64.addr); 

gdtr64 limit: cccc 
gdtr64 addr: a2a2a2a2a2a2a2a2 
<--asm call--> 
gdtr64 limit: cccc 
gdtr64 addr: ff8076db40000097 

この場合、我々は後に書き始めるにはメモリアドレスはgdtr64.limitで占められていますが、書かれている内容は大きく異なります。前の例の制限であった00a0がaddrの最後に移行しました。さもなければ、私達は適切な住所の造りのように見えるものを持っている。

私が使用していたstructに固有のものではないかと疑問に思ったので、それはcharの文字列を試してみることにしました。レジスタは10バイト長である必要があります:

char gdtr_char[10] = "0000000000"; 
printf("GDTR_CHAR: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x \n", 
    (unsigned char) gdtr_char[0], 
    (unsigned char) gdtr_char[1], 
    (unsigned char) gdtr_char[2], 
    (unsigned char) gdtr_char[3], 
    (unsigned char) gdtr_char[4], 
    (unsigned char) gdtr_char[5], 
    (unsigned char) gdtr_char[6], 
    (unsigned char) gdtr_char[7], 
    (unsigned char) gdtr_char[8], 
    (unsigned char) gdtr_char[9] 
); 
printf("<--asm call-->\n"); 
__asm__ volatile("sgdt %0\n" : :"m"(gdtr_char[0])); 
printf("GDTR_CHAR: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x \n", 
    (unsigned char) gdtr_char[0], 
    (unsigned char) gdtr_char[1], 
    (unsigned char) gdtr_char[2], 
    (unsigned char) gdtr_char[3], 
    (unsigned char) gdtr_char[4], 
    (unsigned char) gdtr_char[5], 
    (unsigned char) gdtr_char[6], 
    (unsigned char) gdtr_char[7], 
    (unsigned char) gdtr_char[8], 
    (unsigned char) gdtr_char[9] 
); 

私のCのスキルは開発中です。この結果は次のとおりです。

GDTR_CHAR: 30 30 30 30 30 30 30 30 30 30 
<--asm call--> 
GDTR_CHAR: 97 00 00 50 dd 76 80 ff ff ff 

は再度、初期値はジャンクですが、我々は、レジスタを読んだ後、私たちはすべての10バイトを占めていることを見ることができますが、我々が得たものの逆の順序でしようとすると、私たちの2回目の試験。要約すると、これは別個の「試行」に分割されているにもかかわらず、これらはすべて一括して実行されていました。レジスタの内容は実行(私も奇妙な発見)の間で変更されるようです。すべてのことを言って、私は次の問題の周りに私の頭を包むことはできません:

  • なぜGDTRの内容はそれぞれの実行を変更するのですか?
  • 構造体と文字配列の使用に違いがあるのはなぜですか?
  • GDTのベースメモリアドレスは何ですか(結果は正しいでしょうか?)

ご協力いただければ幸いです。これまで読んでくれてありがとう。

+0

http://forum.osdev.org/viewtopic.php?f=13&t=16600&start=0 – geekosaur

答えて

2

おそらく少なくとも2つの問題があります。

最初の問題は、アライメントのためにパッディングが追加されていることです。したがって、「16ビットリミットと64ビットアドレス」を含む構造体は、おそらく「16ビットの制限で、48ビットのパディング期待していないと64ビットのアドレス "です。ほとんどのコンパイラは、パック構造(例えば、 "#pragma pack"または "__attribute__((packed)))")の拡張子(非標準)を持っています。

第2の問題はエンディアンです。 80x86はリトルエンディアンです。つまり、0x12、0x34、0x45、0x67のバイトは32ビット整数0x67452312を表します。

2回目と3回目の試行では、制限は0x0097で、アドレス部分は0xFFFFFF8076DB4000と仮定します。最初の試行についてはわかりません(GDTRが第1試行と第2試行の間で変更されたように見えます)。

編集:最初の試行の制限の結果は間違って表示されます。制限は「GDT-1のサイズ」であり、GDTエントリはそれぞれ8(または16)バイトであるため、制限は常に最低3ビットが設定されます(たとえば、「0x7」または「0x ??? F "。

+0

ああ、そうです。毎回その値が変わったことに気づく前に最初の試行をしました 私はエンディアンを問題として考えていましたが、実行2と3の間で一貫性がない理由を理解していません。なぜなら、エンディアンが変わるようなものではないからです。 私は 'limit'と' addr'のアドレスを一点でチェックしていましたが、チェックアウトしていたようですが、構造体をパックしてレポートを返します。 – fromClouds

+0

問題は本当にcom piler、 '__attribute __((packed))'を私の構造体に追加することはそのトリックを行いました。別の実験として、プログラムを1000回実行したところ、循環する4つの値しかないことが判明しました。これは、コードが実行されているプロセッサコアに依存する値を取得していたことを示しています。 – fromClouds

関連する問題