加餐 | 汇编代码编程与栈帧管理
该思维导图由 AI 生成,仅供参考
示例 1:过程调用和栈帧
- 深入了解
- 翻译
- 解释
- 总结
这篇文章深入探讨了汇编语言编程与栈帧管理的基础知识,重点介绍了汇编代码编程中的参数传递、栈帧管理、函数调用以及汇编指令操作。通过详细讲解过程调用和栈帧管理的实际场景,并结合示例程序对函数调用时参数传递、栈帧的管理以及局部变量的使用进行了解析。文章还涉及了浮点数的使用,介绍了浮点数传参、浮点数加法运算指令以及浮点数返回值的处理方式。通过对比手写汇编代码和对应的C语言代码,读者可以深入理解汇编语言的编写思路和实现原理。总体而言,本文适合想要深入了解汇编语言编程和栈帧管理的读者,通过实际示例帮助读者加深对汇编语言的理解和掌握。文章内容涵盖了汇编代码编程中的参数传递、栈帧管理、函数调用和汇编指令操作,对于想要深入了解汇编语言编程的读者具有很高的参考价值。
《编译原理之美》,新⼈⾸单¥59
全部留言(18)
- 最新
- 精选
- 骨汤鸡蛋面老师,通过今天学习有以下总结: 1. pushq 和 popq 虽然是单“参数”指令,但一个隐藏的“参数”就是 %rsp。 2. 通过移动 %rsp 指针来改变帧的大小。%rbp 和 %rsp 之间的空间就是当前栈帧。 3. 栈帧先进后出 (一个函数的相关 信息占用一帧)。或者栈帧二字 重点在帧上。%rbp 在函数调用时一次移动 一个栈帧的大小,**%rbp在整个函数执行期间是不变的**。 4. 函数内部访问 栈帧 可以使用 `-4(%rbp)`表示地址,表示%rbp 寄存器存储的地址减去4的地址。说白了,**栈帧内可以基于 (%rbp) 随机访问**,`+4(%rsp)`效果类似。 5. **%rsp并不总是指向真实的栈顶**:在 X86-64 架构下,新的规范让程序可以访问栈顶之外 128 字节的内存,所以,我们甚至不需要通过改变 %rsp 来分配栈空间,而是直接用栈顶之外的空间。比如栈帧大小是16,即·`(%rbp)-(%rsp) = 16`,可以在代码中直接使用 内存地址`-32(%rbp)`。但如果函数内 还会调用 其它函数,为了pushq/popq 指令的正确性,编译器会为%rsp 设置正确的值使其 指向栈顶。 6. 除了callq/pushq/popq/retq 指令操作%rsp外,函数执行期间,可以mov (%rsp)使其指向栈顶一步到位,(%rsp)也可以和(%rbp)挨着一步不动,也可以随着变量的分配慢慢移动。
作者回复: 你总结得很细,很清晰。都可以画出一张脑图了! 这些技术细节,可以找到相应的技术规格文档去阅读,并获得更多你感兴趣的内容。课程里讲的,主要是Unix家族的系统约定。Windows系统可能会不同。但你总能找到相应的技术文档来找到这些约定。 进一步,你可以再寻找下面几个问题的答案: 1.为什么只要移动栈指针就能分配内存。应用程序的内存都是操作系统虚拟出来的,操作系统必须在你使用内存的时候,给你准备好真实的物理内存。对这个问题的回答就涉及一点操作系统的知识,我相信你能查到。 2.在栈里申请内存,相比在堆里相比内存,哪个更快?哪个容易导致内存碎片?哪个更有利于局部性?这两种内存申请方式各自的使用场景是什么?像Python这样的动态语言有可能使用栈吗?像Java这样的语言,在new一个对象的时候,有可能使用栈吗? 希望你能在寻找这些问题答案的时候,获得更多的收获。 或者,可能我会在第二季多少涉及这些内容。
2020-02-2236 - zKerry栈的扩大和缩小有点反直觉啊,为啥扩大是减,缩小是加
作者回复: 因为栈是从高地址向低地址延伸的。所以地址减的话,才是栈的增长。
2019-11-046 - 初心丶可曾記图中的%rbp应该是指向【上一帧的%rbp的值】的下方红线部位,不应该是【返回地址】的下方红线
作者回复: 回头我更新一版图,让图中的箭头指向格子而不是线,这样更加没有歧义。
2019-10-1825 - ifelse不错,有收获
作者回复: 昵称很赞!
2021-10-2021 - favoorr这个第一次学的时候还真很难一次明白,最好是用 GDB 来单步,观察寄存器的值,一边单步,一边拿自己小本本记,来加深理解
作者回复: 你讲的很对。最好善用GDB、LLDB这些调试工具,这样很多抽象的知识就变得可视化了!
2020-12-091 - 骨汤鸡蛋面有个疑惑点:函数调用返回时,一个函数的栈帧是作为一整个单位被丢弃掉嘛?
作者回复: 对的。栈顶指针重新赋值了,栈顶外的内存就抛弃了。这就是一种很自动的内存管理机制,比在堆里申请和释放内存要简单。 所以说在栈里声明的本地变量,它的生存期跟作用域是一致的(闭包除外)。
2020-02-201 - 风局部变量的访问,既可以用rbp-的方式,也可以用rsp+的方式,文中实例里,都是rbp-的方式,所以需要管理好rbp这个寄存器。 如果采用rsp+的方式,是不是根本就不需要rbp这个寄存器了,这样效率不就更高了? 我看到的一些ARM核,里面只有rsp寄存器,没有rbp寄存器,这样是不是更好呢?
作者回复: 没错。用两个寄存器来标记栈桢,确实有点浪费。实际上是可以优化掉的。 如果你用gcc编译的话,可以使用-fomit-frame-pointer参数来生成汇编,会把下面三行代码都去掉。 pushq %rbp movq %rsp, %rbp popq %rbp 我在34讲的一个例子中,手工去掉了这三行代码,生成的机器码可以少5个字节,还少两次内存访问,其中有一次是写操作,高速缓存都帮不上忙。对于追求极致性能的程序来说,这个优化是必要的。
2019-11-051 - 阿鼎协程的切换,用户态代码要复制堆栈寄存器信息。也想请教老师,协程调度是否只能在io线程呢?非io线程能否用协程呢?
作者回复: 非io当然可以用协程。比如迭代器、状态机用协程来写就很优雅。
2019-10-1831 - 沉淀的梦想老师在课中讲了不少“栈”的操作,那编程语言对于"堆"又是用什么指令操作的呢?
作者回复: 鼓励你用c语言,使用malloc和free来申请和释放内存,看看生成的汇编是怎样的。
2019-10-1431 - pebble例一的俩栈帧图里,rbp跟rsp,是否应该都指向再下一个位置呢,rsp指向的,应该是下次要保存数据的位置吧
作者回复: 不是。 rbp,指向栈底。这个值在整合函数执行期间是不变的。 rsp,指向栈顶。这个值会在某些情况下改变: (1)push和pop命令可以改变rsp。 (2)call指令,因为要把返回地址压栈,实际也改变了rsp。 (3)在使用本地变量时,手工改变rsp的值。 rsp如果指向下次要保存数据的位置,相当于栈里总有一个空单元。
2019-10-1421