assembly kernel_thread_helper如何使用内联汇编将参数传递给内核线程函数?

rggaifut  于 2022-11-13  发布在  其他
关注(0)|答案(1)|浏览(116)

我正在阅读2.6.11版linux内核的源代码,对arch/i386/kernel/process.c中的内联汇编kernel_thread_helper感到困惑。
首先,寄存器%ebx存储一个函数的地址,寄存器%edx存储一个指向该函数的指针。

extern void kernel_thread_helper(void);
__asm__(".section .text\n"
    ".align 4\n"
    "kernel_thread_helper:\n\t"
    "movl %edx,%eax\n\t"
    "pushl %edx\n\t"
    "call *%ebx\n\t"
    "pushl %eax\n\t"
    "call do_exit\n"
    ".previous");

我纳闷道:

  • 这段代码为什么要执行movl %edx,%eax?因为pushl %edx已经将%edx的值压入堆栈,那么%ebx指向的函数应该可以成功地从堆栈中检索到所需的参数。那么,是否不需要将%edx复制到%eax
  • 我知道当__attribute__((regparm(3)))被添加到函数定义之前时,函数将按照%eax%edx%ecx和堆栈的顺序检索参数。将%edx中的值复制到%eax的指令是否设计为与使用此__attribute__的函数兼容?具有这样一个属性的函数应该从%eax中检索所需的参数并继续运行!但这会导致一个问题:如果没有人将先前由pushl %edx压入的指针弹出,则该指针可保留在堆栈中。

我是Linux内核的新手,可能误解了一些东西。我想知道这些汇编指令实际上是如何工作的,我的猜测有什么问题。

pieyvz9o

pieyvz9o1#

早期Linux内核版本(v2.5.0)中的一条注解对此进行了解释(为了更准确起见,v2.1.131中添加了movleax):

int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
{
    long retval, d0;

    __asm__ __volatile__(
        "movl %%esp,%%esi\n\t"
        "int $0x80\n\t"     /* Linux/i386 system call */
        "cmpl %%esp,%%esi\n\t"  /* child or parent? */
        "je 1f\n\t"     /* parent - jump */
        /* Load the argument into eax, and push it.  That way, it does
         * not matter whether the called function is compiled with
         * -mregparm or not.  */
        "movl %4,%%eax\n\t"
        "pushl %%eax\n\t"       
        "call *%5\n\t"      /* call fn */
        "movl %3,%0\n\t"    /* exit */
        "int $0x80\n"
        "1:\t"
        :"=&a" (retval), "=&S" (d0)
        :"0" (__NR_clone), "i" (__NR_exit),
         "r" (arg), "r" (fn),
         "b" (flags | CLONE_VM)
        : "memory");
    return retval;
}

因此,是的,正如您所怀疑的,将movl转换为eax正是为了为使用-mregparam编译的 both 函数或等效的__attribute__ * 和 * 不使用(i386 System V ABI调用约定,即堆栈上的所有参数)提供兼容性。
但这就引起了一个问题:如果没有人将先前由pushl %edx压入的指针弹出,则该指针可保留在堆栈中。
好吧,没错,但是如果我们在调用内核线程函数之后只做pushl %eax; call do_exit的话,谁会在乎弹出它呢?执行movl %eax, 0(%esp)也可以,但是如果有足够的堆栈空间来运行内核线程,那么在它返回之后肯定有4个字节来压入寄存器。另外,pushl %eax只有1个字节,而movl只有3个字节。我不确定这两者的延迟或吞吐量,但我真的不认为这是这样一段代码中的问题。

相关问题