【问题标题】:How to write a C function invoked from assembly code如何编写从汇编代码调用的 C 函数
【发布时间】:2012-04-21 02:59:20
【问题描述】:

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

应该考虑哪些特殊问题?

我有一些想法,但谁能提供更多细节:

(1) 调用约定

确保汇编中的调用者和c中的被调用者握手良好。但是我应该使用哪种调用约定?如何声明c函数并在汇编代码中声明?

(2) 保护寄存器

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

还有什么问题?

【问题讨论】:

  • "我需要编写一个 C 函数,该函数将从 linux 内核的汇编代码中调用。" - 是什么阻止了你?
  • @MitchWheat 具体来说,我不知道声明 c 函数时“asmlinkage”的确切用法。我必须使用它吗?还有其他选择吗?这意味着什么?
  • 首先 - 尽量不要这样做。 Linux 在一些地方使用汇编,而且它们都很难处理。我认为问题取决于它是什么汇编代码。在中断处理程序的早期阶段运行,或在早期启动阶段(使用 C 的两个示例)是两件不同的事情。
  • asmlinkage 表示所有参数都通过堆栈传递。而 IIRC asmlinkage 对 64 位代码什么也不做。
  • 制作一个包含函数的简单库。制作一个调用库的 hello 程序。大多数玩具编译器都带有一个测试库和 hello 程序。使用 gdb 之类的调试器对它们进行反汇编,并注意参数是如何传递的。

标签: c assembly linux-kernel inline-assembly


【解决方案1】:

当您的任务标记为 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.

【讨论】:

  • 太棒了!这回答了我的很多困惑。你能告诉我你是怎么找到这么好的文件的吗?一个愚蠢的问题。但说真的,我搜索了几个小时,但没有找到这样的文件。 BTW:你能推荐一些可以指导内核编程的书籍/链接,尤其是低级时尚吗?非常感谢!
  • 嗯,我只知道在哪里看 :) 说到书籍,您可以在这里搜索类似的问题。除此之外,您始终可以查看 Linux 内核源代码本身。另外,欢迎提问。
  • x86_64 调用约定的主要参考是x86-64.org/documentation/abi.pdf(“ABI” - 应用程序二进制接口)。它列出了调用约定和复合数据 (struct) 以及“原始”类型布局/大小/对齐约束。 Linux 内核在 64 位 x86 中使用的约定与此非常接近。在 32 位中,它们与用户空间 ABI 完全不同(如果您好奇,请在网上搜索 gabi.pdf),但是,由于使用了 `gcc -mregparm' - 这就是内核源代码就派上用场了。
猜你喜欢
  • 2017-01-19
  • 1970-01-01
  • 2016-09-07
  • 2017-02-18
  • 1970-01-01
  • 2011-09-04
  • 1970-01-01
  • 2016-06-01
  • 1970-01-01
相关资源
最近更新 更多