1

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

 __asm__ ("movl %eax, %db0");

我收到的错误消息表明我在做一些根本不正确的事情。例如:

Error: unsupported for `mov'

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

4

2 回答 2

5

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

__asm__ ("movl %%eax, %%db0\n\t");

这将使它编译。

更正:
这里有多个问题:

  1. __asm__没有 clobbers 的语句是非常不寻常的(并且很少做你所期望的,因为在汇编中可以做的事情很少没有副作用)。尽管如此,当且仅当指令实际上既没有副作用又没有输入时,看起来可以省略clobber列表。
    其结果之一是%不再需要转义。这个简单示例的编译器为__asm__("movl %eax, %db0\n\t");完全指定的__asm__("movl %%eax, %%db0\n\t":::);. 不过,这不一定是优势,因为...
  2. 第二个问题是便携性;x86_64 和 i386 相似但不相同,其中一个区别是调试 reg 的寄存器宽度。仅这一点就是实际使用内联汇编操作数而不是直接使用寄存器名称的一个特别好的理由,因为以下代码:
    __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__与“正常”此类语句区别对待。

在任何情况下,实际上您在修改调试寄存器肯定需要一个参数/clobber 列表,因为对这些的访问是序列化指令,编译器应该隐式地知道这些指令是memoryclobber。此外,您从它们那里读取/写入它们的值必须来自/来自某个地方……因此是输入/输出。

于 2012-08-03T20:54:45.250 回答
1

FrankH 的答案在 x64 模式下不起作用。可能使用的是:

inline void setDebugReg( long v )
{
   __asm__ __volatile__ ("mov %0, %%db0\n\t" : : "r"(v) : ) ;
}

(对于 db7,您需要一个类似的 asm 来设置观察点的标志)。

但是设置这些寄存器不能在用户空间完成,所以需要其他方法。这是使用 ptrace 执行此操作的示例代码,其中一个进程被派生到 ptrace attach、更改寄存器、ptrace detach 然后退出

于 2014-01-28T21:12:13.070 回答