目录
一. x86汇遍语言基础(Intel格式)
二. AT&T格式汇编语言
三. 程序的机器级代码表示
(1)选择语句
(2)循环语句
(3)函数调用
1.函数调用命令
2.栈帧及其访问
3.栈帧的切换
4.函数传参和返回值
四. CISC和RISC
一. x86汇遍语言基础(Intel格式)
x86架构的CPU有哪些寄存器?
对于前面的通用寄存器,还可以指定使用16位或8位:
常用的x86汇编指令:
算术运算指令:
d和s的解释:destination:目的地(d目的操作数),source:来源地(s源操作数)。因为最终运算结果要送回d,所以目的操作数d不可以是常量。此外x86不允许两个操作数同时来自于主存。
除法的解释:32位的被除数除以32位的除数,在计算前会对被除数进行扩展,把被除数扩展到64位,低32位存入eax,高32位存入edx。
逻辑运算指令:
其他:
用于实现分支结构、循环结构的指令: cmp、test、 jmp、jxxx
用于实现函数调用的指令:push、pop、call、ret
用于实现数据转移的指令:mov
二. AT&T格式汇编语言
三. 程序的机器级代码表示
408考试要求:
只需关注x86汇编语言;若考察其他汇编语言题目会详细注释
题目给出某段简单程序的C语言、汇编语言、机器语言表示。能结合C语言看懂汇编语言的关键语句(看懂常见指令、选择结构、循环结构、函数调用)
汇编语言、机器语言一一对应,要能结合汇编语言分析机器语言指令的格式、寻址方式
不会考:将C语言人工翻译为汇编语言或机器语言
注: Intel x86处理器中程序计数器PC ( Program Counter)通常被称为IP( lnstruction Pointer)
(1)选择语句
jmp <地址> #PC无条件转移至<地址>:
jmp 128 #<地址>可以用常数给出
jmp eax #<地址>可以来自于寄存器
jmp [999] #<地址>可以来自于主存
也可以用“标号”锚定位置,这样不用每次更改地址:
条件转移指令:
示例:
注意:写汇编语言代码时,一般会以函数名作为“标号”,标注该函数指令的起始地址。
扩展:cmp命令的底层原理,本质上是做减法运算。
(2)循环语句
用条件转移指令实现循环:
用loop指令实现循环(注意:循环计数器必须使用ECX寄存器):
(3)函数调用
1.函数调用命令
函数调用指令:call <函数名>
函数返回指令:ret
call指令的作用:①将IP(其实就是程序计数器PC)旧值压栈保存(保存在函数的栈帧顶部)
②设置IP新值,无条件转移至被调用函数的第一条指令
ret指令的作用:从函数的栈帧顶部找到IP旧值,将其出栈并恢复IP寄存器。
2.栈帧及其访问
函数的栈帧(Stack Frame):保存函数大括号内定义的局部变量、保存函数调用相关的信息。
下面的图示,栈顶在下,栈底在上,上面是高地址,下面是低地址。
访问栈帧数据有两种方法:(1)push和pop指令;(2)mov指令
标记栈帧范围:x86中有EBP、ESP寄存器。EBP是堆栈基指针,ESP是堆栈顶指针。EBP:指向当前栈帧的“底部,ESP:指向当前栈帧的“顶部”。对栈帧内数据的访问,都是基于ebp、esp进行的。
1.使用push和pop指令(eax寄存器里的初始值是211)
执行完上述指令后,栈内内容如下,eax内容变为666:
注意:x86系统中,默认以4字节为栈的操作单位。
2.mov指令
可以用mov指令,结合esp、 ebp指针访问栈帧数据
可以用减法/加法指令,即 sub/add修改栈顶指针esp的值。
3.栈帧的切换
在call add后,call会首先把IP(PC)的旧值压栈保存,然后设置IP(PC)新值,无条件转移至被调用函数的第一条指令。在被调用的函数体中,开头一般是这两条命令:
push ebp # 保存上一层函数的栈帧基址(ebp旧值)
mov ebp,esp # 设置当前函数的栈帧基址(ebp新值)
push ebp会把上一层函数的栈帧基址压入栈中,成为本层函数栈帧的底,同时esp向下移动;然后执行mov指令,把ebp也指向本层函数栈帧的底,到此一个新的函数栈帧形成,可以往里继续压入一些数据。以上两行代码可以用enter等价表示:
当函数返回时,切换栈帧的方式如下:
mov esp, ebp # 让esp指向当前栈帧的底部
pop ebp # 将esp所指元素出栈,写入寄存器ebp
mov指令可以把esp和ebp都指向栈帧的底,然后弹出栈中元素至ebp,这时ebp就可以恢复指向上一层函数栈的栈底,esp+,指向IP旧值,最后执行ret指令,从函数的栈帧顶部找到IP旧值,将其出栈并恢复IP寄存器。上面两行可以用leave等价表示:
4.函数传参和返回值
栈帧里包含的内容:gcc编译器将每个栈帧大小设置为16B的整数倍(当前函数的栈帧除外),因此栈帧内可能出现空闲未使用的区域。
- 通常将局部变量集中存储在栈帧底部区域
- 通常将调用参数集中存储在栈帧顶部区域
- 栈帧最底部一定是上一层栈帧基址(ebp旧值)
- 栈帧最顶部一定是返回地址(当前函数的栈帧除外)
使用[ebp-8],[ebp-12]等读取,返回参数。
此外,调用其他函数前,如果有必要,可将某些寄存器(如:eax、 edx、ecx)的值入栈保存,防止中间结果被破坏。如果这些寄存器值不是运算的中间结果,则可以不保存。(这部分内容一般在空区域后,调用参数前)
四. CISC和RISC
可以把CISC理解为“库函数+C语言”,RISC理解为C语言。
CISC:Complex Instruction Set Computer
设计思路:一条指令完成一个复杂的基本功能。
代表:x86架构,主要用于笔记本、台式机等。
RISC: Reduced lnstruction Set Computer
设计思路:一条指令完成一个基本“动作”;多条指令组合完成一个复杂的基本功能。
代表:ARM架构,主要用于手机、平板等。
80-20规律:典型程序中80%的语句仅仅使用处理机中20%的指令。