22 | 生成汇编代码(一):汇编语言其实不难学
22 | 生成汇编代码(一):汇编语言其实不难学
讲述:宫文学
时长15:45大小14.43M
了解汇编语言
1. 汇编语言的组成元素
2. 详细了解指令这个元素
x86-64 架构的寄存器
课程小结
一课一思
赞 13
提建议
精选留言(18)
- 沉淀的梦想2019-10-12没太看懂文稿中的"leaq L_.str(%rip), %rdi"里面的"L_.str(%rip)"的含义,能再解释一下吗?
作者回复: 就是一个间接内存访问。基数是%rip,也就是指令寄存器中的值,也就是leaq这行代码的下一行代码的地址。L_.str会被汇编器算出一个直接数来,比如0x20,也就是32个字节。这行指令就等价于:0x20(%rip)。 0x20是什么意思?就是这个字符串常量的地址与%rip值的差。这种寻址方式叫做rip相对寻址。因为代码区在下面,静态数据区在上面,所以静态数据的地址一定大于指令的地址。在这里,是多0x20个字节,也就是32个字节。 为什么要这么麻烦呢?为什么不使用这个字符串的确定地址呢?这主要是为了让程序代码更加位置无关。在使用动态库之类的场景的时候有好处。 另外,lea这个指令除了获取地址以外,还有一个“奇怪”的用法,它可以用计算地址的方法,实际上对寄存器的值同时完成加法和乘法的计算,相当于一个三元计算,并且只使用一个时钟周期。比如:lea 0x20(,%eax,2) %eax,本来是用来算一个地址,结果被用来一次性的做了一个乘法和一个加法计算。 提供3篇文章作为参考: https://stackoverflow.com/questions/40329260/why-is-the-address-of-static-variables-relative-to-the-instruction-pointer https://stackoverflow.com/questions/3250277/how-to-use-rip-relative-addressing-in-a-64-bit-assembly-program https://zhuanlan.zhihu.com/p/58774036
共 4 条评论23 - sugar2020-05-01我觉得读到这节 依然发现宫老师花费了很多笔墨在字里行间穿插探讨后端技术的重要性和应用场景。我也来贡献一个应用场景吧: 几年前我曾经听组里一位资深的后端(服务器后端)工程师分享过一道golang面试题,题目很简单:“写一个for循环输出从1到10 用go比用c更快,为什么?” ,当时的我不明觉厉,于是去查了些资料发现似乎考点是一个叫达夫机器的概念。如果熟悉汇编,其实大可不必被这些听起来高大上的理念吓到,一个forloop在汇编层面代码也不会长得可怕,直接读读编译后的代码就可以直观研究这样的问题了~展开共 1 条评论12
- 不的2019-10-23老师,为啥要设计成区分调用者、被调用者保护的寄存器,统一被调用者或者调用者保护,有啥问题么
作者回复: 这里有一个效率的问题。 如果全部都是调用者保护,那么其实你调用的对象根本不会破坏你的寄存器,你也要保护起来,那就增加了成本,对于逻辑比较简单的被调用者,是用不到几个寄存器的。 如果全部都是被调用者保护,也是一样的逻辑。如果调用者用了很少几个寄存器,被调用者却要保护很多,也不划算。 所以最优的方法,其实是比较中庸主义的。我觉得这可以用概率的方法做比较严谨的证明。
11 - 有学识的兔子2020-06-16一直想着学习逆向工程,奈何汇编理解不足 学习摸不着头脑,老师的课程给我带来了启发。
作者回复: Great! 计算机科学里很多知识都是通着的,可以触类旁通!
3 - 李圣悦2020-06-08确实汇编并不难,最近一直在查内核宕机问题,阅读内涵汇编代码没什么阻碍。难就难在如何恢复c代码…
作者回复: 感谢分享心得!
1 - 青南2022-03-30老师,请教一下汇编里面的“寄存器”是物理意义上的CPU里面的寄存器还是逻辑意义上的寄存器?如果是物理意义上的,那么,为什么多个程序同时操作同一个寄存器的时候,他们的数据不会冲突呢?因为操作系统里面肯定上有很多程序在运行的,如果A程序现在mov一个数字到rsp寄存器里面, 而B程序此时也mov了一个数字到rsp寄存器里面,那么当a程序从rsp寄存器里面读取数据的时候,不就读到B程序写进去的数据了吗?共 1 条评论
- coder2021-11-02mov指令的源和目的不能同时为内存地址吧?
- Geek_6562452021-10-16很有用
- David2021-06-22大学时微机原理,工作后看gcc后汇编代码发现和大学时讲的不一样了,很头疼,老师这篇文章一扫阴霾,解了我多年的疑惑。
- sugar2020-05-03我一直不太明白 汇编里的push和pop所操作的这个栈,是不是就是操作系统在内存中给当前进程分配的应用内存空间当中的栈区(旁边还有堆区 静态区 常量区 代码区)呢?还是说汇编这里边的栈是单独的一套寄存器/高速缓存 之类的硬件设备? 望解答共 2 条评论
- 牛逼中…2020-03-26这里面说到地址,指的是可执行文件中的相对地址么?肯定不是内存吧,这个时候还没加载运行,和内存无关吧?
作者回复: 汇编代码中到处会用到地址,这个地址就是内存地址。因为汇编代码中的操作数会采用直接内存访问和间接内存访问两种方式。 不过,这些地址一般都不会指定一个绝对数值,而是一个相对值,比如-8(%rsp)是比%rsp的值减8的地址。这是在栈里使用内存的标准方式。其中%rsp指向的是栈顶的地址。而栈顶的地址,是在运行期可以确定的。 至于函数(过程)和全局变量的地址,在链接的时候是可以确定的。
- Dylan2020-03-21记得汇编入门的时候是从写一个简单的斐波那契数列开始~~当时真的觉得神奇,后来和内核驱动程序打交道,看多了大神们写的汇编,感觉好像也没那么难
作者回复: 你说的是对的。并没有太难。 如有可能,大家都加强一下汇编的素养。这样,在思考某些技术问题的时候,因为知道最底层的机制,会思考得比较彻底。
共 2 条评论 - 宝鹏2020-02-26老师,as工具生成的文件不能直接运行,as hello.s -o hello,生成的hello并不是可执行文件
作者回复: 没错!是我的疏忽。在其他章节里是先生成.o,然后再用gcc链接一下。这里疏忽了。我已经把文稿改了!
共 2 条评论 - 好雨知时节2020-02-23宫老师,想问下:文中在示例中提到: 8(%rbp),是比 %rbp 寄存器的值加 8。 //想知道这里的%rbp 寄存器中存储的内容一个地址还是具体的数据值?8(%rbp) 的含义是:%rbp 寄存器中存储的地址加8后的新地址 还是 %rbp 寄存器中具体数据值+8 得到的具体值? -8(%rbp),是比 %rbp 寄存器的值减 8。//
作者回复: 在寄存器里存的就是一个数。至于这个数的语义是地址,还是别的数字,汇编代码是不管的。你完全可以用%rbp来做纯数学计算。因为根据现在的系统调用约定,其实不强制要求%rbp一定用来表示栈底地址,只对%rsp有要求。
- 权华2019-11-12刚刚的问题,xorl %eax, %eax,将返回值置为0。
作者回复: 对的。
- 权华2019-11-12leaq L_.str(%rip), %rdi leaq L_.str.1(%rip), %rsi xorl %eax, %eax callq _printf xorl %eax, %eax 宫老师你好,这是文章前面的汇编代码的一部分,我不明白 xorl %eax, %eax 这两行代码,它的作用是什么?为什么没有给寄存器 eax 就直接 xorl 操作了。展开
作者回复: 修昂党羽给eax赋值为0,但用xorl使用的时钟周期更少。
- 沉淀的梦想2019-10-14看了老师的回答与讲解"RIP相对寻址"的文章,但是还是不太理解:%rip存储的是下一条要执行的代码的地址,他存储值应该在不停地变,所以他和静态数据的偏移应该也在不停地变,为什么这里的偏移(L_.str)看起来是个常量啊?
作者回复: 对的。 如果你在不同的代码里调用使用L_.str(%rip),前面的L_.str对应的立即数是不同的。这个计算工作是由汇编器来算,不影响我们编程的效率。
- 峰2019-10-12感受是写了一大坨,天都黑了,还没写完。。。
作者回复: 就是搬砖的工作太多,创造性的工作太少:-)