快速入门汇编语言

这篇是我在先后学习了《汇编原理》、《CSAPP》第三章和《x86 data sheet》,以及经历了大量google后写出的总结性文档,用于自查和复习。若能有所助益,不胜荣幸。如有错漏,烦请不吝赐教。

1. 从C到汇编

在初学C语言时,我们都会写一个叫做hello.c的文件,通过编译(广义)这个文件(及库文件)并执行,计算机会在屏幕上显示"Hello world!"这一行字符串。但是,我们不禁发问,计算机究竟是怎样理解hello.c中的代码的?

显然,计算机不可能直接理解这么抽象的语句,它只认识高电平和低电平,也就是二进制语言(010010...)。编译器就负责把人类写的源文件“翻译”成计算机认识的二进制文件。“翻译”的牵涉到一系列工具和复杂的过程,主要是预由预处理器把源代码中的宏去掉/替换;由编译器将源代码翻译为汇编程序(这是我们的主角);由汇编器将汇编程序翻译为目标文件,目标文件已经是二进制文件了,但它还不能执行;链接器则将多个目标文件合为一个可执行文件。然后我们执行可执行文件时,计算机会将可执行文件中的二进制代码搬到内存,拆解为一系列的指令,CPU按照一定的顺序执行这些指令,就完成了对可执行文件的执行。如果这部分看不懂,你就需记住一句话,计算机并不直接执行源文件,而是执行编译完成后生成的可执行二进制文件

让我以x86_64下的hello.c为例。

这是源代码

这是二进制代码(为了显示方便,用16进制代替了)

那汇编语言是什么呢?它是源代码和二进制代码之间的桥梁,它与二进制代码一一对应,同时又具备了可读性。可以说,它就是文本化的二进制代码。在发明高级语言之前,它一直是人类使用的程序语言。让我们看看hello.c对应的汇编语言是什么样子的。

汇编语言(节选)

实际上,它与二进制代码的一一对应关系很容易看出:

左边是二进制,右边是汇编

2. x86_64平台上的汇编语言

我们知道高级语言,如C语言,的编写规则与硬件平台无关。同样一份C语言文件,在x86,x86_64, arm上(可以搭载windows,macOS,linux)发挥着相同的功能,这也正是高级语言的优势之一。然而,很不幸,汇编语言是高度定制化的,同样一份源代码在不同的平台上生成的汇编代码是不同的。这是因为汇编代码其实就是一条一条的指令,然而不同的机器上的指令集体系结构是不同的。在x86_64平台上,你需要用到它提供的它自己的复杂指令集;在arm机器上,则要用到它自身的精简指令集。打个不是很恰当的比方,可以把生成可执行的二进制文件想象成为x86_64和arm分别建造房屋,然而x86_64只提供石头,arm只提供砖块。虽然最终房屋的功能都是相同的,但是它们的外貌一定是有差别的。

我在日常学习中主要用到x86_64的平台(恰如大多数人),所以本文解决如何看懂x86_64上的汇编语言的问题。说到这儿,不得不提到x86_64平台的一些关键点。

2.1 16个64位的寄存器(而构成的寄存器文件)

16个寄存器

寄存器用途不一,但主要还是用于存储变量或地址的值。等等,看到这幅图,有没有一个疑惑:明明是16个寄存器,为何却有64个名称?

因为我们遇到的变量不一定都是64位的。以%rax为例,比如我要存储一个char类型的变量,那么我用得到的只有它的低8位,所以专门为这8位取名为%al(l代表low);同理,如果要存储short类型的变量,则要用到它的低16位,专以%ax名之;若变量是int类型的,则要用到低32位,以%eax命名。

这16个寄存器中,有的要承担特殊的任务,如果%rsp用于指向栈顶,%rax(及%eax等)要存储函数返回的值,有6个寄存器要用来保存函数的参数值,而有的则没有限制。

其它寄存器还有caller-saved(调用者保存), callee-saved(被调用者保存)的特性,这些在下文都会有详细的解释,这里只做简单的提示。

下图更加全面地展示了寄存器堆及其特点(源自CSAPP):

2.2 (虚拟)内存

先不要管“虚拟”二字,它与主题无关。内存像个仓库,不管是二进制代码本身还是部分临时变量都存储在内存中(还有部分存储在寄存器文件中),它们时刻准备着为CPU所用(访问或写入)。你可以把内存看作一块超级大的数组,所以内存的每一个单元(字节)都有自己的地址。

2.3 栈(stack)

学过数据结构的话,就会非常熟悉栈的特性:它遵循着后进先出(LIFO)的原则。而在x86-64的内存中,也有栈空间,它在程序运行时发挥着重要的作用:它储存着临时变量,担任过程调用的中转站即保存返回地址,(可能过多的)参数,为当前过程储存临时变量。

栈最重要的特征是有一个栈顶指针,这就是%rsp(16个寄存器之一)。我们知道,栈能操作的数据只有存储在栈顶的数据,所以几乎所有与栈相关的指令都离不开这根指针。栈有两个操作:压栈(push)和弹出(pop)。实际上,压栈是指,将栈顶指针往下移动若干个字节,使得栈的容量增大,再把新的数据填入栈顶。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值