前言
一个C/C++文件要经过预处理(preprocessing)、编译(compilation)、汇编(assembly)和连接(linking)才能变成可执行程序
后缀名 | 语言种类 | 后期操作 |
.c | C源程序 | 预处理、编译、汇编 |
.C | C++源程序 | 预处理、编译、汇编 |
.cc | C++源程序 | 预处理、编译、汇编 |
.cxx | C++源程序 | 预处理、编译、汇编 |
.m | Objective-C源程序 | 预处理、编译、汇编 |
.i | 预处理后的C文件 | 编译、汇编 |
.ii | 预处理后的C++文件 | 编译、汇编 |
.s | 汇编语言源程序 | 汇编 |
.S | 汇编语言源程序 | 预处理、汇编 |
.h | 预处理器文件 | 通常不出现在命令行上 |
- .o: 目标文件(Object file,OBJ文件)
- .a: 归档库文件(Archive file)
总体选项
参数 | 说明 |
-c | 预处理、编译和汇编源文件,但是不做连接 |
-S | 编译后即停止,不进行汇编 |
-E | 预处理后即停止 |
-o | 制定输出文件名 |
-v | 显示制作GCC工具自身时的配置命令 |
连接器选项
参数 | 说明 |
object-file-name | 如果某些文件没有特别明确的后缀,就认为它们是OBJ文件或库文件 |
-llibrary | 连接名为library的库文件 |
-nostartfiles | 不连接启动标准启动文件,标准库文件仍然正常使用 |
-nostdlib | 不连接系统标准启动文件和标准库文件,只把制定的文件传递给连接器 |
-static | 在支持动态连接的系统上阻止连接共享库 |
-shared | 生成一个共享OBJ文件,可以和其它OBJ文件连接产生可执行文件 |
-Xlinker option | 把选项option传递给连接器。可以用来传递系统特定的连接选项,GCC无法识别这些选项。如果需要传递携带参数的选项,必须使用两次”-Xlinker”,一次传递选项,另一次传递其参数 |
-Wl, option | 把选项option传递给连接器。如果option中含有逗号,就在逗号处分割成多个选项。 |
-u | 使连接器认为取消了symbol的符号定义,从而连接库模块以取得定义 |
连接脚本
- ALIGN(align):指定对齐的要求
- AT(ldadr):指定这个段在编译出来的映像文件中的地址——加载地址。如果不使用这个选项,则加载地址等于运行地址
Objcopy
参数 | 说明 |
input-file、outfile | 如果未指定outfile文件,将生产文件覆盖为input-file |
-I bfdname | 指明源文件的格式 |
-O bfdname | 使用指定的格式来输出文件 |
-F bfdname | 同时指明源文件、目的文件的格式 |
-R sectionname | 从输出文件中删除所有名为sectionname的段 |
-S或–strip-all | 不从源文件中复制重定位信息和符号信息到目标文件中 |
-g或–strip-debug | 不从源文件中复制调试符号到目标文件中 |
Objdump
参数 | 说明 |
-b bfdname | 指定目标码格式 |
-d | 反汇编可执行段 |
-D | 反汇编所有段 |
-EB或-EL | 指定字节序 |
-f | 显示文件的整体头部摘要信息 |
-h | 显示目标文件各个段的头部摘要信息 |
-i | 显示支持的目标文件格式和CPU架构 |
-j name | 仅显示指定section的信息 |
-m machine | 指定反汇编目标文件时使用的架构 |
寄存器列表和内存单元对应关系
- 编号低的寄存器对应内存中的低地址单元
- 编号高的寄存器对应内存中的高地址单元
汇编指令的执行条件
CPSR条件标志位N、Z、C、V分别表示Negative、Zero、Cary、Overflow
条件码 | 助记符 | 含义 | CPSR中条件标志位 |
0000 | eq | 相等 | Z = 1 |
0001 | ne | 不相等 | Z = 0 |
0010 | cs | 无符号数大于/等于 | C = 1 |
0011 | cc | 无符号数小于 | C = 0 |
0100 | mi | 负数 | N = 1 |
0101 | pl | 非负数 | N = 0 |
0110 | vs | 上溢出 | V = 1 |
0111 | vc | 没有上溢出 | V = 0 |
1000 | hi | 无符号数大于 | C = 1且Z = 0 |
1001 | ls | 无符号数小于等于 | C = 0或Z = 1 |
1010 | ge | 带符号数大于等于 | N = 1,V = 1或N = 0, V = 0 |
1011 | lt | 带符号数小于 | N = 1,V = 0或N = 0, V = 1 |
1100 | gt | 带符号数大于 | Z = 0且N = V |
1101 | le | 带符号数小于/等于 | Z = 1或 N != V |
1110 | al | 无条件执行 | - |
1111 | nv | 从不执行 | - |
寄存器的使用规则
寄存器 | 别名 | 使用规则 |
r15 | pc | 程序计数器 |
r14 | lr | 连接寄存器 |
r13 | sp | 数据栈指针 |
r12 | ip | 子程序内部调用的scratch寄存器 |
r11 | v8 | ARM状态局部变量寄存器8 |
r10 | v7、sl | ARM状态局部变量寄存器7,在支持数据栈检查的ATPCS中为数据栈限制指针 |
r9 | v6、sb | ARM状态局部变量寄存器6,在支持RWPI的ATPCS中为静态基址寄存器 |
r8 | v5 | ARM状态局部变量寄存器5 |
r7 | v4、wr | ARM状态局部变量寄存器4,Thumb状态工作寄存器 |
r6 | v3 | ARM状态局部变量寄存器3 |
r5 | v2 | ARM状态局部变量寄存器2 |
r4 | v1 | ARM状态局部变量寄存器1 |
r3 | a4 | 参数/结果/scratch寄存器4 |
r2 | a3 | 参数/结果/scratch寄存器3 |
r1 | a2 | 参数/结果/scratch寄存器2 |
r0 | a1 | 参数/结果/scratch寄存器1 |
使用规则总结
- 子程序间通过寄存器r0~r3来传递参数,这时可以使用它们的别名a1~a4。被调用的子程序返回前无需恢复r0~r3的内容
- 在子程序中,使用r4~r11来保存局部变量,这时可以使用它们的别名v1~v8。如果在子程序中使用了它们的某些寄存器,子程序进入时要保存这些寄存器的值,在返回前恢复它们;对于子程序中没有使用到的寄存器,则不必进行这些操作。在Thumb程序中,通常只能使用寄存器r4~r7来保存局部变量
- 寄存器r12用作子程序间scratch寄存器,别名为ip
- 寄存器r13用作数据栈指针,别名为sp。在子程序中寄存器r13不能用作其它用途。它的值在进入、退出子程序时必须相等
- 寄存器r14称为连接寄存器,别名lr。它用于保存子程序的返回地址。如果在子程序中保存了返回地址,r14可以用作其它用途
- 寄存器r15是程序计数器,别名为pc。它不能用作其它用途
数据栈使用规则
数据栈有两个增长方向:向内存地址减小的方向增长时,称为DESCENDING栈;向内存地址增加的方向增长时,称为ASCENDING栈
所谓数据栈的增长就是移动栈指针。当栈指针指向栈顶元素(最后一个入栈的数据)时,称为Full栈;当栈指针指向栈顶元素(最后一个入栈的数据)相邻的一个空的数据单元时,称为Empty栈
综合这两个特点,数据栈可以分为以下4种:
- FD:Full Descending,满递减
- ED:Empty Descending,空递减
- FA:Full Ascending,满递增
- EA:Empty Ascending,空递增
ATPCS规定数据栈为FD类型,并且对数据栈的操作是8字节对齐的。使用stmdb/ldmia批量内存访问指令来操作FD数据栈
使用stmdb命令往数据栈中保存内容时,先递减sp指针,再保存数据;使用ldmia命令从数据栈中恢复数据时,先获取数据,再递增sp指针,sp指针总是指向栈顶元素。