2.x86
2.1.运算、逻辑类指令
有符号数和无符号数在形式上可能有差别,但是都会带有每个指令的主体部分
2.2.分支结构
1.CMP交由ALU运行,并由ALU产生标志位,再通过标志位进行判断和相应操作
2.偏移量的单位可能是字节,也可能是指令字长(往前/往后跳多少条指令,RISC):
jmp -10; PC = AE86
①以字节为单位:PC = AE86 - 10
②以指令字长为单位(RISC,定长指令4B):PC = AE86 - 10 * 4
2.3.循环结构
1.条件转移指令:先实现循环结构主体相对应的机器指令,再在这些指令后,添加cmp和jxx指令,即满足条件的话,则回到循环主体指令对应的开始处
2.loop:循环到label位置
2.4.函数调用/返回
1.(1)call(调用一个函数):
①压入调用函数的所使用的参数(程序员通过push指令手动实现)
②执行Call指令
③压入返回地址,即当前的PC值(CALL指令实现)(存放返回地址的地址是a栈的栈顶)
④压入a的栈底指针,即ebp的值(程序员通过push指令手动实现)(存放a的栈底地址的地址是b栈的栈底)(每个函数刚开始执行的时都要执行push ebp,即将上一层的栈底指针压入栈中)
(该函数刚开始便执行push ebp,将上一层的栈底地址压入函数调用栈中)
⑤修改ESP寄存器的值为b函数的栈顶,即ESP指向④;修改EBP为b函数的栈底,即EBP指向③(CALL指令实现)(esp寄存器和ebp寄存器分别只有一个,故执行b函数时保存的是b函数的相关值)
(存放返回地址的地址属于a栈,存放a的栈底地址的地址属于b栈)
(2)ret(这次调用返回):CALL的逆向操作
①通过逐一弹出函调用栈数的栈顶元素的方式(pop)回收该函数的函数调用栈空间,每次esp弹出后都会 - 1
②当弹出到ebp所指向的位置是该函数的栈底(该处存放上一层函数的栈底地址)时(ebp和esp此时都指向该栈的栈底)
A.弹出栈顶元素,并修改ebp寄存器为上一层函数的栈底地址(ebp指向上一层函数的栈底),esp弹出自动 - 1,即esp指向上一层函数的栈顶元素(该地址存放的是上一层函数的返回地址,即调用函数结束后,返回上一层函数继续执行的下一条指令的地址)
B.继续弹出栈顶元素,并将PC的值修改为上层函数的返回地址
2.设main函数调用a函数,a函数调用b函数:则函数调用栈中
①最底层为main函数
②a函数在main函数的上一层:存放a函数中的变量、返回地址(b函数执行结束,返回a函数后执行的下一条指令的地址;执行结束后,将PC的值修改为该地址)
③b函数在a函数的上一层:存放b函数中的变量、a函数的栈底地址的地址(函数b执行结束后
,ebp的值修改为该地址,作用是确定每个函数的地址范围)
即每个被调用函数需要记录:
①其函数结束,返回上一层函数后应该继续执行上一层函数的哪个指令,即返回地址
②上一层函数的栈底地址(存放该地址的地址作为自身函数的栈底,EBP指向该处)
3.①EBP(栈底指针)寄存器和ESP(栈顶指针)寄存器分别只有一个(函数调用和函数返回时ebp和esp都要进行修改)
②b函数在执行过程中,EBP指向b函数的栈底,ESP指向b函数的栈顶
③b函数执行结束,通过逐一弹出b函数的栈顶元素的方式(pop)回收b函数的函数调用栈空间(b函数的ESP在执行弹出栈顶元素操作时不断减1),直到b函数的ESP和EBP指向同一位置时,即到了b函数的栈底,且该位置存放a函数的栈底地址(a函数调用的b函数,即a函数是b函数的上一层函数);然后,EBP改为指向a栈底将EBP寄存器的值从b的栈底地址改为a的栈底地址),ESP则通过继续弹出栈顶元素的方式,就会从指向b的栈底 - 1,即ESP指向a的栈顶(a的栈顶此时存放的是返回地址,即b函数结束,返回a函数时应该继续执行的下条指令),将a的栈顶元素弹出并放入PC,即将下一条要执行的a函数的指令(即调用b函数指令的下一条指令)放入PC中
4.函数中声明一个局部变量的机器级指令实现是通过一个push指令将该变量压入栈中
5.函数使用当前函数的局部变量和上一层函数调用时使用的参数是通过对EBP进行偏移实现的:
设每个地址的长度是4B,且高地址部分存放的是a栈,低地址部分存放的是b栈,函数a调用函数b,则在函数b的执行期间
①ebp为b的栈底,存放的是a的栈底的地址
②ebp + 4为返回地址,存放的是函数b执行结束返回函数a时,应该执行函数a的下一条指令的地址
③ebp + 8开始为函数a调用函数b时传入的参数,属于a栈(+8即a2,+12即a1)
④ebp - 4开始为函数b的局部变量,属于b栈(-4即b1,-8即b2)
3.真题
①第1行(push):每次循环调用该函数时,第一件事先将ebp寄存器的值(上一层函数的栈底地址)压入栈中
②第11行(cmp):将存放在ebp + 8地址的值(上一层函数调用该函数时所使用的的参数)和1对比
③第12行(jle):当第11行的cmp结果为真时,跳转到虚拟地址为0040 1035处执行,否则按顺序执行下一条指令
④第13行(mov):将存放在ebp + 8地址的值(上一层函数调用该函数时所使用的的参数)放入eax寄存器中
⑤第14行(sub):对eax寄存器的值减1(实现n-1)
⑥第15行(push):将eax寄存器的值压入函数调用栈中(调用函数时,传入该调用函数的参数要压入本函数的函数调用栈中)
⑦第16行(call):调用CALL函数,跳转虚拟地址为0040 1000H处执行指令(实现循环)
(1)f(10)将调用10次函数f1;执行第16行call指令时将会递归调用f1
(2)第12行jle是条件转移指令;第16行call、第20行jmp、第30行ret一定会使程序跳转执行
(3)第16行的CALL指令占用5个字节(E8 D6 FF FF FF),且该指令的地址为0040 1025H,则下一条指令的地址为0040 1025 + 5 = 0040 102AH
相对寻址方式是以PC寄存器中的值为基准进行偏移,PC中存放的是下一条指令的地址,则执行CALL指令时,PC中的内容是0040 102AH,要将下一条执行的指令回到f1处,则需将PC修改为0040 1000H,即偏移量0040 1000H - 0040 102A = -2AH
偏移量-2AH用补码表示为FF FF FF D6,而CALL指令的后4B为D6 FF FF FF,故采用小端存储
(4)f(13)的真值超过了32bit的int类型所能表示的范围,需要将返回值类型改为double
(5)当高n + 1位不全为0或者不全为1时,乘法就发生溢出;发生溢出时转为异常处理,则需要添加陷入指令