Sunday, December 5, 2021

[coding] assembly language fundamentals


文字指令翻译成二进制,这个步骤就称为 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新建一个帧,用来储存它的内部变量。也就是说,此时同时存在两个帧:mainadd_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