gdtr
レジスタの内容を、具体的には gcc インライン アセンブリを使用して C 変数に読み込もうとしています。そのために、ここで見つけたいくつかのコードを適応させていますが、コードは 32 ビット プロセッサ用に書かれています。そのため、命令を 64 ビットに適合させる際に、誰かが説明してくれることを期待していた奇妙な動作に遭遇しました。
まず、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
呼び出し前の値はただのジャンク値なので、何が変更されたかがわかります。制限が から に更新され、 の最後の 2 バイトが変更されていることがcccc
わかり00a0
ますgdtr64.addr
。これは私にはあまり意味がありません。
実験として、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
s を試すことにしました。レジスタの長さは 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 回目の試行で得られたものとは逆の順序になっています。要約する:
trial 1 limit: 00a0
trial 1 addr: ************ffff
-------------------------------
trial 2 limit: ****
trial 2 addr: ff8076db40000097
-------------------------------
trial 3 array: 97 00 00 40 db 76 80 ff ff ff
reversed: ff ff ff 80 76 db 40 00 00 97 //byte-wise
ちなみに、これは別々の「トライアル」に分割されていましたが、これらは一度に実行されました. レジスタの内容は、実行ごとに変化するようです (これも奇妙だと思います)。そうは言っても、次の問題に頭を悩ませることはできません。
- GDTR の内容が実行ごとに変わるのはなぜですか?
- struct と char 配列の使用に違いがあるのはなぜですか?
- GDT のベース メモリ アドレスは何ですか (つまり、どの結果が正しいか [ある場合])?
どんな助けでも大歓迎です。ここまで読んでくれてありがとう。