首先,展示RISC-V代码段数据段的内存分布示意图。
下面是tinyriscv项目sdk文件路径下的启动代码。
.section .init;
.globl _start;
.type _start,@function
_start:
.option push
.option norelax
la gp, __global_pointer$
.option pop
la sp, _sp
#ifdef SIMULATION
/* clear sim result flags */
csrrwi x0, sstatus, 0x0
#endif
/* Load data section */
la a0, _data_lma
la a1, _data
la a2, _edata
bgeu a1, a2, 2f
1:
lw t0, (a0)
sw t0, (a1)
addi a0, a0, 4
addi a1, a1, 4
bltu a1, a2, 1b
2:
/* Clear bss section */
la a0, __bss_start
la a1, _end
bgeu a0, a1, 2f
1:
sw zero, (a0)
addi a0, a0, 4
bltu a0, a1, 1b
2:
/* set exception and interrupt entry */
la a0, trap_entry
csrw mtvec, a0
li a0, 0
li a1, 0
call main
loop:
j loop
以一个period打头的命令是assembler directives,他们是对汇编器的命令,而不是汇编器需要翻译的命令。他们告诉assembler把data和text放到哪。Figure 3.9是RISC-V assembler的directives。
.section .init;
Subsequent items are stored in the section named .init.
globl _start;
:Declare that label sym is global and may be referenced from other files.
这里的.option push
, .option pop
, option norelax
,在上面的表格都有相应的描述。
这里为什么需要使用norelax?
笔者尝试去掉norelax的选项,然后进行反汇编分析。发现去掉会导致结果异常,gp
寄存器不能正确指向__global_pointer
。汇编器出于自己的喜好,做了一些骚操作,导致结果不符合预期。编译器这样做的原因已经超出了笔者的认知范围,笔者暂时无法回答。但从反汇编结果来看,加上准没错。
反汇编文件在tinyriscv/sdk/examples/freertos/Demo/tinyriscv_GCC
路径下的freertos.dump
。笔者在这里贴上_start
函数的反汇编,帮助分析启动代码。
Disassembly of section .init:
00000000 <_start>:
0: 20001197 auipc gp,0x20001
4: 91018193 addi gp,gp,-1776 # 20000910 <__global_pointer$>
8: 20004117 auipc sp,0x20004
c: ff810113 addi sp,sp,-8 # 20004000 <__freertos_irq_stack_top>
10: 00002517 auipc a0,0x2
14: 29850513 addi a0,a0,664 # 22a8 <_data_lma>
18: 20000597 auipc a1,0x20000
1c: fe858593 addi a1,a1,-24 # 20000000 <_data>
20: 20000617 auipc a2,0x20000
24: 0f460613 addi a2,a2,244 # 20000114 <xQueue>
28: 00c5fc63 bgeu a1,a2,40 <_start+0x40>
2c: 00052283 lw t0,0(a0)
30: 0055a023 sw t0,0(a1)
34: 00450513 addi a0,a0,4
38: 00458593 addi a1,a1,4
3c: fec5e8e3 bltu a1,a2,2c <_start+0x2c>
40: 20000517 auipc a0,0x20000
44: 0d450513 addi a0,a0,212 # 20000114 <xQueue>
48: 14018593 addi a1,gp,320 # 20000a50 <_end>
4c: 00b57863 bgeu a0,a1,5c <_start+0x5c>
50: 00052023 sw zero,0(a0)
54: 00450513 addi a0,a0,4
58: feb56ce3 bltu a0,a1,50 <_start+0x50>
5c: 00000517 auipc a0,0x0
60: 63050513 addi a0,a0,1584 # 68c <trap_entry>
64: 30551073 csrw mtvec,a0
68: 00000513 li a0,0
6c: 00000593 li a1,0
70: 090000ef jal ra,100 <main>
启动代码做了下面几件事:
- 初始化gp:gp寄存器存__global_pointer的内存地址
- 初始化sp:sp寄存器存_sp的内存地址
- 初始化data段
- 初始化bss段,清零
- 初始化中断向量,将trap_entry符号所在的地址给到mtvec寄存器。
- 跳转到main