571
社区成员
发帖
与我相关
我的任务
分享在阅读Risc-V的汇编代码前需要一点汇编基础。
1.以下是RISC-V的汇编指示符和作用
| 指示符 | 作用 |
|---|---|
| .text | 代码段,之后跟的符号都在.text内 |
| .data | 数据段,之后跟的符号都在.data内 |
| .bss | 未初始化数据段,之后跟的符号都在.bss中 |
| .section .foo | 自定义段,之后跟的符号都在.foo段中,.foo段名可以做修改 |
| .align n | 按2的n次幂字节对齐 |
| .balign n | 按n字节对齐 |
| .globl sym | 声明sym未全局符号,其它文件可以访问 |
| .string “str” | 将字符串str放入内存 |
| .byte b1,…,bn | 在内存中连续存储n个单字节 |
| .half w1,…,wn | 在内存中连续存储n个半字(2字节) |
| .word w1,…,wn | 在内存中连续存储n个字(4字节) |
| .dword w1,…,wn | 在内存中连续存储n个双字(8字节) |
| .float f1,…,fn | 在内存中连续存储n个单精度浮点数 |
| .double d1,…,dn | 在内存中连续存储n个双精度浮点数 |
| .option rvc | 使用压缩指令(risc-v c) |
| .option norvc | 不压缩指令 |
| .option relax | 允许链接器松弛(linker relaxation,链接时多次扫描代码,尽可能将跳转两条指令替换为一条) |
| .option norelax | 不允许链接松弛 |
| .option pic | 与位置无关代码段 |
| .option nopic | 与位置有关代码段 |
| .option push | 将所有.option设置存入栈 |
| .option pop | 从栈中弹出上次存入的.option设置 |
2.以下是常用指令(伪指令) 更多详情可参考https://github.com/jameslzhu/riscv-card


第一栏为伪指令,第二栏为基础指令,第三栏说明伪指令的作用。基础指令是RISC-V处理器支持的指令,伪指令由基础指令组成,在汇编的时候由汇编器将伪指令转换为基础指令。
RISC-V RV32标准指令集有以下几种框架:
R-format for register-register arithmetic/logical operations
I-format for register-immediate arith/logical operations and loads
S-format for stores
B-format for branches
U-format for 20-bit upper immediate instructions
J-format for jumps
Others: Used for OS & Syncronization
R即Reg相关;I即立即数相关;S存储相关;B分支相关;U高位数相关(因为一条32位指令中无法表示高达32位的数据);J跳转相关。
tips: about arithmetic & logical operations.
逻辑右移(LSR)是将各位依次右移指定位数,然后在左侧补0,算术右移(ASR)是将各位依次右移指定位数,然后在左侧用原符号位补齐。
逻辑左移与算术左移操作相同。
RISC-V采用小端格式(Little-Endian),即低位字节排放在内存的低地址端,高位字节排放在内存的高地址端。
以下是C代码:
//main.c
int add(int a, int b){
return a + b ;
}
int main(void){
return add(2, 3);
}
rvgcc main.c -g 加-g是为了反汇编时将C和汇编交差显示,方便阅读。rvobjdum -dS a.out > main.s 反汇编看到:
000000000001019c <add>:
int add(int a, int b){
1019c: 1101 addi sp,sp,-32 //从原来的栈顶再向下再开辟32Byte栈空间
1019e: ec22 sd s0,24(sp) //保存 保存寄存器,
101a0: 1000 addi s0,sp,32 //栈底保存到fp/s0
101a2: 87aa mv a5,a0 //a5=a0=2
101a4: 872e mv a4,a1 //a4=a1=3
101a6: fef42623 sw a5,-20(s0) //a5=a0=2
101aa: 87ba mv a5,a4
101ac: fef42423 sw a5,-24(s0) //a5=a4=a1=3
return a + b ;
101b0: fec42703 lw a4,-20(s0) //a4=2
101b4: fe842783 lw a5,-24(s0) //a5=3
101b8: 9fb9 addw a5,a5,a4 //相加赋值给a5
101ba: 2781 sext.w a5,a5 //有符号扩展
}
101bc: 853e mv a0,a5 //a5赋给返回寄存器a0
101be: 6462 ld s0,24(sp)
101c0: 6105 addi sp,sp,32 //释放子函数开辟的栈空间
101c2: 8082 ret //返回到ra内容地址
00000000000101c4 <main>:
int main(void){
101c4: 1141 addi sp,sp,-16 //向下开辟16Byte栈空间
101c6: e406 sd ra,8(sp) //调用子函数前需要将ra压栈,否则子函数会覆盖ra
101c8: e022 sd s0,0(sp)
101ca: 0800 addi s0,sp,16 //栈底保存到fp/s0
return add(2, 3);
101cc: 458d li a1,3 //参数寄存器赋值
101ce: 4509 li a0,2 //参数寄存器赋值
101d0: fcdff0ef jal ra,1019c <add> //跳进函数add的地址1019c,并将返回地址(pc+4)存入ra
101d4: 87aa mv a5,a0 //计算结果a0 赋给a5
}
101d6: 853e mv a0,a5 //赋给a0,作为main函数的返回值
101d8: 60a2 ld ra,8(sp) //main函数的ra出栈
101da: 6402 ld s0,0(sp) //main函数的sp出栈
101dc: 0141 addi sp,sp,16 //栈指针指向栈底,释放栈
101de: 8082 ret //从main函数的ra返回
汇编语言参数传递的3种方法
寄存器传参, 效率高,适合少量参数
地址传参, 参数打包到一个结构体,讲结构体的地址传入,然后解包
堆栈传参,调用前先压栈,再调用,返回从堆栈返回,也可以从寄存器返回
三种传参方法可以单独使用,也可以联合使用
354