美文网首页Linux学习之路我用 Linux程序员
Linux boot的第一步:启动汇编调用main函数

Linux boot的第一步:启动汇编调用main函数

作者: code猪 | 来源:发表于2018-05-08 00:19 被阅读124次

    为了讲清原理,我们首先介绍C函数调用机制,然后再介绍汇编调用C函数。

    一、C函数调用机制

    对于汇编中的函数,其使用栈来传递参数和存储临时变量。其中的帧指针ebp用于定位参数和临时变量,而栈指针esp用于辅助所有的存储,包括ebp、各种返回跳转地址、参数和临时变量。
    其示意图图如下:


    栈帧结构

    接下来我们对实例进行分析。
    首先,给出C语言源码:

    int add(int a, int b)
    {
        int c;
        c=a+b;
        return c;
    }
    
    int main()
    {
        int a, b, c;
        a=1; b=2;
        c=add(a, b);
        return c;
    }
    

    进行汇编:

    gcc -Wall -S -o test.s test.c
    

    得到汇编源码:

        .file   "test.c"
        .text
    .globl add
        .type   add, @function
    add:
        pushl   %ebp
        movl    %esp, %ebp
        subl    $16, %esp
        movl    12(%ebp), %eax
        movl    8(%ebp), %edx
        leal    (%edx,%eax), %eax
        movl    %eax, -4(%ebp)
        movl    -4(%ebp), %eax
        leave
        ret
        .size   add, .-add
    .globl main
        .type   main, @function
    main:
        pushl   %ebp
        movl    %esp, %ebp
        subl    $24, %esp
        movl    $1, -12(%ebp)
        movl    $2, -8(%ebp)
        movl    -8(%ebp), %eax
        movl    %eax, 4(%esp)
        movl    -12(%ebp), %eax
        movl    %eax, (%esp)
        call    add
        movl    %eax, -4(%ebp)
        movl    -4(%ebp), %eax
        leave
        ret
        .size   main, .-main
        .ident  "GCC: (GNU) 4.4.7 20120313 (Red Hat 4.4.7-18)"
        .section    .note.GNU-stack,"",@progbits
    

    可以看到GCC现代版本会为局部变量多分配一些空间。main函数中分配了24个字节,其实12个就够了;add函数分配了16个字节,其实4个就够了。我对汇编源码修改为最少空间,发现也能运行。
    其对应栈帧示意图如下:


    add函数调用前后的栈帧结构

    其中对应3个指令特别关键:

    call:将返回指令压栈,并跳转到调用函数位置
    ret:弹出栈顶处的地址,并跳转到其地址
    leave:
      movl %ebp, %esp
      popl %ebp
    

    二、汇编调用C函数

    理解了C函数的调用机制,接下来就比较简单了。
    对上面的例子可以进行分析。
    首先,摘出汇编源码,1.s:

        .text
    .globl main
        .type   main, @function
    main:
        pushl   %ebp
        movl    %esp, %ebp
        subl    $24, %esp
        movl    $1, -12(%ebp)
        movl    $2, -8(%ebp)
        movl    -8(%ebp), %eax
        movl    %eax, 4(%esp)
        movl    -12(%ebp), %eax
        movl    %eax, (%esp)
        call    add
        movl    %eax, -4(%ebp)
        movl    -4(%ebp), %eax
        leave
        ret
        .size   main, .-main
        .ident  "GCC: (GNU) 4.4.7 20120313 (Red Hat 4.4.7-18)"
        .section    .note.GNU-stack,"",@progbits
    

    然后,对应的C函数,2.c:

    int add(int a, int b)
    {
        int c;
        c=a+b;
        return c;
    }
    

    接下来使用gcc编译链接:

    gcc -o test 1.s 2.c
    

    这样,我们就知道Linux启动汇编是怎么调用main函数的,也就有了入门基础。
    当然,还需要了解x86的保护模式,这个我推荐李忠老师的《x86汇编语言:从实模式到保护模式》。
    码字快,画图耗时麻烦,希望大家认可。

    相关文章

      网友评论

      本文标题:Linux boot的第一步:启动汇编调用main函数

      本文链接:https://www.haomeiwen.com/subject/ebpbrftx.html