我正在尝试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
调用之前的值只是垃圾值,所以我可以知道发生了什么变化。我们可以看到 limit 从 更新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
我使用的 s 所固有的,所以我决定尝试一串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 个字节,但与我们尝试第二次试验时得到的顺序相反。总结一下:
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 的内存基址是什么(即哪个结果是正确的[如果有的话])?
任何帮助将不胜感激。感谢您阅读本文。