I have been trying to experiment with the Intel Debug Registers however I seem to be doing something incorrectly. I have created a very simple Linux LKM and tried to use inline assembly to perform trivial manipulation of the registers. e.g:
__asm__ ("movl %eax, %db0");
The error messages I get suggest I’m doing something fundamentally incorrect. e.g:
Error: unsupported for `mov'
Does anyone have any insight into how to work with these registers?
That’s a syntax error – gcc’s inline assembler uses
%as operand identifier, and to explicitly use it in register names for x86 you’ll have to write:That’ll make it compile.
Correction:
There’s multiple issues here:
__asm__statements without clobbers are highly unusual (and rarely do what you’re expecting because there’s very few things one can do in assembly that have no side effects). Nonetheless, if and only if the instruction is actually both side-effect-free and has no inputs, it looks to be possible to omit the clobber list.One consequence of that is that escaping
%is no longer necessary; the compiler for this simple example creates the same opcode for__asm__("movl %eax, %db0\n\t");as it does for the fully-specified__asm__("movl %%eax, %%db0\n\t":::);. This is not necessarily an advantage though, because …__asm__("mov %0, %%db0\n\t" : : "a"((uintptr_t)0) : );compiles both on 64bit (
gcc -m64 ...) and 32bit (gcc -m32 ...) – it creates the same instruction, but the registers used when writing this in assembly are different for 64bit:208: 0f 23 c0 mov %rax,%db0while in 32bit, it does:
269: 0f 23 c0 mov %eax,%db0The use of an input operand here provides the ability to abstract the register width away (
uintptr_tcomes from<inttypes.h>and is guaranteed to always be full general-purpose register width), so the same inline assembly can be used for 32bit and 64bit compiles.I admit I had no idea the compiler treats clobber-list-less
__asm__differently from “normal” such statements.In any case, in reality you will definitely need an argument/clobber list when modifying debug registers, because access to these are serializing instructions which should be implicitly made known to the compiler as
memoryclobber. Besides, the value you’re reading from them / writing to them must go to / come from somewhere … hence input/output.