GCC 工具及相关介绍¶
gcc常用命令¶
编译目标控制
-E Preprocess only; do not compile, assemble or link.
-S Compile only; do not assemble or link.
-c Compile and assemble, but do not link.
-o <file> Place the output into <file>.
编译单个汇编文件
汇编源码test.S
.section ".text.init"
.globl _start
_start:
la x1, data1
add x2, x1, x3
sub x1, x3, x4
mul x5, x1, x3
.section ".data"
data1: .word 0x00000001
data2: .word 0x00000002
链接脚本link.ld如下
OUTPUT_ARCH( "riscv" )
ENTRY(_start)
SECTIONS
{
. = 0x00000000;
.text.init : { *(.text.init) }
.text : { *(.text) }
.data : { *(.data) }
.bss : { *(.bss) }
}
编译命令
riscv32-unknown-elf-gcc -march=rv32imafv -nostartfiles -T./link.ld test.S -o test
编译优化
-O0 不做任何优化
-O1 主要对代码的分支,表达式,常量来进行优化
-O2 加入了寄存器的使用,load和store的频率会降低
-O3 额外的启用了 -finline-functions 等高等级优化,不建议使用
-Os 在O2基础上启用空间优化,优化代码尺寸
编译动态链接库
#需要注意的是动态链接库的名字libtest.so中的lib是必须加的
gcc test.c -shared -fPIC -o libtest.so
编译静态链接库
gcc -c test.c
ar -crv libtest.a test.o
调试
-g 保留调试信息,会使elf中增加若干个debug段
-W 使能编译警告,后面接各种警告类型,使用-Wall可以开启所有警告
-w 关闭编译警告
riscv-GAS扩展汇编指令的步骤¶
首先不得不说gas里面各个架构下面的代码都不太一样,本以为会像内核的设备驱动一样,能明显的看出来各个类似驱动是兄弟关系。但实际上gas下面的各个架构的代码差异还比较大,即便是定义指令的结构体也是各个架构自己定义的,具体原因不太清楚,只是扫了一眼,有时间应该再细致一些看看(大概率是以后不会再看了)
以下步骤基于riscv官方提供的工具链源码
https://github.com/riscv/riscv-gnu-toolchain
step1-修改opcode头文件¶
//修改riscv-binutils/include/opcode/riscv-opc.h文件,在文件中增加对新指令的注册
// MATCH即指令编码本身
// MASK即匹配指令时需要检查的每一个bit,需要检查的置1(不管编码本身是1还是0,需要检查则置1)
// 举例
// name |31 26 |25 |24 20 |19 15 |14 |13 12 |11 7 |6 0
// xxxxx.xx |000001 |0 |rs2 |rs1 |0 |00 |rd |1111011
#define MATCH_XXXXX_XX 0x5e002057
#define MASK_XXXXX_XX 0xfe00707f
DECLARE_INSN(xxxxx_xx, MATCH_XXXXX_XX, MASK_XXXXX_XX)
step2-修改opcode源文件¶
// 修改riscv-binutils/opcodes/riscv-opc.c
// 在riscv_opcodes数组中添加新指令
{"xxxxx.xx", 0, {"V", 0}, "Vd,Vt,Vs", MATCH_XXXXX_XX, MASK_XXXXX_XX, match_opcode, 0 }
step3-修改参数校验函数¶
// 修改gas/config/tc-riscv.c
// 在validate_riscv_insn中增加相关参数的校验
// riscv 在validate_riscv_insn实现了指令参数的校验
// 例如指令: vslideup.vi
// 注册参数列表为 "Vd,Vt,Vi,Vm"
// [31:26] |[25] |[24:20] |[19:15] |[14:12] |[11:7] |[6:0]
// 001110 |vm |vs2 |imm |011 |vd |1010111
// 函数中遍历指令的参数列表,其中宏定义如下
// #define OP_MASK_RD 0x1f
// #define OP_SH_RD 7
// #define OP_MASK_VM 0x1
// #define OP_SH_VM 25
// #define OP_MASK_RS2 0x1f
// #define OP_SH_RS2 20
// #define OP_MASK_VI 0x1f
// #define OP_SH_VI 15
// 这样循环如下代码便能够遍历所有参数是否设置合法
case 'V':
switch ( c = *p++) {
case 'd':
USE_BITS (OP_MASK_RD, OP_SH_RD);
break;
case 'm':
USE_BITS (OP_MASK_VM, OP_SH_VM);
break;
case 't':
USE_BITS (OP_MASK_RS2, OP_SH_RS2);
break;
case 'i':
USE_BITS (OP_MASK_VI, OP_SH_VI);
break;
}
break;
step4-添加指令组装处理¶
// 修改gas/config/tc-riscv.c
// 在riscv_ip函数中增加指令组装处理
case 'i':
if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
|| imm_expr->X_op != O_constant
|| imm_expr->X_add_number < 0
|| imm_expr->X_add_number >= 32)
{
as_bad (_("bad value for uimm[19:15] field, "
"value must be0...32"));
break;
}
INSERT_OPERAND (VI, *ip, imm_expr->X_add_number);
imm_expr->X_op = O_absent;
s = expr_end;
continue;
step5-添加指令打印信息¶
// 修改opcodes/riscv-dis.c文件
// 在该文件的print_insn_args函数中新增指令打印信息
case 'i':
print (info->stream, "%d", EXTRACT_OPERAND(VI, l));
break;
step6-同步修改gdb相关文件¶
将riscv-opc.h和riscv-opc.c的修改同步到riscv-gdb目录的对应目录下的riscv-opc.h和riscv-opc.c文件中
strip工具¶
strip工具可以除去目标elf文件中的行号信息、重定位信息、调试段、注释段、文件头以及所有或部分符号表,减少elf对象文件的大小
也有人说是处理COFF文件,这个COFF和elf到底有什么区别?具体说不好,但是感觉结构很相似,strip的help信息中描述为Removes symbols and sections from files,也没有具体说明,这里先暂且理解为处理ELF文件
-R --remove-section=<name> Also remove section <name> from the output
-g -S -d --strip-debug Remove all debugging symbols & sections
readelf工具¶
Display information about the contents of ELF format files
关于elf文件格式,可以参考之前的总结**arm-linux-kmodule-load.md**中的描述
// 查看节区头部表
-S --section-headers Display the sections' header
// 查看文件头
-h --file-header Display the ELF file header
objcopy工具¶
Usage: objcopy [option(s)] in-file [out-file] Copies a binary file, possibly transforming it in the process
objcopy可以copy文件内容到另一个文件中,过程中可以进行格式转换
objcopy将elf转换为bin¶
# -O --output-target <bfdname> Create an output file in format <bfdname>
# -R --remove-section <name> Remove section <name> from the output
# -S --strip-all Remove all symbol and relocation information
objcopy -O binary -R .comment -S test.elf test.bin
objcopy仅输出某一个节区¶
-j --only-section <name> Only copy section <name> into the output
objcopy -O binary -j .data test.elf test.bin
指定空白区填充值¶
# --gap-fill <val> Fill gaps between sections with <val>
objcopy -O binary --gap-fill 0xff test.elf test.bin