程序编码
在编译时指定'-Og'选项让GCC产生符合原始程序结构的机器代码
机器级代码
对C语言隐藏, 但对汇编代码可见的:
- 程序计数器
- 整数寄存器文件
- 条件码寄存器
- 向量寄存器
输出c源码的机器表示
gcc -Og -S mstore.c
机器代码与反汇编的特性:
x86-64的指令长度从1-15字节不等
指令设计的格式, 从某个给定位置, 能将字节唯一解码成机器指令
哈夫曼编码
反汇编无需访问源代码
反汇编与gcc的命名规则有些许差别
比如movq的q在反汇编中会被省略
关于格式的注解
.file "mstore.c"
.text
.globl mulstore
.type mulstore, @function
mulstore:
.LFB0:
.cfi_startproc
pushq %rbx
.cfi_def_cfa_offset 16
.cfi_offset 3, -16
movq %rdx, %rbx
call mult2
movq %rax, (%rbx)
popq %rbx
.cfi_def_cfa_offset 8
ret
.cfi_endproc
.LFE0:
.size mulstore, .-mulstore
.ident "GCC: (GNU) 4.8.5 20150623 (Red Hat 4.8.5-36)"
.section .note.GNU-stack,"",@progbits
以上是gcc完整生成的.s文件
所有. 开头的是伪指令, 可以忽略
ATT汇编代码格式
.file "mstore.c"
.text
.globl mulstore
.type mulstore, @function
mulstore:
.LFB0:
.cfi_startproc
pushq %rbx
.cfi_def_cfa_offset 16
.cfi_offset 3, -16
movq %rdx, %rbx
call mult2
movq %rax, (%rbx)
popq %rbx
.cfi_def_cfa_offset 8
ret
.cfi_endproc
.LFE0:
.size mulstore, .-mulstore
.ident "GCC: (GNU) 4.8.5 20150623 (Red Hat 4.8.5-36)"
.section .note.GNU-stack,"",@progbits
数据格式
16位: 字(w)
32位: 双字(l)
64位: 四字(q)
moveb: 传送字节
movew: 传送字
movel: 传送双字
moveq: 传送四字
访问信息
x86-64的CPU包含一组16个存储64位的通用目的寄存器
- 16位操作可以访问2位字节
- 32位操作可以访问4位字节
...
栈指针(%rsp)
操作数指示符
立即数: 代表常数
$后面接c语言表示法的整数
寄存器: 表示寄存器里的内容
r
a
用来表示寄存器a 用R[r
a
]表示里面的内容
内存引用: 指定内存地址里的内容 M[地址]
数据传送指令
压入栈和弹出栈数据
. 将四字压入栈
pushq S
. 将四字弹出栈
popq D
%rsp 是栈指针 %rax是返回值
算术和逻辑操作
加载有效地址
. x= y+x*4
leaq (%rdi,%rsi,4), %rax
一元和二元操作
. 从%edi中减去%esi
subl %esi, %edi
移位操作
. 将x左移四位
salq $4, %rax
特殊的算术操作
控制
条件码
- CF:进位标志
- ZF:零标志
- SF:符号标志
- OF:溢出标志
读取条件码
跳转指令
用条件控制实现分支控制
cmpq %rsi, %rdi
jg .L4
movq %rdi, %rax
subq %rsi, %rax
ret
.L4:
leaq (%rdi,%rsi), %rax
ret
对应的c代码:
if (x > y){
return x+y;
}else{
return x-y;
}
用条件传送实现条件分支
分支预测
循环
do-while
while
- guarded-do
for
switch语句
跳转表
过程
- 传递控制
- 传递数据
- 分配和释放内存
运行时栈
转移控制
保存当前程序地址,将程序计数器设置为新过程地址 返回时读取保存的地址,继续执行
数据传送
- 传递函数参数的寄存器
栈上的局部存储
寄存器中的局部存储空间
- 被调用者保存寄存器
- 调用者保存寄存器
递归过程
数组的分配和访问
基本原则
T A[N]
指针运算
&D [ i ] [ j ] = X
D
L(Ci+j)
定长数组
变长数组
异质的数据结构
都是对地址进行偏移得到的
- 结构
- 联合
- 数据对齐
在机器级程序中将控制与数据结合起来
理解指针
在计算机科学中,指针(Pointer)是编程语言中的一个对象,利用地址,它的值直接指向(points to)存在电脑存储器中另一个地方的值
GDB调试器
UNIX及UNIX-like下的调试工具
内存越界引用和缓冲区溢出
对抗缓冲区溢出攻击
- 栈随机化
- 栈破坏检测
- 限制可执行代码区域
变长帧
浮点代码
%ymm0 ~ %ymm15
浮点传送和转换操作
指令 | 源 | 目的 | 描述 |
---|---|---|---|
vmovss | $M_{32}$ | X | 传送单精度数 |
vmovss | X | $M_{32}$ | 传送单精度数 |
vmovsd | $M_{64}$ | X | 传送双精度数 |
vmovsd | X | $M_{64}$ | 传送双精度数 |
vmovaps | X | X | 传送对齐的封装好的单精度数 |
vmovapd | X | X | 传送对齐的封装好的双精度数 |
指令 | 源 | 目的 | 描述 |
---|---|---|---|
vcvttss2si | $X/M_{32}$ | $R_{32}$ | 用截断的方法把单精度数转换成整数 |
vevttsd2si | $X/M_{64}$ | $R_{32}$ | 用截断的方法把双精度数转换成整数 |
vcvttss2siq | $X/M_{32}$ | $R_{64}$ | 用截断的方法把单精度数转换成四字整数 |
vcvttsd2siq | $X/M_{64}$ | $R_{64}$ | 用截断的方法把双精度数转换成四字整数 |
指令 | 源1 | 源2 | 目的 | 描述 |
---|---|---|---|---|
vcvtsi2ss | $M_{32}/R_{32}$ | X | X | 把整数转换成单精度数 |
vcvtsi2sd | $M_{32}/R_{32}$ | X | X | 把整数转换成双精度数 |
vcvtsi2ssq | $M_{64}/R_{64}$ | X | X | 把四字整数转换成单精度数 |
vcvtsi2sdq | $M_{64}/R_{64}$ | X | X | 把四字整数转换成双精度数 |
过程中的浮点代码
使用XMM寄存器来传递浮点参数
浮点运算操作
单精度 | 双精度 | 效果 | 描述 |
---|---|---|---|
vaddss | vaddsd | D←S2+S1 | 浮点数加 |
vsubss | vsubsd | D←S2-S1 | 浮点数减 |
vmulss | vmulsd | D←S2XS1 | 浮点数乘 |
vdivss | vdivsd | D←S2/S1 | 浮点数除 |
vmaxss | vmaxsd | D←max(S2,S1) | 浮点数最大值 |
vminss | vminsd | D←min(S2,S1) | 浮点数最小值 |
sgrtss | sqrtsd | $D←\sqrt{S1}$ | 浮点数平方根 |
定义和使用浮点常数
浮点操作不能把立即数作为操作数
编译器必须为所有浮点常量初始化存储空间
在浮点代码中使用位级操作
单精度 | 双精度 | 效果 | 描述 |
---|---|---|---|
vxorps | vorpd | D←S2·S1 | 位级异或(EXCLUSIVE-OR) |
vandps | andpd | D←S2&S1 | 位级与(AND) |