x86 子函数调用过程分析

本文通过一个简单的C语言程序实例,详细分析了x86架构下函数调用的过程,包括参数传递方式、栈帧的建立与撤销等,并探讨了x86未采用寄存器传递参数的原因。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

课程:《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000

工作系统:深度操作系统 15.1
工作环境: gcc version 5.3.1 20160114 (Debian 5.3.1-6)
GNU gdb (Debian 7.10-1+b1) 7.10

1)c代码:

/**
 * Author: lxhuster
 * Abstract: 
 */

 int foo( int x)
 {
    return (x + 2);
 }

 void main()
 {
    foo(5);
 }

2)编译C代码
这里写图片描述

3)对应汇编代码:

    .file   "main.c"

main:
    pushl   %ebp
    movl    %esp, %ebp
    pushl   $5
    call    foo
    addl    $4, %esp
    nop
    leave
    ret

foo:
    pushl   %ebp
    movl    %esp, %ebp
    movl    8(%ebp), %eax
    addl    $2, %eax
    popl    %ebp
    ret

4)foo函数调用过程分析:
main函数通过栈传递参数“5”
这里写图片描述

总结:通过分析函数调用过程,

        1)可以发现x86默认情况下使用的减满栈的堆栈生长方案。
        2)在汇编代码中movl    8(%ebp), %eax之类访问%ebp指向地址之上的操作多半是在读取传入参数,
                反之,读取%ebp指向地址之下的操作就是在操作局部变量。

疑问:恩,为什么x86木有使用寄存器来传递函数调用参数?

推荐图书:

1)深入理解计算机体系结构
2)老“码”识途从机器码到框架

### Linux x86 架构下的函数调用约定 在Linux x86_64架构中,函数调用遵循特定的规则和惯例。这些规则定义了哪些寄存器用于传递参数、哪些由调用者保存而哪些由被调用者负责保护。 #### 参数传递机制 当进行函数调用时,前六个整数或指针类型的参数按照如下顺序放置于寄存器中:`rdi`, `rsi`, `rdx`, `rcx`, `r8`, 和 `r9`[^1]。如果存在超过六个以上的参数,则超出部分会依次压入栈中。浮点型数据通常利用XMM寄存器(如`xmm0`至`xmm7`)来进行传输。 #### 调用方与被调用方的责任划分 - **Caller-Saved (易失性)**: 对于那些可能因函数调用而改变其值的寄存器(`rax`, `rcx`, `rdx`, `r10`, `r11`),任何希望保留这些寄存器原有内容的操作都需要显式地将其存储起来,在必要时候再恢复。 - **Callee-Saved (非易失性)**: 另一方面,像`rbx`, `rsp`, `rbp`, 以及从`r12`到`r15`这样的寄存器被认为是稳定的;即除非特别说明,否则不应随意更改它们的内容。若确实需要修改这类寄存器中的值,那么应当先保存当前状态,并在适当的时候予以还原[^3]。 #### 返回地址处理 每当执行一条call指令跳转到目标子程序处运行时,CPU自动把下一条待执行指令的位置(也就是所谓的返回地址)推送到堆栈顶部。这使得当遇到ret指令时能够顺利回到原来的地方继续往下走。 ```nasm ; 假设这是一个简单的汇编函数例子 global my_function extern printf ; 外部链接的标准库打印函数 section .data fmt db "Result is %d", 10, 0 section .text my_function: push rbp ; 设置新的基址指针 mov rbp, rsp ; 将栈顶赋给rbp sub rsp, 16 ; 预留空间供局部变量使用 ; 这里预留了16字节的空间作为对齐目的 lea rdi, [rel fmt]; 加载格式字符串地址到rdi mov esi, eax ; 把eax里的数值复制到esi准备传参给printf xor eax, eax ; 清零al以便告诉printf我们只用了两个参数 call printf ; 执行外部print操作 add rsp, 16 ; 恢复原来的rsp位置 pop rbp ; 恢复旧的rbp指向 ret ; 结束并返回上级调用者那里去 ``` 此代码片段展示了如何在一个典型的x86_64环境下编写一个接受单个输入并通过标准输出显示结果的小型汇编函数。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值