我一直在尝试使用英特尔调试寄存器,但我似乎做错了什么。我创建了一个非常简单的 Linux LKM,并尝试使用内联汇编来执行寄存器的简单操作。例如:
__asm__ ("movl %eax, %db0");
我收到的错误消息表明我在做一些根本不正确的事情。例如:
Error: unsupported for `mov'
有没有人对如何使用这些寄存器有任何见解?
我一直在尝试使用英特尔调试寄存器,但我似乎做错了什么。我创建了一个非常简单的 Linux LKM,并尝试使用内联汇编来执行寄存器的简单操作。例如:
__asm__ ("movl %eax, %db0");
我收到的错误消息表明我在做一些根本不正确的事情。例如:
Error: unsupported for `mov'
有没有人对如何使用这些寄存器有任何见解?
这是一个语法错误 - gcc 的内联汇编器%
用作操作数标识符,并且要在 x86 的寄存器名称中显式使用它,您必须编写:
__asm__ ("movl %%eax, %%db0\n\t");
这将使它编译。
更正:
这里有多个问题:
__asm__
没有 clobbers 的语句是非常不寻常的(并且很少做你所期望的,因为在汇编中可以做的事情很少没有副作用)。尽管如此,当且仅当指令实际上既没有副作用又没有输入时,看起来可以省略clobber列表。%
不再需要转义。这个简单示例的编译器为__asm__("movl %eax, %db0\n\t");
完全指定的__asm__("movl %%eax, %%db0\n\t":::);
. 不过,这不一定是优势,因为...__asm__("mov %0, %%db0\n\t" : : "a"((uintptr_t)0) : );
gcc -m64 ...
) 和 32 位 ( )上编译gcc -m32 ...
- 它创建相同的指令,但在汇编中编写时使用的寄存器对于 64 位是不同的:208: 0f 23 c0 mov %rax,%db0
269: 0f 23 c0 mov %eax,%db0
uintptr_t
来自<inttypes.h>
并且保证始终是完整的通用寄存器宽度),因此相同的内联汇编可用于 32 位和 64 位编译。我承认我不知道编译器将 clobber-list-less__asm__
与“正常”此类语句区别对待。
在任何情况下,实际上您在修改调试寄存器时肯定需要一个参数/clobber 列表,因为对这些的访问是序列化指令,编译器应该隐式地知道这些指令是memory
clobber。此外,您从它们那里读取/写入它们的值必须来自/来自某个地方……因此是输入/输出。
FrankH 的答案在 x64 模式下不起作用。可能使用的是:
inline void setDebugReg( long v )
{
__asm__ __volatile__ ("mov %0, %%db0\n\t" : : "r"(v) : ) ;
}
(对于 db7,您需要一个类似的 asm 来设置观察点的标志)。
但是设置这些寄存器不能在用户空间完成,所以需要其他方法。这是使用 ptrace 执行此操作的示例代码,其中一个进程被派生到 ptrace attach、更改寄存器、ptrace detach 然后退出。