csapp 第三章 3.2.2 x86-64 函数调用约定:寄存器分工与c语言转汇编学习(未完成版)

问题引入

当我在学习汇编语言,c语言代码转化成汇编代码时,了解到汇编代码需要将变量储存到寄存器,而x86-64寄存器只有稀缺的16个,这16个寄存器需要分给无限层级的函数调用,不可避免遇到深层的函数使用相同的寄存器覆盖上层函数的寄存器值,导致函数回调时原先的值已经被破坏的情况。那么是怎么处理这个问题的呢?

具体代码如下图,

是关于计算两个数的相乘结果同时用传指针的方法来获取值的一个功能。

c语言代码

c 复制代码
long mult2(long x, long y) {
    return x * y;
}

// 把乘法结果存到指定地址
void multstore(long x, long y, long *dest) {
    long t = mult2(x, y);   // 调用mult2计算
    *dest = t;              // 结果存入dest指向的内存
}

int main() {
    long result = 0;        // 初始化为0
    multstore(3, 4, &result);  // 调用:3*4=12,存入result
    // result现在是12
    return 0;
}

对应汇编代码

汇编 复制代码
# mult2函数:计算x*y
mult2:
    movq    %rdi, %rax      # x从%rdi移到%rax
    imulq   %rsi, %rax      # %rax = x * y
    ret                     # 返回,结果在%rax

# multstore函数:调用mult2,结果存到*dest
multstore:
    pushq   %rbx            # 保存%rbx的旧值到栈(callee-saved)
    movq    %rdx, %rbx      # 把dest指针存到%rbx(安全转移)
    call    mult2           # 调用mult2(x, y)
    movq    %rax, (%rbx)    # 把mult2的返回值存到*dest
    popq    %rbx            # 恢复%rbx的旧值
    ret                     # 返回main

# main函数
main:
    subq    $16, %rsp       # 给result分配栈空间
    movq    $0, 8(%rsp)     # result = 0
    
    leaq    8(%rsp), %rdx   # &result → %rdx(第3个参数)
    movl    $4, %esi        # 4 → %esi(第2个参数y)
    movl    $3, %edi        # 3 → %edi(第1个参数x)
    call    multstore       # 调用multstore
    
    movq    8(%rsp), %rax   # result的值(可选)
    addq    $16, %rsp       # 恢复栈
    xorl    %eax, %eax      # return 0
    ret

ok,首先这里multstore中 pushq %rbx 和 popq %rbx 就体现了对应的寄存器值保存处理

发现问题

解决问题

这里用实际上是将寄存器分为 caller-saved(调用者保存) / callee-saved(被调用者保存) 两种,来避免这个问题

caller-saved / callee-saved

caller-saved寄存器,调用者保存,被调用者不负责保存,适合临时短期变量

具体例子说明

图一:

这里算平均值的例子中sum和count都是短期存一下得到一个短期结果,avg也是短期结果。在调用函数call printf中不会被用到,什么叫短期和长期使用变量呢。如下图

图二:

在图二场景中,(转换成汇编代码后)如果我将i赋给一个寄存器假如是&rbx,那么在while循环中会调用另一个函数do_something,那么这个do_something函数中可能也会将一个变量储存在&rbx中,那么i原本的值可能就被这个新变量的值覆盖了,当这个do_something函数结束后,i++的时候i的值已经被覆盖不是执行do_something前的数字,那么程序就出问题了。所以就需要保存调用函数之前那个i值。(挺像并发的,记住原来的模样后再切出去)

这里的i就是一个需要长期保存的值,长期变量(因为他有可能在后面调用函数时被改)

回过头来再看图一,方案A中就是一个短期变量了,

相关推荐
韩振方4 分钟前
容器已经能运行了,为什么 Kubernetes 还要用 Pod?
后端
会编程的吕洞宾4 分钟前
AgentScope Java 实战:给 AI Agent 加上权限管控
后端
Thneonl3 小时前
全集群钟差 300 毫秒会发生什么:证书悄悄过期,日志倒流
运维·后端
金銀銅鐵3 小时前
[Java] 借助GUI展示class文件的版本号
后端·python·ai编程
马剑威(威哥爱编程)3 小时前
【AI全栈后端12-12】Spring Boot 3.x 到 4.x 迁移实操:Jakarta 11、Jackson 3 与 AI 2.0
java·开发语言·spring boot·后端
136096757233 小时前
一台 4 核 8G 已经跑了 6 个站点,我是怎么把第 7 个塞进去的
后端
对象存储与RustFS3 小时前
JuiceFS + 对象存储:把 S3 变成 POSIX 文件系统实测
后端·rust·开源
她的男孩3 小时前
企业接口照样拦得住:独立 Flyway、@RequiresFeature 与离线许可证
java·spring boot·后端
十年Java程序媛3 小时前
Lambda 与函数式接口|别只会复制 ()->{},底层规则和坑一次性讲清
java·spring boot·后端