文字指令翻译成二进制,这个步骤就称为 assembling,完成这个步骤的程序就叫做 assembler。它处理的文本,自然就叫做 aseembly code。标准化以后,称为 assembly language,缩写为 asm
每一种 CPU 的机器指令都是不一样的,因此对应的汇编语言也不一样。目前最常见的 x86 汇编语言
学习汇编语言,首先必须了解两个知识点:寄存器和内存模型
寄存器 register
32位 CPU、64位 CPU 这样的名称,其实指的就是寄存器的大小。32 位 CPU 的寄存器大小就是4个字节。
内存模型
Heap
动态的内存占用请求(比如新建对象,或者使用
malloc命令)这种因为用户主动请求而划分出来的内存区域,叫做 Heap(堆)。它由起始地址开始,从低位(地址)向高位(地址)增长。Heap 的一个重要特点就是不会自动消失,必须手动释放,或者由垃圾回收机制来回收。
Stack
除了 Heap 以外,其他的内存占用叫做 Stack(栈)。简单说,Stack 是由于函数运行而临时占用的内存区域。
int main() { int a = 2; int b = 3; return add_a_and_b(a, b); }
上面代码中,main函数内部调用了add_a_and_b函数。执行到这一行的时候,系统也会为add_a_and_b新建一个帧,用来储存它的内部变量。也就是说,此时同时存在两个帧:main和add_a_and_b。一般来说,调用栈有多少层,就有多少帧。
CPU指令 Instructions
int add_a_and_b(int a, int b) { return a + b; } int main() { return add_a_and_b(2, 3); }
gcc 将这个程序转成汇编语言。
$ gcc -S example.c
上面的命令执行以后,会生成一个文本文件example.s,里面就是汇编语言,包含了几十行指令。这么说吧,一个高级语言的简单操作,底层可能由几个,甚至几十个 CPU 指令构成。CPU 依次执行这些指令,完成这一步操作。
example.s经过简化以后,大概是下面的样子。
_add_a_and_b: push %ebx mov %eax, [%esp+8] //move 将某个值写入某个寄存器 mov %ebx, [%esp+12] add %eax, %ebx //加法操作 结果写入第一个运算子 pop %ebx //取出stack最后一个操作的值 放入指定寄存器 ret //终止当前函数调用,返回上一层,清除当前call frame _main: push 3 //push指令用于将运算子放入 Stack,这里就是将3写入main这个帧 push 2 call _add_a_and_b //call 调用函数 add %esp, 8 //释放stack之前push的3, 2 (相当于 pop 2, pop 3) ret
Ref:
https://www.ruanyifeng.com/blog/2018/01/assembly-language-primer.html


No comments:
Post a Comment