x86-64栈帧解析:CSAPP练习3.34汇编代码疑问求解
问题1:leaq指令的作用与参数传递逻辑
你的认知有个小偏差:这里的leaq根本不是做内存地址计算,是gcc常用的整数算术优化写法。
首先%rdi里存储的是入参x的实际数值(x是通过寄存器传递的long类型参数,值直接存在寄存器里,不存在“取x的内存地址”的操作)。leaq D(reg)的作用是把reg + D这个计算出来的“地址值”直接存到目标寄存器,完全不访问内存,所以这里的leaq 1(%rdi), %r15等价于%r15 = x + 1,和普通加法没区别,只是比addq更灵活,也不会修改状态标志位。
关于参数传递,你需要结合x86-64 System V调用约定理解:
- 函数的前6个整数/指针参数,依次通过
%rdi、%rsi、%rdx、%rcx、%r8、%r9传递 - 超过6个的参数,从右往左压入栈中传递
你现在看到的只是P函数的前半部分代码:开头的6个pushq是保存被调用者保存寄存器的旧值,所以P可以自由修改这些寄存器存储计算出来的a0~a5的值,后续(题目没贴的后半部分代码)会把这些寄存器里的值移动到对应的参数寄存器,作为Q的前6个参数。剩下的a6、a7是第7、8个参数,符合规则直接压入栈中,Q会按照调用约定自动从对应寄存器和栈位置读取参数,不需要额外感知参数来源。
问题2:栈结构与未使用空间的作用
你疑惑的%rsp+16 ~ %rsp+23的8字节空间,是为了满足栈对齐要求的填充空间,没有实际存储有效数据,值是随机的垃圾值。
x86-64 System V调用约定强制要求:执行call指令调用函数前,栈指针%rsp必须16字节对齐(即%rsp的值模16等于0)。我们可以算一下当前的栈偏移:
- 刚进入P函数时,
call P已经压了8字节的返回地址,此时%rsp模16余8,未对齐 - 连续6次
pushq共压入48字节,48是16的倍数,此时%rsp模16还是余8 - 执行
subq $24, %rsp分配栈空间:24 = 16(存储a6、a7两个参数) + 8(填充对齐),减去24后%rsp模16余0,刚好满足对齐要求
最终你看到的24字节栈空间的完整结构为:
填充垃圾值: 16(%rsp) a7(x+7): 8(%rsp) a6(x+6): 0(%rsp) <--- %rsp指向此处
内容的提问来源于stack exchange,提问作者Leonard Mohr
相关产品推荐
相关产品推荐

