使用英特尔调试寄存器的正确方法(GCC /内联汇编)

JOg*_*den 1 x86 gcc x86-64 linux-kernel

我一直试图尝试英特尔调试寄存器,但我似乎做错了什么.我创建了一个非常简单的Linux LKM,并尝试使用内联汇编来执行寄存器的简单操作.例如:

 __asm__ ("movl %eax, %db0");
Run Code Online (Sandbox Code Playgroud)

我得到的错误消息表明我做的事情从根本上是错误的.例如:

Error: unsupported for `mov'
Run Code Online (Sandbox Code Playgroud)

有没有人对如何使用这些寄存器有任何见解?

Fra*_*kH. 5

这是一个语法错误 - gcc的内联汇编程序%用作操作数标识符,并且要在x86的寄存器名称中明确使用它,您必须编写:

__asm__ ("movl %%eax, %%db0\n\t");
Run Code Online (Sandbox Code Playgroud)

这将使它编译.

更正:
这里有多个问题:

  1. __asm__没有clobbers的陈述是非常不寻常的(并且很少做你期望的事情,因为在装配中可以做很少的事情没有副作用).尽管如此,当且仅当指令实际上既没有副作用且没有输入时,它看起来可以省略clobber列表.
    其结果之一%是不再需要逃逸; 这个简单示例的编译器创建了与__asm__("movl %eax, %db0\n\t");完全指定的操作码相同的操作码__asm__("movl %%eax, %%db0\n\t":::);.这不一定是优势,因为......
  2. 第二个问题是便携性; x86_64和i386相似但不相同,其中一个区别是调试寄存器的寄存器宽度.仅这一点是实际使用内联汇编操作数而不是直接使用寄存器名称的一个特别好的理由,因为以下代码:
    __asm__("mov %0, %%db0\n\t" : : "a"((uintptr_t)0) : );
    在64位(gcc -m64 ...)和32位(gcc -m32 ...)上编译- 它创建相同的指令,但在汇编时使用的寄存器对于64位是不同的:
    208: 0f 23 c0 mov %rax,%db0
    在32位中,它确实:
    269: 0f 23 c0 mov %eax,%db0
    这里使用输入操作数提供了抽象寄存器宽度的能力(uintptr_t来自<inttypes.h>并保证始终是完整的通用寄存器宽度),因此相同的内联汇编可用于32位和64位编译.

我承认我不知道编译器将clobber-list-less __asm__与"普通"这样的语句区别对待.

在任何情况下,在现实中,你肯定修改调试寄存器时,需要一个参数/撞列表,因为访问这些序列化应该被隐式地知道编译器的指令memory撞.此外,你从他们那里读取/写给他们的价值必须来自/来自某个地方......因此输入/输出.

  • 这仅在程序集有操作数时才是正确的。没有它们,只有一个 `%` 用于寄存器。 (2认同)