程序的机器级表示:基础(续)
汇编基础(续)
将C语言转换为目标代码(机器语言)(续)
高级语言通过编译器,变为汇编语言或机器语言。汇编语言通过汇编器变成机器语言。最后经过链接器,形成最后的exe文件
链接分为静态链接和动态链接:
静态链接:直接拷贝代码
动态链接:动态执行
Windows:
静态库:*.lib
动态库:*.dll
Linux:
静态库:*.a
动态库:*.so
gcc编译器采用ATT标准的Intel汇编格式。不同的编译器可能会导致汇编语言不同
数据类型
1/2/4/8字节分别用DB,DW,DD,QD表示
汇编语言中没有集合类型(例如结构体、数组等)
底层(支持)的主要操作
1、算术操作(+、-、*、/、>>、<<)
2、逻辑操作
3、访存(比较出名的有Move指令)
从内存中读取数据,送至寄存器
把寄存器数据写往内存
4、转移控制
高级语言的跳转指令能够使用,是因为底层能够支持转移控制指令
以上四大指令,任何系统都必须支持,只有这样才能正确执行,完成一件事情
debugger重要性
debugger具有跟踪的能力,在ide中使用debugger编译出的exe比release编译出的exe要大得多。debugger主要是为了方便调试,当不再需要调试时,可以不用debugger
在多线程时,不使用debugger会导致“找不到”
汇编基础:寄存器、操作数、移动
寄存器
在CPU中的寄存器非常有限,因为会增加功耗并影响集成度
x86-64寄存器
8位机时只有四个寄存器,之后逐渐扩展。最开始时,每个寄存器都具有意义,因此都具有特别的名字。而现在除了%rsp之外,其他的都没有专门用途了(例如曾经,寄存器中加入了对字符串的操作:%rsi,目前也不专门使用了),因此新增的寄存器(图右侧)都没有专门取名
目前唯一具有特定功能的寄存器是%rsp,永远指向栈顶的地址。栈顶是小地址,栈底是大地址
IA32寄存器
如果是16位机,则只能使用寄存器右半段的部分
移动数据
ATT格式:
movq Source, Dest;
ATT格式和Intel是反过来的,Intel是Dest在前,Source在后
操作数类型:
1、立即数:常整数,类似于C中的常量,但前面加入了前缀"$"。例如:$0x400
2、寄存器:%rax等
3、地址模式(内存):(%rax)等,括号表示从地址中取来的数
地址模式中,D(Rb, Ri, S)表示Mem[Reg[Rb]+S*Reg[Ri]+D]的简写,其中,Reg[xx]表示寄存器xx的地址,Mem[yy]表示yy地址下的值
源操作数和目标操作数的关系:
无法从一个地址源操作数移动到另一个地址目标操作数(因为冯诺依曼体系结构中处理器是计算机的核心,所有指令都要通过处理器完成,而在内存到内存的过程中无处理器参与)
全局变量读取较慢,尽量使用局部变量(局部变量在寄存器中,取出来非常快)
算术运算与逻辑运算
计算地址的指令
leaq Src, Dst
将源操作数的地址送给目的操作数
在不引用内存的情况下计算地址。不同于movq,movq计算地址后,还要取那个地址上的值来赋给目的操作数,而leaq则是直接把计算出的地址赋给目的操作数(没有取那个地址上的值)
理解:寄存器存的是地址,例如在movq时,(%rax)是取了寄存器%rax存储的地址/上的值,而leaq时的(%rax)则是直接把%rax存储的地址给拿了出来当做一个值来用
例如:leaq (%rdi, %rdi, 2), %rax,等价于t = x+x*2
算术运算
算术运算分为加载有效地址、单操作数运算、双操作数运算和移位
程序的机器级表示:控制
特殊处理器(%rip:指令指针):在Intel中叫ip,在其他地方叫PC
处理器状态
除了整数寄存器外,还有一组单个位的条件码寄存器,用于描述最近的算术或逻辑操作的属性,可以据此实现条件分支命令的执行
最常用的四个条件码:CF、ZF、SF、OF
CF(Carry Flag):进位标志:最近的操作是否使最高位产生了进位(“无符号型”数的溢出)
ZF(Zero Flag):零标志:最近的操作是否得出结果0
SF(Sign Flag):符号标志:最近的操作是否得到负数结果
OF(Overflow Flag):溢出标志:最近的操作是否导致一个补码溢出(正溢出或负溢出)
运算时会视情况更新四个标签,例如:在运行t = a+b时:
如果最高位进位(“无符号型”数的溢出),则CF=1
如果t==0,则ZF=1
如果t<0,则SF=1
如果发生了符号溢出,即(a>0 && b>0 && t<0) || (a<0 && b<0 && t>=0),则OF=1
在leaq指令运行时,不会更新这四种状态,因为leaq是用地址进行计算的
cmp指令
cmpq Src2, Src1
比较两数的大小
会像计算Src1-Src2那样,只更新条件码,不更新目的寄存器
在cmpq b, a中:
当两数为无符号类型时,如果发生了溢出,则CF=1
如果a==b,则ZF=1
如果(a-b) < 0,则SF=1
如果(a-b)发生符号溢出,则OF=1
test指令
testq Src2, Src1
相当于计算Src1 & Src2,更新状态的规则同上
SetX指令
用于设置寄存器的最低字节(只更新最低字节,其余七个字节不动)
例如:
cmp $eax,%edx
setb %cl
表示先比较$eax, %edx的大小,更新flag寄存器中的值。再根据flag来判断的结果,将%cl置0或1
常见的Set-指令:
表中Condition是flag的表达式,使用这些指令时,会根据这些表达式来更新相应的寄存器(值为0或1)。例如:在使用指令setl %xxx时,会根据(SF^OF)的值,来更新相应的寄存器%xxx
条件分支指令
跳转
jX指令
跳转至代码的其他部分的条件码