RISC-V 32位架构基础知识¶
Pwn 速查
- 函数参数:
a0-a7,返回值:a0(64 位返回值可用a0:a1)。 - Linux 系统调用:调用号放
a7,参数放a0-a5,指令为ecall。 ra会被call/jal覆盖;非叶子函数必须保存自己的返回地址。
Note
RISC-V 基础指令通常为 4 字节;启用 RVC 压缩扩展时也会出现 2 字节指令。
RISC-V 32位寄存器详解¶
通用寄存器 (x0-x31)¶
| 寄存器 | ABI 名 | 用途 |
|---|---|---|
x0 |
zero |
硬件零寄存器 |
x1 |
ra |
返回地址 |
x2 |
sp |
栈指针 |
x3 |
gp |
全局指针 |
x4 |
tp |
线程指针 |
x5-x7 |
t0-t2 |
临时寄存器 |
x8 |
s0/fp |
保存寄存器 / 帧指针 |
x9 |
s1 |
保存寄存器 |
x10-x11 |
a0-a1 |
参数 / 返回值 |
x12-x17 |
a2-a7 |
参数;a7 也放 Linux syscall number |
x18-x27 |
s2-s11 |
保存寄存器 |
x28-x31 |
t3-t6 |
临时寄存器 |
寄存器别名和用途¶
| 寄存器 | 别名 | 主要用途 | 调用约定 |
|---|---|---|---|
x0 |
zero |
硬件零常量 | 常数 0 |
x1 |
ra |
返回地址 | 调用者保存 |
x2 |
sp |
栈指针 | 特殊 / 保持对齐 |
x3 |
gp |
全局指针 | ABI 相关 |
x4 |
tp |
线程指针 | ABI 相关 |
x5-x7 |
t0-t2 |
临时寄存器 | 调用者保存 |
x8-x9 |
s0/fp, s1 |
保存寄存器 / 帧指针 | 被调用者保存 |
x10-x17 |
a0-a7 |
参数 / 返回值;a7 为 syscall number |
调用者保存 |
x18-x27 |
s2-s11 |
保存寄存器 | 被调用者保存 |
x28-x31 |
t3-t6 |
临时寄存器 | 调用者保存 |
特殊寄存器¶
| 寄存器 | 作用 |
|---|---|
pc |
程序计数器 |
mstatus |
机器状态寄存器 |
mtvec |
机器陷阱向量基地址 |
mcause |
机器陷阱原因 |
mtval |
机器陷阱值 |
mip |
机器中断挂起 |
mie |
机器中断使能 |
Warning
用户态 Pwn 通常不能直接读写 mstatus、mtvec 这类机器态 CSR;这里只是说明体系结构中存在这些寄存器。
Linux RISC-V 32位系统调用表¶
常用系统调用号¶
常用系统调用号(展开查看)
| C | |
|---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 | |
系统调用约定¶
| 项目 | 约定 |
|---|---|
| 系统调用号 | a7 (x17) |
| 参数 1-6 | a0-a5 (x10-x15) |
| 返回值 | a0 (x10) |
| 错误 | Linux 通常在 a0 返回 -errno(约 -4095 到 -1)表示失败 |
| 触发指令 | ecall |
系统调用示例¶
| GAS | |
|---|---|
函数调用约定 (RISC-V ABI)¶
参数传递¶
| 项目 | RISC-V ABI 约定 |
|---|---|
| 前 8 个参数 | a0-a7 (x10-x17) |
| 更多参数 | 通过栈上传参区域传递 |
| 返回值 | a0 (x10) (32 位), a0:a1 (x10:x11) (64 位) |
| 栈对齐 | 函数入口 sp 保持 16 字节对齐 |
Note
call 会覆盖 ra,所以只要当前函数之后还要返回上一级,就要先保存 ra。
栈帧结构¶
| Text Only | |
|---|---|
函数调用流程¶
指令集合¶
RISC-V 基础指令通常为 4 字节;启用压缩指令扩展(RVC)时也会出现 2 字节指令。
指令格式¶
RISC-V 使用多种指令格式:
一般汇编格式:
指令分类¶
RISC-V指令可以分为以下几类:
Note
MUL/DIV/REM 等乘除法指令来自 M 扩展;多数 Linux 发行版目标会包含该扩展,但阅读极简内核或裸机题时仍需确认 ELF/CPU 支持情况。
整数计算指令¶
| 指令 | 含义 | 示例 | 描述 |
|---|---|---|---|
| ADD | 寄存器相加 | add rd, rs1, rs2 |
rd = rs1 + rs2 |
| ADDI | 立即数相加 | addi rd, rs1, imm |
rd = rs1 + imm |
| SUB | 寄存器相减 | sub rd, rs1, rs2 |
rd = rs1 - rs2 |
| MUL | 乘法 | mul rd, rs1, rs2 |
rd = rs1 * rs2 |
| MULH | 高位乘法 | mulh rd, rs1, rs2 |
rd = (rs1 * rs2) >> 32 |
| DIV | 有符号除法 | div rd, rs1, rs2 |
rd = rs1 / rs2 |
| DIVU | 无符号除法 | divu rd, rs1, rs2 |
rd = rs1 / rs2 (无符号) |
| REM | 有符号求余 | rem rd, rs1, rs2 |
rd = rs1 % rs2 |
| REMU | 无符号求余 | remu rd, rs1, rs2 |
rd = rs1 % rs2 (无符号) |
逻辑运算指令¶
| 指令 | 含义 | 示例 | 描述 |
|---|---|---|---|
| AND | 位与 | and rd, rs1, rs2 |
rd = rs1 & rs2 |
| ANDI | 立即数位与 | andi rd, rs1, imm |
rd = rs1 & imm |
| OR | 位或 | or rd, rs1, rs2 |
rd = rs1 | rs2 |
| ORI | 立即数位或 | ori rd, rs1, imm |
rd = rs1 | imm |
| XOR | 位异或 | xor rd, rs1, rs2 |
rd = rs1 ^ rs2 |
| XORI | 立即数位异或 | xori rd, rs1, imm |
rd = rs1 ^ imm |
移位指令¶
| 指令 | 含义 | 示例 | 描述 |
|---|---|---|---|
| SLL | 逻辑左移 | sll rd, rs1, rs2 |
rd = rs1 << rs2 |
| SLLI | 立即数逻辑左移 | slli rd, rs1, shamt |
rd = rs1 << shamt |
| SRL | 逻辑右移 | srl rd, rs1, rs2 |
rd = rs1 >> rs2 (逻辑) |
| SRLI | 立即数逻辑右移 | srli rd, rs1, shamt |
rd = rs1 >> shamt (逻辑) |
| SRA | 算术右移 | sra rd, rs1, rs2 |
rd = rs1 >> rs2 (算术) |
| SRAI | 立即数算术右移 | srai rd, rs1, shamt |
rd = rs1 >> shamt (算术) |
比较指令¶
| 指令 | 含义 | 示例 | 描述 |
|---|---|---|---|
| SLT | 小于设置 | slt rd, rs1, rs2 |
rd = (rs1 < rs2) ? 1 : 0 |
| SLTI | 立即数小于设置 | slti rd, rs1, imm |
rd = (rs1 < imm) ? 1 : 0 |
| SLTU | 无符号小于设置 | sltu rd, rs1, rs2 |
rd = (rs1 < rs2) ? 1 : 0 (无符号) |
| SLTIU | 立即数无符号小于设置 | sltiu rd, rs1, imm |
rd = (rs1 < imm) ? 1 : 0 (无符号) |
分支指令¶
| 指令 | 含义 | 示例 | 描述 |
|---|---|---|---|
| BEQ | 相等分支 | beq rs1, rs2, label |
如果 rs1 == rs2 跳转 |
| BNE | 不等分支 | bne rs1, rs2, label |
如果 rs1 != rs2 跳转 |
| BLT | 小于分支 | blt rs1, rs2, label |
如果 rs1 < rs2 跳转 |
| BGE | 大于等于分支 | bge rs1, rs2, label |
如果 rs1 >= rs2 跳转 |
| BLTU | 无符号小于分支 | bltu rs1, rs2, label |
如果 rs1 < rs2 跳转 (无符号) |
| BGEU | 无符号大于等于分支 | bgeu rs1, rs2, label |
如果 rs1 >= rs2 跳转 (无符号) |
跳转指令¶
| 指令 | 含义 | 示例 | 描述 |
|---|---|---|---|
| JAL | 跳转并链接 | jal rd, label |
rd = pc + 4; pc = pc + offset |
| JALR | 寄存器跳转并链接 | jalr rd, rs1, imm |
rd = pc + 4; pc = (rs1 + imm) & ~1 |
加载/存储指令¶
| 指令 | 含义 | 示例 | 描述 |
|---|---|---|---|
| LW | 加载字 | lw rd, offset(rs1) |
rd = M[rs1 + offset] |
| LH | 加载半字 | lh rd, offset(rs1) |
rd = M[rs1 + offset] (符号扩展) |
| LHU | 加载无符号半字 | lhu rd, offset(rs1) |
rd = M[rs1 + offset] (零扩展) |
| LB | 加载字节 | lb rd, offset(rs1) |
rd = M[rs1 + offset] (符号扩展) |
| LBU | 加载无符号字节 | lbu rd, offset(rs1) |
rd = M[rs1 + offset] (零扩展) |
| SW | 存储字 | sw rs2, offset(rs1) |
M[rs1 + offset] = rs2 |
| SH | 存储半字 | sh rs2, offset(rs1) |
M[rs1 + offset] = rs2[15:0] |
| SB | 存储字节 | sb rs2, offset(rs1) |
M[rs1 + offset] = rs2[7:0] |
立即数加载指令¶
| 指令 | 含义 | 示例 | 描述 |
|---|---|---|---|
| LUI | 加载高位立即数 | lui rd, imm |
rd = imm << 12 |
| AUIPC | PC相对高位立即数 | auipc rd, imm |
rd = pc + (imm << 12) |
系统指令¶
| 指令 | 含义 | 示例 | 描述 |
|---|---|---|---|
| ECALL | 环境调用 | ecall |
触发系统调用 |
| EBREAK | 环境断点 | ebreak |
触发调试断点 |
| FENCE | 内存屏障 | fence |
内存操作排序 |
伪指令¶
RISC-V 汇编器支持许多伪指令,简化编程:
| 伪指令 | 实际指令 | 示例 | 描述 |
|---|---|---|---|
| NOP | addi x0, x0, 0 |
nop |
空操作 |
| LI | addi rd, x0, imm 或多条指令 |
li t0, 100 |
加载立即数 |
| MV | addi rd, rs, 0 |
mv t0, t1 |
寄存器移动 |
| NOT | xori rd, rs, -1 |
not t0, t1 |
按位取反 |
| NEG | sub rd, x0, rs |
neg t0, t1 |
取负数 |
| LA | 多条指令 | la t0, symbol |
加载地址 |
| J | jal x0, offset |
j label |
无条件跳转 |
| JR | jalr x0, rs, 0 |
jr ra |
寄存器跳转 |
| CALL | auipc ra, offset[31:12] + jalr ra, ra, offset[11:0] |
call func |
函数调用 |
| RET | jalr x0, ra, 0 |
ret |
函数返回 |
寻址模式¶
RISC-V 支持以下寻址模式:
立即数寻址¶
寄存器寻址¶
基址+偏移寻址¶
PC相对寻址¶
| GAS | |
|---|---|
符号地址寻址¶
重点指令详解¶
算术运算指令¶
| 指令 | 计算公式 | 示例 | 备注 |
|---|---|---|---|
| ADD rd, rs1, rs2 | rd = rs1 + rs2 | add t0, t1, t2 |
32位加法 |
| ADDI rd, rs1, imm | rd = rs1 + imm | addi t0, t1, 100 |
立即数加法 |
| SUB rd, rs1, rs2 | rd = rs1 - rs2 | sub t0, t1, t2 |
32位减法 |
| MUL rd, rs1, rs2 | rd = (rs1 * rs2)[31:0] | mul t0, t1, t2 |
乘法低32位 |
| MULH rd, rs1, rs2 | rd = (rs1 * rs2)[63:32] | mulh t0, t1, t2 |
有符号乘法高32位 |
| MULHU rd, rs1, rs2 | rd = (rs1 * rs2)[63:32] | mulhu t0, t1, t2 |
无符号乘法高32位 |
| MULHSU rd, rs1, rs2 | rd = (rs1 * rs2)[63:32] | mulhsu t0, t1, t2 |
有符号×无符号乘法高32位 |
| DIV rd, rs1, rs2 | rd = rs1 / rs2 | div t0, t1, t2 |
有符号除法 |
| DIVU rd, rs1, rs2 | rd = rs1 / rs2 | divu t0, t1, t2 |
无符号除法 |
| REM rd, rs1, rs2 | rd = rs1 % rs2 | rem t0, t1, t2 |
有符号求余 |
| REMU rd, rs1, rs2 | rd = rs1 % rs2 | remu t0, t1, t2 |
无符号求余 |
数据传输指令示例¶
| 指令 | 目的 | 源 | 描述 |
|---|---|---|---|
| MV | t0 | t1 | 将 t1 里面的数据复制到 t0 中 |
| LI | t0 | 0x1234 | 将立即数0x1234加载到t0中 |
| LA | t0 | symbol | 将符号地址加载到t0中 |
| LW | t0 | 0(t1) | 从t1指向的地址加载字到t0 |
| LW | t0 | 4(t1) | 从t1+4地址加载字到t0 |
| SW | t1 | 0(t0) | 将 t1 中的值写入到 t0 中所保存的地址中 |
栈操作指令¶
RISC-V 没有专门的栈指令,使用加载/存储指令操作栈:
| 操作 | 指令序列 | 描述 | 等价效果 |
|---|---|---|---|
| PUSH t0 | addi sp, sp, -4 + sw t0, 0(sp) |
将 t0 压栈 | 减少栈指针,存储值 |
| POP t0 | lw t0, 0(sp) + addi sp, sp, 4 |
从栈中弹出到 t0 | 加载值,增加栈指针 |
| 保存返回地址 | addi sp, sp, -4 + sw ra, 0(sp) |
保存返回地址 | 函数序言 |
| 恢复返回地址 | lw ra, 0(sp) + addi sp, sp, 4 |
恢复返回地址 | 函数结尾 |
常用编程模式¶
循环结构¶
| GAS | |
|---|---|
条件判断¶
函数调用模板¶
数组操作¶
位操作技巧¶
内存对齐和优化¶
这些编程模式涵盖了RISC-V汇编中最常用的结构和技巧,可以作为编写RISC-V汇编程序的参考模板。