加餐 | 汇编代码编程与栈帧管理
加餐 | 汇编代码编程与栈帧管理
讲述:宫文学
时长13:30大小12.35M
示例 1:过程调用和栈帧
示例 2:同时使用寄存器和栈来传参
示例 3:循环语句的汇编码解析
示例 4:if 语句的汇编码解析
示例 5:浮点数的使用
课程小结
一课一思
赞 8
提建议
精选留言(18)
- 骨汤鸡蛋面2020-02-22老师,通过今天学习有以下总结: 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一个对象的时候,有可能使用栈吗? 希望你能在寻找这些问题答案的时候,获得更多的收获。 或者,可能我会在第二季多少涉及这些内容。
共 3 条评论6 - zKerry2019-11-04栈的扩大和缩小有点反直觉啊,为啥扩大是减,缩小是加
作者回复: 因为栈是从高地址向低地址延伸的。所以地址减的话,才是栈的增长。
6 - 初心丶可曾記2019-10-18图中的%rbp应该是指向【上一帧的%rbp的值】的下方红线部位,不应该是【返回地址】的下方红线
作者回复: 回头我更新一版图,让图中的箭头指向格子而不是线,这样更加没有歧义。
共 2 条评论5 - ifelse2021-10-20不错,有收获
作者回复: 昵称很赞!
共 2 条评论1 - 骨汤鸡蛋面2020-02-20有个疑惑点:函数调用返回时,一个函数的栈帧是作为一整个单位被丢弃掉嘛?
作者回复: 对的。栈顶指针重新赋值了,栈顶外的内存就抛弃了。这就是一种很自动的内存管理机制,比在堆里申请和释放内存要简单。 所以说在栈里声明的本地变量,它的生存期跟作用域是一致的(闭包除外)。
1 - 风2019-11-05局部变量的访问,既可以用rbp-的方式,也可以用rsp+的方式,文中实例里,都是rbp-的方式,所以需要管理好rbp这个寄存器。 如果采用rsp+的方式,是不是根本就不需要rbp这个寄存器了,这样效率不就更高了? 我看到的一些ARM核,里面只有rsp寄存器,没有rbp寄存器,这样是不是更好呢?
作者回复: 没错。用两个寄存器来标记栈桢,确实有点浪费。实际上是可以优化掉的。 如果你用gcc编译的话,可以使用-fomit-frame-pointer参数来生成汇编,会把下面三行代码都去掉。 pushq %rbp movq %rsp, %rbp popq %rbp 我在34讲的一个例子中,手工去掉了这三行代码,生成的机器码可以少5个字节,还少两次内存访问,其中有一次是写操作,高速缓存都帮不上忙。对于追求极致性能的程序来说,这个优化是必要的。
1 - 阿鼎2019-10-18协程的切换,用户态代码要复制堆栈寄存器信息。也想请教老师,协程调度是否只能在io线程呢?非io线程能否用协程呢?
作者回复: 非io当然可以用协程。比如迭代器、状态机用协程来写就很优雅。
共 3 条评论1 - 沉淀的梦想2019-10-14老师在课中讲了不少“栈”的操作,那编程语言对于"堆"又是用什么指令操作的呢?
作者回复: 鼓励你用c语言,使用malloc和free来申请和释放内存,看看生成的汇编是怎样的。
共 3 条评论1 - pebble2019-10-14例一的俩栈帧图里,rbp跟rsp,是否应该都指向再下一个位置呢,rsp指向的,应该是下次要保存数据的位置吧
作者回复: 不是。 rbp,指向栈底。这个值在整合函数执行期间是不变的。 rsp,指向栈顶。这个值会在某些情况下改变: (1)push和pop命令可以改变rsp。 (2)call指令,因为要把返回地址压栈,实际也改变了rsp。 (3)在使用本地变量时,手工改变rsp的值。 rsp如果指向下次要保存数据的位置,相当于栈里总有一个空单元。
共 2 条评论1 - ♀楠生♀2022-11-01 来自广东as functio-call2-craft.s -o function-call2样得到的是.o文件,而不是可执行程序.执行./function-call2 会报 zsh: exec format error: ./function-call2
- 程序员班吉2022-04-08扩展栈和缩小栈,使用的是一个立即数,这个立即数表示的是将地址向上或者向下移动多少吗?
- Geek_6562452021-10-16视屏流,音频流在汇编和机器码中又是什么样子的呢?
- Geek_6562452021-10-16汇编转化成机器码又是什么样子的呢?
- minghu62021-05-29看一些汇编代码的例子总是觉得腰背酸痛, 原理性不多, 就是些具体规定而且高度平台特定, 纯搬砖, 给平台打工, 看了真不带劲儿
作者回复: 是,与汇编有关的工作,工程性更强一些。
- favoorr2020-12-09这个第一次学的时候还真很难一次明白,最好是用 GDB 来单步,观察寄存器的值,一边单步,一边拿自己小本本记,来加深理解
作者回复: 你讲的很对。最好善用GDB、LLDB这些调试工具,这样很多抽象的知识就变得可视化了!
1 - __Unsafe2020-10-22_fun1: # 函数调用的序曲,设置栈指针 pushq %rbp # 把调用者的栈帧底部地址保存起来 movq %rsp, %rbp # 把调用者的栈帧顶部地址,设置为本栈帧的底部 movl $10, -4(%rbp) # 变量c赋值为10,也可以写成 movl $10, (%rsp) 这里不能写成movl $10, (%rsp)吧,上一步movq %rsp %rbp已经把%rbp的值设为%rsp一样了展开
作者回复: 只要是等价的,就可以。 只不过,这种代码通常都是由编译器机械的生成的,它会按照一个固定的套路来生成代码。
- 骨汤鸡蛋面2020-02-22在“同时使用寄存器和栈来传参”那一些小节,且%rsp 并未指向栈真实的最顶部,那么当在fun里再调用其它方法时,其它方法 执行pushq %rbp,会不会覆盖掉 -4(%rbp)的值,进而出错?
作者回复: 必须是叶子函数才可以。 下面是https://en.wikipedia.org/wiki/X86_calling_conventions 中摘的一段,介绍System V AMD64 ABI的内容,你可以看看: For leaf-node functions (functions which do not call any other function(s)), a 128-byte space is stored just beneath the stack pointer of the function. The space is called red-zone. This zone will not be clobbered by any signal or interrupt handlers. Compilers can thus utilize this zone to save local variables. Compilers may omit some instructions at the starting of the function (adjustment of RSP, RBP) by utilizing this zone. However, other functions may clobber this zone. Therefore, this zone should only be used for leaf-node functions. gcc and clang offer the -mno-red-zone flag to disable red-zone optimizations.
- 沉淀的梦想2019-10-14老师的as用的什么版本,为什么我用as汇编文稿中的代码会出错(注释删了也一样会出错): function-call2-craft.s: Assembler messages: function-call2-craft.s:2: Error: no such instruction: `n-call2-craft.s 函数调用和参数传递' function-call2-craft.s:4: Error: character following name is not '#' function-call2-craft.s:66: Error: character following name is not '#'展开
作者回复: macOS操作系统,gnu汇编器。 Linux上生成的汇编码应该也差不多,有些系统调用是不同的。 windows上,你就要安装一下相关的环境了和工具了,比如MinGW。或者装一个Linux虚拟机。
共 3 条评论