
一、汇编语言是什么?00000011。编译器的作用,就是将高级语言写好的程序,翻译成一条条操作码。

00000011写成汇编语言就是 ADD。只要还原成二进制,汇编语言就可以被 CPU 直接执行,所以它是最底层的低级语言。二、来历


[list=none]
[*]EAX[/*]
[*]EBX[/*]
[*]ECX[/*]
[*]EDX[/*]
[*]EDI[/*]
[*]ESI[/*]
[*]EBP[/*]
[*]ESP[/*]
[/list]

0x1000到
0x8000,起始地址是较小的那个地址,结束地址是较大的那个地址。

malloc命令),系统就会从预先分配好的那段内存之中,划出一部分给用户,具体规则是从起始地址开始划分(实际上,起始地址会有一段静态数据,这里忽略)。举例来说,用户要求得到10个字节内存,那么从起始地址
0x1000开始给他分配,一直分配到地址
0x100A,如果再要求得到22个字节,那么就分配到
0x1020。


[code][/code]
int main() {
int a = 2;
int b = 3;
}
main函数时,会为它在内存里面建立一个帧(frame),所有
main的内部变量(比如
a和
b)都保存在这个帧里面。
main函数执行结束后,该帧就会被回收,释放所有的内部变量,不再占用空间。

[code][/code]
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。一般来说,调用栈有多少层,就有多少帧。

add_a_and_b运行结束,它的帧就会被回收,系统会回到函数
main刚才中断执行的地方,继续往下执行。通过这种机制,就实现了函数的层层调用,并且每一层都能使用自己的本地变量。


0x8000,第一帧假定是16字节,那么下一次分配的地址就会从
0x7FF0开始;第二帧假定需要64字节,那么地址就会移动到
0x7FB0。
七、CPU 指令7.1 一个实例example.c。
[code][/code]
int add_a_and_b(int a, int b) {
return a + b;
}
int main() {
return add_a_and_b(2, 3);
}
[code][/code]
$ gcc -S example.c
example.s,里面就是汇编语言,包含了几十行指令。这么说吧,一个高级语言的简单操作,底层可能由几个,甚至几十个 CPU 指令构成。CPU 依次执行这些指令,完成这一步操作。
example.s经过简化以后,大概是下面的样子。
[code][/code]
_add_a_and_b:
push %ebx
mov %eax, [%esp+8]
mov %ebx, [%esp+12]
add %eax, %ebx
pop %ebx
ret
_main:
push 3
push 2
call _add_a_and_b
add %esp, 8
ret
add_a_and_b和
main,对应两个标签
_add_a_and_b和
_main。每个标签里面是该函数所转成的 CPU 运行流程。
[code][/code]
push %ebx
push是 CPU 指令,
%ebx是该指令要用到的运算子。一个 CPU 指令可以有零个到多个运算子。
_main标签开始执行,这时会在 Stack 上为
main建立一个帧,并将 Stack 所指向的地址,写入 ESP 寄存器。后面如果有数据要写入
main这个帧,就会写在 ESP 寄存器所保存的地址。
[code][/code]
push 3
push指令用于将运算子放入 Stack,这里就是将
3写入
main这个帧。
push指令其实有一个前置操作。它会先取出 ESP 寄存器里面的地址,将其减去4个字节,然后将新地址写入 ESP 寄存器。使用减法是因为 Stack 从高位向低位发展,4个字节则是因为
3的类型是
int,占用4个字节。得到新地址以后, 3 就会写入这个地址开始的四个字节。
[code][/code]
push 2
push指令将
2写入
main这个帧,位置紧贴着前面写入的
3。这时,ESP 寄存器会再减去 4个字节(累计减去8)。
7.3 call 指令call指令用来调用函数。
[code][/code]
call _add_a_and_b
add_a_and_b函数。这时,程序就会去找
_add_a_and_b标签,并为该函数建立一个新的帧。
_add_a_and_b的代码。
[code][/code]
push %ebx
_add_a_and_b这个帧。这是因为后面要用到这个寄存器,就先把里面的值取出来,用完后再写回去。
push指令会再将 ESP 寄存器里面的地址减去4个字节(累计减去12)。7.4 mov 指令
mov指令用于将一个值写入某个寄存器。
[code][/code]
mov %eax, [%esp+8]
2,再将
2写入 EAX 寄存器。
[code][/code]
mov %ebx, [%esp+12]
3,将其写入 EBX 寄存器。7.5 add 指令
add指令用于将两个运算子相加,并将结果写入第一个运算子。
[code][/code]
add %eax, %ebx
pop指令用于取出 Stack 最近一个写入的值(即最低位地址的值),并将这个值写入运算子指定的位置。
[code][/code]
pop %ebx
pop指令还会将 ESP 寄存器里面的地址加4,即回收4个字节。7.7 ret 指令
ret指令用于终止当前函数的执行,将运行权交还给上层函数。也就是,当前函数的帧将被回收。
[code][/code]
ret
add_a_and_b函数终止执行,系统就回到刚才
main函数中断的地方,继续往下执行。
[code][/code]
add %esp, 8
pop操作已经回收了4个字节,这里再回收8个字节,等于全部回收。
[code][/code]
ret
main函数运行结束,
ret指令退出程序执行。八、参考链接
本篇文章为 @ 21CTO 创作并授权 21CTO 发布,未经许可,请勿转载。
内容授权事宜请您联系 webmaster@21cto.com或关注 21CTO 公众号。
该文观点仅代表作者本人,21CTO 平台仅提供信息存储空间服务。
请扫描二维码,使用微信支付哦。