程序编码
在编译时指定'-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) |