如何编写一个可从汇编代码调用的C函数

3

我需要编写一个C函数,该函数将从Linux内核的汇编代码中调用。

应该考虑哪些特殊问题?

我已经有一些想法了,但是有没有人可以提供更多细节:

(1) 调用约定

确保汇编语言中的调用方和C语言中的被调用方能够良好通信。但是我应该使用哪种调用约定?如何声明C函数并在汇编代码中声明它?

(2) 保护寄存器

在调用之前,应该在汇编中保存一些寄存器。我大致记得它们是eax、ecx和edx。但是除非需要在C函数调用之前引用旧值,否则不必保存它们,对吗?

还有其他问题吗?


1
我需要编写一个C函数,该函数将从Linux内核中的汇编代码调用。 - 有什么问题吗? - Mitch Wheat
@MitchWheat 具体来说,我不知道在声明 C 函数时使用 "asmlinkage" 的确切用法。我必须使用它吗?还有其他选择吗?这会有什么影响? - Infinite
1
首先,尽量不要这样做。Linux 在一些地方使用汇编语言,而且它们都很棘手。我认为问题取决于是什么汇编代码。在中断处理程序的非常早期阶段运行,或者在早期引导阶段运行(C 用于两个示例)是两件不同的事情。 - ugoren
asmlinkage 意味着所有参数都通过堆栈传递。如果我没记错,asmlinkage 在 64 位代码中不起作用。 - Ilya Matveychikov
制作一个包含函数的简单库。创建一个调用该库的 hello 程序。大多数玩具编译器都配备了测试库和 hello 程序,使用像 gdb 这样的调试器对它们进行反汇编,并注意参数是如何传递的。 - hellork
显示剩余2条评论
1个回答

2

如果您的问题标记为 linux-kernel,请查看以下内核头文件:arch/x86/include/asm/calling.h

   3 x86 function call convention, 64-bit:
   4 -------------------------------------
   5  arguments           |  callee-saved      | extra caller-saved | return
   6 [callee-clobbered]   |                    | [callee-clobbered] |
   7 ---------------------------------------------------------------------------
   8 rdi rsi rdx rcx r8-9 | rbx rbp [*] r12-15 | r10-11             | rax, rdx [**]
   9
  10 ( rsp is obviously invariant across normal function calls. (gcc can 'merge'
  11   functions when it sees tail-call optimization possibilities) rflags is
  12   clobbered. Leftover arguments are passed over the stack frame.)
  13
  14 [*]  In the frame-pointers case rbp is fixed to the stack frame.
  15
  16 [**] for struct return values wider than 64 bits the return convention is a
  17      bit more complex: up to 128 bits width we return small structures
  18      straight in rax, rdx. For structures larger than that (3 words or
  19      larger) the caller puts a pointer to an on-stack return struct
  20      [allocated in the caller's stack frame] into the first argument - i.e.
  21      into rdi. All other arguments shift up by one in this case.
  22      Fortunately this case is rare in the kernel.
  23
  24 For 32-bit we have the following conventions - kernel is built with
  25 -mregparm=3 and -freg-struct-return:
  26
  27 x86 function calling convention, 32-bit:
  28 ----------------------------------------
  29  arguments         | callee-saved        | extra caller-saved | return
  30 [callee-clobbered] |                     | [callee-clobbered] |
  31 -------------------------------------------------------------------------
  32 eax edx ecx        | ebx edi esi ebp [*] | <none>             | eax, edx [**]
  33
  34 ( here too esp is obviously invariant across normal function calls. eflags
  35   is clobbered. Leftover arguments are passed over the stack frame. )
  36
  37 [*]  In the frame-pointers case ebp is fixed to the stack frame.
  38
  39 [**] We build with -freg-struct-return, which on 32-bit means similar
  40      semantics as on 64-bit: edx can be used for a second return value
  41      (i.e. covering integer and structure sizes up to 64 bits) - after that
  42      it gets more complex and more expensive: 3-word or larger struct returns
  43      get done in the caller's frame and the pointer to the return struct goes
  44      into regparm0, i.e. eax - the other arguments shift up and the
  45      function's register parameters degenerate to regparm=2 in essence.

太好了!这解答了我很多困惑。你能告诉我你是如何找到这样好的文档的吗?这是一个愚蠢的问题。但说真的,我搜索了几个小时,也没有找到这样的文档。顺便问一下:你能推荐一些书籍/链接来指导内核编程,特别是低级别的方式吗?非常感谢! - Infinite
嗯,我知道该去哪里找 :) 谈到书籍,你可以在这里搜索类似的问题。此外,你也可以直接查看Linux内核源代码。同时,欢迎提出问题。 - Ilya Matveychikov
x86_64调用约定的主要参考资料是http://www.x86-64.org/documentation/abi.pdf(“ABI” - 应用程序二进制接口)。它列出了调用约定和复合数据(struct)以及“原始”类型的布局/大小/对齐约束。Linux内核在64位x86中使用的约定与此非常接近。在32位中,它们与用户空间ABI非常不同(如果您好奇,请搜索网络上的_gabi.pdf_),但由于使用了`gcc -mregparm',这就是内核源代码派上用场的地方。 - FrankH.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接