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

使用英特尔调试寄存器(GCC/内联程序集)的正确方法,gcc,linux-kernel,x86,x86-64,Gcc,Linux Kernel,X86,X86 64,我一直在尝试英特尔调试寄存器,但我似乎做了一些不正确的事情。我创建了一个非常简单的LinuxLKM,并尝试使用内联汇编来执行寄存器的简单操作。e、 g: __asm__ ("movl %eax, %db0"); 我收到的错误消息表明我做了一些根本不正确的事情。e、 g: Error: unsupported for `mov' 有人知道如何使用这些寄存器吗 这是一个语法错误-gcc的内联汇编程序使用%作为操作数标识符,要在x86的寄存器名中显式使用它,您必须编写: __asm__ ("mo

我一直在尝试英特尔调试寄存器,但我似乎做了一些不正确的事情。我创建了一个非常简单的LinuxLKM,并尝试使用内联汇编来执行寄存器的简单操作。e、 g:

 __asm__ ("movl %eax, %db0");
我收到的错误消息表明我做了一些根本不正确的事情。e、 g:

Error: unsupported for `mov'

有人知道如何使用这些寄存器吗

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

__asm__ ("movl %%eax, %%db0\n\t");
那就可以编译了

更正:
这里有多个问题:

  • \uuuu asm\uuuuu
    语句中没有缓冲区是非常不寻常的(而且很少做您期望的事情,因为在汇编中很少有没有副作用的事情可以做)。尽管如此,如果且仅当指令实际上没有副作用且没有输入时,看起来有可能省略碰撞器列表。
    这样做的一个后果是不再需要转义
    %
    ;这个简单示例中的编译器为
    \uuu asm\uuuu(“movl%eax,%db0\n\t”)创建相同的操作码与完全指定的
    \uuuu asm\uuu(“movl%%eax,%%db0\n\t”:。这不一定是一个优势,因为
  • 第二个问题是可移植性;x86_64和i386类似但不完全相同,其中一个区别是调试寄存器的寄存器宽度。这本身就是实际使用内联汇编操作数而不是直接使用寄存器名的一个特别好的理由,因为下面的代码:
    (mov%0,%%db0\n\t):“a”((uintpttr)0):)
    在64位(
    gcc-m64…
    )和32位(
    gcc-m32…
    )上编译-它创建相同的指令,但在汇编中写入此指令时使用的寄存器对于64位是不同的:
    208:0f 23 c0 mov%rax,%db0

    在32位时,它会:
    269:0f 23 c0 mov%eax,%db0

    此处使用输入操作数提供了提取寄存器宽度的能力(
    uintpttr\u t
    来自
    ,并保证始终为完整的通用寄存器宽度),因此相同的内联程序集可用于32位和64位编译
  • 我承认我不知道编译器对待clobber list less
    \uuuu asm\uuuu
    的方式与“普通”这样的语句不同


    在任何情况下,在修改调试寄存器时,实际上您肯定需要一个参数/clobber列表,因为对这些寄存器的访问是串行化指令,编译器应该将这些指令隐式地称为
    内存
    clobber。此外,你从他们那里读到的/写给他们的价值一定来自于某个地方。。。因此输入/输出。

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

    __asm__ ("movl %%eax, %%db0\n\t");
    
    那就可以编译了

    更正:
    这里有多个问题:

  • \uuuu asm\uuuuu
    语句中没有缓冲区是非常不寻常的(而且很少做您期望的事情,因为在汇编中很少有没有副作用的事情可以做)。尽管如此,如果且仅当指令实际上没有副作用且没有输入时,看起来有可能省略碰撞器列表。
    这样做的一个后果是不再需要转义
    %
    ;这个简单示例中的编译器为
    \uuu asm\uuuu(“movl%eax,%db0\n\t”)创建相同的操作码与完全指定的
    \uuuu asm\uuu(“movl%%eax,%%db0\n\t”:。这不一定是一个优势,因为
  • 第二个问题是可移植性;x86_64和i386类似但不完全相同,其中一个区别是调试寄存器的寄存器宽度。这本身就是实际使用内联汇编操作数而不是直接使用寄存器名的一个特别好的理由,因为下面的代码:
    (mov%0,%%db0\n\t):“a”((uintpttr)0):)
    在64位(
    gcc-m64…
    )和32位(
    gcc-m32…
    )上编译-它创建相同的指令,但在汇编中写入此指令时使用的寄存器对于64位是不同的:
    208:0f 23 c0 mov%rax,%db0

    在32位时,它会:
    269:0f 23 c0 mov%eax,%db0

    此处使用输入操作数提供了提取寄存器宽度的能力(
    uintpttr\u t
    来自
    ,并保证始终为完整的通用寄存器宽度),因此相同的内联程序集可用于32位和64位编译
  • 我承认我不知道编译器对待clobber list less
    \uuuu asm\uuuu
    的方式与“普通”这样的语句不同


    在任何情况下,在修改调试寄存器时,实际上您肯定需要一个参数/clobber列表,因为对这些寄存器的访问是串行化指令,编译器应该将这些指令隐式地称为
    内存
    clobber。此外,你从他们那里读到的/写给他们的价值一定来自于某个地方。。。因此输入/输出。

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

    inline void setDebugReg( long v )
    {
       __asm__ __volatile__ ("mov %0, %%db0\n\t" : : "r"(v) : ) ;
    }
    
    (db7需要一个类似的asm来设置监视点的标志)


    但是,不能在用户空间中设置这些寄存器,因此需要其他方法。下面是使用ptrace执行此操作的示例代码,其中。

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

    inline void setDebugReg( long v )
    {
       __asm__ __volatile__ ("mov %0, %%db0\n\t" : : "r"(v) : ) ;
    }
    
    (db7需要一个类似的asm来设置监视点的标志)

    然而,设置这些寄存器不能在用户空间中完成,因此需要其他方法