1

I have this code

int64_t ret;
char *fmt = "\n\n\n%s\n\n\n";
char *s;

s = kmalloc(13, GFP_KERNEL);
memcpy(s, "Hello world!\0", 13);

__asm__ __volatile__ (
    "movq %2, %%rdi;"
    "movq %1, %%rsi;"
    "movq $2, %%rax;"

    "call printk;"
    "movq %%rax, %0;"

    : "=r" (ret)
    : "r" (s), "r" (fmt)
    :
);

__asm__ __volatile__ (
    "movq %0, %%rdi;"
    "movq $1, %%rax;"
    "call kfree;"

    :
    : "r" (s)
    :
);

return ret;

which crashes on the kfree call. It must be something silly, but I can't find out what it is. What is the problem?

EDIT: "Crashes" == The kernel module triggers an Oops.

EDIT: It seems to work just fine if I use m instead of r. But why?

alexandernst
  • 14,352
  • 22
  • 97
  • 197
  • It seems likely that those registers do not hold the values you think they hold. Is there a reason you have to use inlined assembly here rather than just writing your code in C? – Eric Melski Dec 01 '13 at 21:28
  • @EricMelski I'm going to write some pure asm so I'm testing with this instead. According to docs, RDI holds the 1st argument and RAX holds the number of arguments that the call accepts. – alexandernst Dec 01 '13 at 21:50
  • Why don't you start from testing the same in userspace using a step by step debugging? – oakad Dec 02 '13 at 03:01
  • @user16653 I could, but userspace and kernelspace are really different (even the registers used for args doesn't match), and I'm not sure if that will confuse me even more. Anyways, what would I'd be looking for? – alexandernst Dec 02 '13 at 08:54
  • Different in what respect? Never noticed any difference, at least when amd64 is concerned. :) – oakad Dec 03 '13 at 00:31
  • @alexandernst Mind you, you're using intra-kernel ABI (which is just a normal C abi) not a system call/vdso ABI (which is indeed different, as could be expected). – oakad Dec 03 '13 at 00:33
  • @user16653 Ah, indeed, you're right. I mixed syscalls with calls. – alexandernst Dec 03 '13 at 10:35

1 Answers1

3

You should look at the generated assembly code (use -S option to gcc) to see exactly what is happening.

I am guessing that the problem is that you didn't specify the correct clobbers for the printk block. Remember that certain registers may be freely used by a called function. The compiler doesn't process the contents of the inline asm block, so it doesn't know it has a call inside. It expects that registers not specified as output or clobber will come out unchanged from the asm block. Thus, I assume the compiler caches the variable s in a register that is destroyed by the printk or your use of rax, rdi and rsi which you also don't tell the compiler. Note that there are specific register constraints, so you could use those instead of moving the arguments around in assembly.

Also note that rax (actually al) is only used for varargs and stdargs functions, and even then it should just contain the number of arguments passed via sse vector (xmm) registers, not the total number. See the x86-64 ABI docs for more information.

Finally, if you really want to write asm code, you should consider writing standalone asm module instead of C if possible. You could save yourself some headache. See this question for an example.

Community
  • 1
  • 1
Jester
  • 56,577
  • 4
  • 81
  • 125