10.1 汇编与本地代码一一对应
在各本地代码中,附带上表示其功能的英语单词缩写,如加法运算的本地代码加上add,这些缩写称为助记符,使用助记符的编程语言称为汇编语言
即使使用汇编语言编写的源代码,最终也需要转换成本地代码才能运行,负责转换工作的程序称为汇编器,转换这一处理本身称为汇编。用汇编语言编写的源代码,和本地代码是一一对应的,所以本地代码也可以转换成汇编语言的源代码,这一过程称为反汇编
10.2 通过编译器输出汇编语言的源代码
10.3 不会转换成本地代码的伪指令
汇编语言的源代码,是由转换成本地代码的指令和针对汇编器的伪指令构成的。伪指令负责把程序的构造及汇编的方法指示给汇编器,不过伪指令无法汇编转换成本地代码
由伪指令segment和ends围起来的部分,是给构成程序的命令和数据的集合体加上一个名字而得到的,称为段定义,段定义指的是命令和数据等程序的集合体的意思,一个程序由多个段定义构成
伪指令proc和endp围起来的部分,表示的是过程的范围,汇编中,这种相当于C语言的函数的形式称为过程
10.4 汇编的语法 = 操作码 + 操作数
汇编语言指令的语法结构是 操作码+操作数。操作码表示的是指令动作,操作数表示的是指令对象
本地代码加载到内存后才能运行,内存中存储着构成本地代码的指令和数据。程序运行时,CPU会从程序中把指令和数据读出,然后将其存储在CPU内部的寄存器中进行处理
寄存器并不仅有存储指令和数据的功能,也有运算的功能。寄存器的名称会通过汇编语言的源代码指定给操作数,CPU内部也有程序员无法直接操作的寄存器
10.5 最常用的mov指令
指令中最常使用的是对寄存器和内存进行数据存储的mov指令。mov指令的两个操作数,分别用来指定数据的存储地和读出源,如果指定了没有用方括号围起来的内容,就表示对改值进行处理,如果指定了用方括号围起来的内容,方括号中的值就会被解释为内存地址,然后对该内存地址对应的值进行读写
如: mov ebp,esp :esp中的值被直接存储到了ebp寄存器中
如:mov eax, dword ptr [ebp + 8]:ebp寄存器的值加8后被解释为内存地址,经过dword ptr后,eax存储了ebp+8这个内存地址中的值
dword ptr 表示的是从指定的内存地址读取出4字节数据
10.6 对栈进行push和pop
程序运行时,会在内存上申请分配一个称为栈的数据空间,栈使用push和pop操作数据
push指令运行后,操作数中指定的值就会被自动push入栈,pop指令运行后,最后存储在栈中的值就会被pop到指定的操作数中出栈
push指令和pop指令中只有一个操作数,该操作数表示的是‘push以及pop什么’,push和pop操作后,esp寄存器的值会自动更新
10.7 函数调用机制
第5行执行call指令时,把程序流程跳转到了操作数中指定的AddNum函数所在的内存地址处。AddNum函数处理完毕后,程序流程需要返回到第6行。
call指令运行后,call指令的下一行的内存地址会自动push入栈,该值会在AddNum函数处理的最后通过ret指令pop出栈,然后程序流程返回到第6行
第6行会把栈中存储的两个参数进行销毁处理,即栈清理处理。虽然通过两次pop也可达成同样的目的,但采用esp+8的方式更有效率
10.8 函数内部的处理
函数的参数是通过栈来传递,返回值是通过寄存器来返回
AddNum函数的具体操作:
ebp寄存器的值在第1行入栈,第5行出栈,是为了把函数中用到的ebp寄存器的内容,恢复到函数调用前的状态
第3行指定栈中存储的第一个参数123,并将其读出到eax寄存器中,第4行将参数456与eax中的123相加并存储到eax中
第6行ret指令运行后,函数返回目的地的内存地址会自动出栈,程序流程回到函数调用后
10.9 始终确保全局变量的内存空间
C中,在函数外部定义的变量称为全局变量,在函数内部定义的变量称为局部变量,全局变量可以参阅源代码的任意部分,而局部变量只能在定义该变量的函数内进行参阅
10.10 临时确保局部变量用的内存空间
局部变量是临时保存在寄存器和栈中的,函数内利用的栈,在函数处理完后会恢复到初始状态,因此局部变量的值也就销毁了,而寄存器也可能会用于其它目的
局部变量只是函数处理运行期间临时存储在寄存器和栈上的
10.11 循环处理的实现方法
C中for循环对应的汇编代码:
C语言的for语句通过在括号中指定循环计数器的初始值(i=0),循环的继续条件(i<10),循环计数器的更新(i++)这3种形式来进行循环处理的
而在汇编语言的源代码中,循环是通过比较指令(cmp)和跳转指令(jl)来实现的
10.12 条件分支的实现方法
条件分支的实现方法同循环处理的方法类似,使用的也是cmp指令和跳转指令
使用了3种跳转指令,分别是比较结果小时跳转的jle(jump on less or equal),大时跳转的jge,不管结果怎样都跳转的jmp
在汇编语言中,如果不使用相当于C的goto语句的jmp指令,就无法实现分支和循环
10.13 了解程序运行方式的必要性
使用两个函数对100翻倍处理:
上面的程序如果使用多线程处理时,同时调用了MyFunc1和MyFunc2函数,但结果有可能不是我们需要的400,而是200
线程:是操作系统分配给CPU的最小运行单位,源代码的一个函数就相当于一个线程,多线程处理指的是在一个程序中同时运行多个函数的意思
为了避免该bug,可以采用禁止线程切换的锁定方法,通过锁定,在特定范围内处理完成之前,处理不会被切换到其它函数中