栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

C语言中主函数调用另外一个函数,汇编代码理解

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

C语言中主函数调用另外一个函数,汇编代码理解

栈的理解

由于主函数在执行过程中,需要从主函数跳到被调用的函数,这就涉及到保存当前函数状态,进入被调函数的操作。这个保存状态的操作就需要用到栈。
栈的结构如下:

关于栈的理解有几点要注意的:

  • 栈是由高地址向低地址方向增长
  • rsp是堆栈寄存器,里面存放的是栈顶指针的地址
  • 栈顶指针只有在需要扩大栈大小或者缩小栈大小的时候,才会向低地址移动,否则向入栈和出栈是根据栈顶指针的偏移量来操作的,栈顶指针不需要移动。
示例代码

主函数call_proc.c

#include"proc.h"
long call_proc()
{
        long  x1 = 1; int  x2 = 2;
        short x3 = 3; char x4 = 4;
        proc(x1, &x1, x2, &x2, x3, &x3, x4, &x4);
        return (x1+x2)*(x3-x4);
}

被调函数proc.c

void proc(long  a1, long  *a1p,
          int   a2, int   *a2p,
          short a3, short *a3p,
          char  a4, char  *a4p)
{
        *a1p += a1;
        *a2p += a2;
        *a3p += a3;
        *a4p += a4;
}

汇编代码

利用执行gcc -Og -S xxx.c分别生成两者的汇编代码call_proc.s和proc.s如下(只列出关键部分):
call_proc.s

.file   "call_proc.c"
        .text
        .globl  call_proc
        .type   call_proc, @function 
call_proc: 
.LFB0:
        .cfi_startproc
        endbr64
        pushq   %rbx
        .cfi_def_cfa_offset 16
        .cfi_offset 3, -16
        subq    $32, %rsp
        .cfi_def_cfa_offset 48
        movl    $40, %ebx
        movq    %fs:(%rbx), %rax
        movq    %rax, 24(%rsp)
        xorl    %eax, %eax
        movq    $1, 16(%rsp)
        movl    $2, 12(%rsp)
        movw    $3, 10(%rsp) 
        movb    $4, 9(%rsp)
        leaq    12(%rsp), %rcx
        leaq    16(%rsp), %rsi
        leaq    9(%rsp), %rax
        pushq   %rax  
        .cfi_def_cfa_offset 56
        pushq   $4    
        .cfi_def_cfa_offset 64
        leaq    26(%rsp), %r9
        movl    $3, %r8d
        movl    $2, %edx
        movl    $1, %edi
        call    proc@PLT
        movslq  28(%rsp), %rcx
        addq    32(%rsp), %rcx
        movswl  26(%rsp), %edx
        movsbl  25(%rsp), %eax
        subl    %eax, %edx
        movslq  %edx, %rax
        imulq   %rcx, %rax
        addq    $16, %rsp
        .cfi_def_cfa_offset 48
        movq    24(%rsp), %rdi
        xorq    %fs:(%rbx), %rdi
        jne     .L4
        addq    $32, %rsp
        .cfi_remember_state
        .cfi_def_cfa_offset 16
        popq    %rbx
        .cfi_def_cfa_offset 8
        ret

proc.s

       .file   "proc.c"
        .text
        .globl  proc
        .type   proc, @function
proc:
.LFB0:
        .cfi_startproc
        endbr64
        movq    16(%rsp), %rax
        addq    %rdi, (%rsi)
        addl    %edx, (%rcx)
        addw    %r8w, (%r9)
        movl    8(%rsp), %edx
        addb    %dl, (%rax)
        ret
        .cfi_endproc
汇编代码分析 先分析call_proc.s中的汇编代码 更改栈的大小
subq $32, %rsp //将rsp减少32个字节,因为栈的增长方向是大地址往小地址,相当于增大栈的容量
保存局部变量
//将局部变量压栈,rsp前面的数字是相当于栈顶指针的偏移量
movq    $1, 16(%rsp) 
movl    $2, 12(%rsp)
movw    $3, 10(%rsp)
movb    $4, 9(%rsp)

做完上述操作,栈的示意图如下:

保存被调函数的形参

因为该被调函数有8个形参,但是最多只有6个形参能保存到寄存器中,因此有两个被保存到了栈中,且是形参的最后两个值。

 //将形参保存到寄存器中
 leaq    12(%rsp), %rcx  //load effective adress,用于一个内存地址直接赋给目的操作数
 leaq    16(%rsp), %rsi
 
 //将形参保存到栈中,
 leaq    9(%rsp), %rax
 pushq   %rax  //将寄存器中的数压栈
 pushq   $4  // 将立即数压栈
 
 //将形参保存到寄存器中
 leaq    26(%rsp), %r9
 movl    $3, %r8d
 movl    $2, %edx
 movl    $1, %edi

参数和形参的对应关系如下图所示:

调用被调函数
call    proc@PLT

调用被调函数前,会把调用代码的的下一条执行指令压栈,方便从被调函数中返回到主函数时候,还能往下执行。

返回
... //一系列逻辑操作
ret
被调函数汇编代码 逻辑运算
movq    16(%rsp), %rax

// 直接从寄存器中取出运算数
addq    %rdi, (%rsi)
addl    %edx, (%rcx)
addw    %r8w, (%r9)

//从栈中取出运算数
movl    8(%rsp), %edx
addb    %dl, (%rax)

返回
ret
总结
  • 嵌套的函数调用,需要用栈来保存局部变量、形参、返回时候的指令地址
  • C语言的参数是按照从右到左的顺序压到栈中的
参考

C语言函数调用的底层实现
汇编语言中mov和lea的区别有哪些?

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/953709.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号