跳转至

RISC-V 64位架构基础知识

Pwn 速查

  • 函数参数:a0-a7,返回值:a0(128 位返回值可用 a0:a1)。
  • Linux 系统调用:调用号放 a7,参数放 a0-a5,指令为 ecall
  • ra 会被 call / jal 覆盖;非叶子函数必须保存自己的返回地址。

Note

RISC-V 基础指令通常为 4 字节;启用 RVC 压缩扩展时也会出现 2 字节指令。

RISC-V 64位寄存器详解

通用寄存器 (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 通常不能直接读写 mstatusmtvec 这类机器态 CSR;这里只是说明体系结构中存在这些寄存器。

Linux RISC-V 64位系统调用表

常用系统调用号

常用系统调用号(展开查看)
C
#define __NR_io_setup                0
#define __NR_io_destroy              1
#define __NR_io_submit               2
#define __NR_io_cancel               3
#define __NR_io_getevents            4
#define __NR_setxattr                5
#define __NR_lsetxattr               6
#define __NR_fsetxattr               7
#define __NR_getxattr                8
#define __NR_lgetxattr               9
#define __NR_fgetxattr              10
#define __NR_listxattr              11
#define __NR_llistxattr             12
#define __NR_flistxattr             13
#define __NR_removexattr            14
#define __NR_lremovexattr           15
#define __NR_fremovexattr           16
#define __NR_getcwd                 17
#define __NR_lookup_dcookie         18
#define __NR_eventfd2               19
#define __NR_epoll_create1          20
#define __NR_epoll_ctl              21
#define __NR_epoll_pwait            22
#define __NR_dup                    23
#define __NR_dup3                   24
#define __NR_fcntl                  25
#define __NR_inotify_init1          26
#define __NR_inotify_add_watch      27
#define __NR_inotify_rm_watch       28
#define __NR_ioctl                  29
#define __NR_ioprio_set             30
#define __NR_ioprio_get             31
#define __NR_flock                  32
#define __NR_mknodat                33
#define __NR_mkdirat                34
#define __NR_unlinkat               35
#define __NR_symlinkat              36
#define __NR_linkat                 37
#define __NR_umount2                39
#define __NR_mount                  40
#define __NR_pivot_root             41
#define __NR_nfsservctl             42
#define __NR_statfs                 43
#define __NR_fstatfs                44
#define __NR_truncate               45
#define __NR_ftruncate              46
#define __NR_fallocate              47
#define __NR_faccessat              48
#define __NR_chdir                  49
#define __NR_fchdir                 50
#define __NR_chroot                 51
#define __NR_fchmod                 52
#define __NR_fchmodat               53
#define __NR_fchownat               54
#define __NR_fchown                 55
#define __NR_openat                 56
#define __NR_close                  57
#define __NR_vhangup                58
#define __NR_pipe2                  59
#define __NR_quotactl               60
#define __NR_getdents64             61
#define __NR_lseek                  62
#define __NR_read                   63
#define __NR_write                  64
#define __NR_readv                  65
#define __NR_writev                 66
#define __NR_pread64                67
#define __NR_pwrite64               68
#define __NR_preadv                 69
#define __NR_pwritev                70
#define __NR_sendfile               71
#define __NR_pselect6               72
#define __NR_ppoll                  73
#define __NR_signalfd4              74
#define __NR_vmsplice               75
#define __NR_splice                 76
#define __NR_tee                    77
#define __NR_readlinkat             78
#define __NR_fstatat                79
#define __NR_fstat                  80
#define __NR_sync                   81
#define __NR_fsync                  82
#define __NR_fdatasync              83
#define __NR_sync_file_range        84
#define __NR_timerfd_create         85
#define __NR_timerfd_settime        86
#define __NR_timerfd_gettime        87
#define __NR_utimensat              88
#define __NR_acct                   89
#define __NR_capget                 90
#define __NR_capset                 91
#define __NR_personality            92
#define __NR_exit                   93
#define __NR_exit_group             94
#define __NR_waitid                 95
#define __NR_set_tid_address        96
#define __NR_unshare                97
#define __NR_futex                  98
#define __NR_set_robust_list        99
#define __NR_get_robust_list       100
#define __NR_nanosleep             101
#define __NR_getitimer             102
#define __NR_setitimer             103
#define __NR_kexec_load            104
#define __NR_init_module           105
#define __NR_delete_module         106
#define __NR_timer_create          107
#define __NR_timer_gettime         108
#define __NR_timer_getoverrun      109
#define __NR_timer_settime         110
#define __NR_timer_delete          111
#define __NR_clock_settime         112
#define __NR_clock_gettime         113
#define __NR_clock_getres          114
#define __NR_clock_nanosleep       115
#define __NR_syslog                116
#define __NR_ptrace                117
#define __NR_sched_setparam        118
#define __NR_sched_setscheduler    119
#define __NR_sched_getscheduler    120
#define __NR_sched_getparam        121
#define __NR_sched_setaffinity     122
#define __NR_sched_getaffinity     123
#define __NR_sched_yield           124
#define __NR_sched_get_priority_max 125
#define __NR_sched_get_priority_min 126
#define __NR_sched_rr_get_interval 127
#define __NR_restart_syscall       128
#define __NR_kill                  129
#define __NR_tkill                 130
#define __NR_tgkill                131
#define __NR_sigaltstack           132
#define __NR_rt_sigsuspend         133
#define __NR_rt_sigaction          134
#define __NR_rt_sigprocmask        135
#define __NR_rt_sigpending         136
#define __NR_rt_sigtimedwait       137
#define __NR_rt_sigqueueinfo       138
#define __NR_rt_sigreturn          139
#define __NR_setpriority           140
#define __NR_getpriority           141
#define __NR_reboot                142
#define __NR_setregid              143
#define __NR_setgid                144
#define __NR_setreuid              145
#define __NR_setuid                146
#define __NR_setresuid             147
#define __NR_getresuid             148
#define __NR_setresgid             149
#define __NR_getresgid             150
#define __NR_setfsuid              151
#define __NR_setfsgid              152
#define __NR_times                 153
#define __NR_setpgid               154
#define __NR_getpgid               155
#define __NR_getsid                156
#define __NR_setsid                157
#define __NR_getgroups             158
#define __NR_setgroups             159
#define __NR_uname                 160
#define __NR_sethostname           161
#define __NR_setdomainname         162
#define __NR_getrlimit             163
#define __NR_setrlimit             164
#define __NR_getrusage             165
#define __NR_umask                 166
#define __NR_prctl                 167
#define __NR_getcpu                168
#define __NR_gettimeofday          169
#define __NR_settimeofday          170
#define __NR_adjtimex              171
#define __NR_getpid                172
#define __NR_getppid               173
#define __NR_getuid                174
#define __NR_geteuid               175
#define __NR_getgid                176
#define __NR_getegid               177
#define __NR_gettid                178
#define __NR_sysinfo               179
#define __NR_mq_open               180
#define __NR_mq_unlink             181
#define __NR_mq_timedsend          182
#define __NR_mq_timedreceive       183
#define __NR_mq_notify             184
#define __NR_mq_getsetattr         185
#define __NR_msgget                186
#define __NR_msgctl                187
#define __NR_msgrcv                188
#define __NR_msgsnd                189
#define __NR_semget                190
#define __NR_semctl                191
#define __NR_semtimedop            192
#define __NR_semop                 193
#define __NR_shmget                194
#define __NR_shmctl                195
#define __NR_shmat                 196
#define __NR_shmdt                 197
#define __NR_socket                198
#define __NR_socketpair            199
#define __NR_bind                  200
#define __NR_listen                201
#define __NR_accept                202
#define __NR_connect               203
#define __NR_getsockname           204
#define __NR_getpeername           205
#define __NR_sendto                206
#define __NR_recvfrom              207
#define __NR_setsockopt            208
#define __NR_getsockopt            209
#define __NR_shutdown              210
#define __NR_sendmsg               211
#define __NR_recvmsg               212
#define __NR_readahead             213
#define __NR_brk                   214
#define __NR_munmap                215
#define __NR_mremap                216
#define __NR_add_key               217
#define __NR_request_key           218
#define __NR_keyctl                219
#define __NR_clone                 220
#define __NR_execve                221
#define __NR_mmap                  222
#define __NR_fadvise64             223
#define __NR_swapon                224
#define __NR_swapoff               225
#define __NR_mprotect              226
#define __NR_msync                 227
#define __NR_mlock                 228
#define __NR_munlock               229
#define __NR_mlockall              230
#define __NR_munlockall            231
#define __NR_mincore               232
#define __NR_madvise               233
#define __NR_remap_file_pages      234
#define __NR_mbind                 235
#define __NR_get_mempolicy         236
#define __NR_set_mempolicy         237
#define __NR_migrate_pages         238
#define __NR_move_pages            239
#define __NR_rt_tgsigqueueinfo     240
#define __NR_perf_event_open       241
#define __NR_accept4               242
#define __NR_recvmmsg              243
#define __NR_arch_specific_syscall 244
#define __NR_wait4                 260
#define __NR_prlimit64             261
#define __NR_fanotify_init         262
#define __NR_fanotify_mark         263
#define __NR_name_to_handle_at     264
#define __NR_open_by_handle_at     265
#define __NR_clock_adjtime         266
#define __NR_syncfs                267
#define __NR_setns                 268
#define __NR_sendmmsg              269
#define __NR_process_vm_readv      270
#define __NR_process_vm_writev     271
#define __NR_kcmp                  272
#define __NR_finit_module          273
#define __NR_sched_setattr         274
#define __NR_sched_getattr         275
#define __NR_renameat2             276
#define __NR_seccomp               277
#define __NR_getrandom             278
#define __NR_memfd_create          279
#define __NR_bpf                   280
#define __NR_execveat              281
#define __NR_userfaultfd           282
#define __NR_membarrier            283
#define __NR_mlock2                284
#define __NR_copy_file_range       285
#define __NR_preadv2               286
#define __NR_pwritev2              287
#define __NR_pkey_mprotect         288
#define __NR_pkey_alloc            289
#define __NR_pkey_free             290
#define __NR_statx                 291
#define __NR_io_pgetevents         292
#define __NR_rseq                  293
#define __NR_kexec_file_load       294
#define __NR_pidfd_send_signal     424
#define __NR_io_uring_setup        425
#define __NR_io_uring_enter        426
#define __NR_io_uring_register     427
#define __NR_open_tree             428
#define __NR_move_mount            429
#define __NR_fsopen                430
#define __NR_fsconfig              431
#define __NR_fsmount               432
#define __NR_fspick                433
#define __NR_pidfd_open            434
#define __NR_clone3                435
#define __NR_close_range           436
#define __NR_openat2               437
#define __NR_pidfd_getfd           438
#define __NR_faccessat2            439
#define __NR_process_madvise       440
#define __NR_epoll_pwait2          441
#define __NR_mount_setattr         442
#define __NR_quotactl_fd           443
#define __NR_landlock_create_ruleset 444
#define __NR_landlock_add_rule     445
#define __NR_landlock_restrict_self 446
#define __NR_memfd_secret          447
#define __NR_process_mrelease      448
#define __NR_futex_waitv           449
#define __NR_set_mempolicy_home_node 450

系统调用约定

系统调用查询

项目 约定
系统调用号 a7 (x17)
参数 1-6 a0-a5 (x10-x15)
返回值 a0 (x10)
错误 Linux 通常在 a0 返回 -errno(约 -4095-1)表示失败
触发指令 ecall

系统调用示例

GAS
; write(1, "Hello", 5)
li a0, 1            ; fd = 1 (stdout)
la a1, hello_str    ; buf = "Hello"
li a2, 5            ; count = 5
li a7, 64           ; __NR_write
ecall               ; 系统调用

; exit(0)
li a0, 0            ; status = 0
li a7, 93           ; __NR_exit
ecall

函数调用约定 (RISC-V ABI)

参数传递

项目 RISC-V ABI 约定
前 8 个参数 a0-a7 (x10-x17)
更多参数 通过栈上传参区域传递
返回值 a0 (x10) (64 位), a0:a1 (x10:x11) (128 位)
栈对齐 函数入口 sp 保持 16 字节对齐

Note

call 会覆盖 ra,所以只要当前函数之后还要返回上一级,就要先保存 ra

栈帧结构

Text Only
高地址
+------------------+
| 第n个参数        |  <- 超过a0-a7的参数
| ...              |
| 第9个参数        |
+------------------+
| 返回地址 (ra)    |  <- 函数调用时保存
| 老的帧指针(fp)   |  <- 可选
| 局部变量         |
| 保存的寄存器     |  <- s0-s11需要保存
+------------------+  <- sp (当前栈指针)
低地址

函数调用流程

GAS
; 调用者函数 (Caller,非叶子函数示例)
addi sp, sp, -32    ; 分配栈空间并保持16字节对齐
sd ra, 24(sp)       ; call 会覆盖 ra,非叶子函数必须保存自己的返回地址
sd s0, 16(sp)       ; 如果调用者自己使用了 s 寄存器,也要保存
li a0, arg1         ; 设置参数1
li a1, arg2         ; 设置参数2
call function       ; 调用函数
; a0 包含返回值
ld ra, 24(sp)       ; 恢复返回地址
ld s0, 16(sp)       ; 恢复寄存器
addi sp, sp, 32     ; 释放栈空间
ret                 ; 返回

; 被调用者 (Callee)
function:
addi sp, sp, -32    ; 分配栈空间
sd ra, 24(sp)       ; 保存返回地址
sd s0, 16(sp)       ; 保存需要使用的寄存器
; 函数体
li a0, return_val   ; 设置返回值
ld ra, 24(sp)       ; 恢复返回地址
ld s0, 16(sp)       ; 恢复寄存器
addi sp, sp, 32     ; 释放栈空间
ret                 ; 返回

指令集合

RISC-V 基础指令通常为 4 字节;启用压缩指令扩展(RVC)时也会出现 2 字节指令。

指令格式

RISC-V 使用多种指令格式:

Text Only
1
2
3
4
5
6
R-type: funct7 | rs2 | rs1 | funct3 | rd | opcode
I-type: imm\[11:0\] | rs1 | funct3 | rd | opcode
S-type: imm\[11:5\] | rs2 | rs1 | funct3 | imm\[4:0\] | opcode
B-type: imm\[12|10:5\] | rs2 | rs1 | funct3 | imm\[4:1|11\] | opcode
U-type: imm\[31:12\] | rd | opcode
J-type: imm\[20|10:1|11|19:12\] | rd | opcode

一般汇编格式:

GAS
MNEMONIC rd, rs1, rs2/imm
助记符 目的寄存器, 源寄存器1, 源寄存器2/立即数

指令分类

RISC-V指令可以分为以下几类:

Note

MUL/DIV/REM 等乘除法指令来自 M 扩展;多数 Linux 发行版目标会包含该扩展,但阅读极简内核或裸机题时仍需确认 ELF/CPU 支持情况。

整数计算指令(XLEN / 64 位)
指令 含义 示例 描述
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) >> 64
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 (无符号)
字运算指令(32 位结果符号扩展)
指令 含义 示例 描述
ADDW 字相加 addw rd, rs1, rs2 rd = sext((rs1 + rs2)[31:0])
ADDIW 立即数字相加 addiw rd, rs1, imm rd = sext((rs1 + imm)[31:0])
SUBW 字相减 subw rd, rs1, rs2 rd = sext((rs1 - rs2)[31:0])
MULW 字乘法 mulw rd, rs1, rs2 rd = sext((rs1 * rs2)[31:0])
DIVW 有符号字除法 divw rd, rs1, rs2 rd = sext(rs1[31:0] / rs2[31:0])
DIVUW 无符号字除法 divuw rd, rs1, rs2 rd = sext(rs1[31:0] / rs2[31:0])
REMW 有符号字求余 remw rd, rs1, rs2 rd = sext(rs1[31:0] % rs2[31:0])
REMUW 无符号字求余 remuw rd, rs1, rs2 rd = sext(rs1[31:0] % rs2[31:0])
逻辑运算指令
指令 含义 示例 描述
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 (算术)
移位指令 (64位)
指令 含义 示例 描述
SLLW 字逻辑左移 sllw rd, rs1, rs2 rd = sext((rs1 << rs2[4:0])[31:0])
SLLIW 立即数字逻辑左移 slliw rd, rs1, shamt rd = sext((rs1 << shamt)[31:0])
SRLW 字逻辑右移 srlw rd, rs1, rs2 rd = sext(rs1[31:0] >> rs2[4:0])
SRLIW 立即数字逻辑右移 srliw rd, rs1, shamt rd = sext(rs1[31:0] >> shamt)
SRAW 字算术右移 sraw rd, rs1, rs2 rd = sext(rs1[31:0] >> rs2[4:0])
SRAIW 立即数字算术右移 sraiw rd, rs1, shamt rd = sext(rs1[31:0] >> 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
加载/存储指令 (32位)
指令 含义 示例 描述
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]
加载/存储指令 (64位)
指令 含义 示例 描述
LD 加载双字 ld rd, offset(rs1) rd = M[rs1 + offset]
LWU 加载无符号字 lwu rd, offset(rs1) rd = M[rs1 + offset] (零扩展到64位)
SD 存储双字 sd rs2, offset(rs1) M[rs1 + offset] = rs2
立即数加载指令
指令 含义 示例 描述
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 取负数
NEGW subw rd, x0, rs negw t0, t1 取负数(32位)
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 支持以下寻址模式:

立即数寻址
GAS
addi t0, zero, 100  # t0 = 0 + 100
li t0, 100          # 伪指令,等价于上面
寄存器寻址
GAS
add t0, t1, t2      # t0 = t1 + t2
mv t0, t1           # t0 = t1 (伪指令)
基址+偏移寻址
GAS
ld t0, 8(sp)        # t0 = M[sp + 8]
sd t0, -8(t1)       # M[t1 - 8] = t0
PC相对寻址
GAS
auipc t0, 0x10000   # t0 = pc + (0x10000 << 12)
符号地址寻址
GAS
1
2
3
la t0, symbol       # 加载符号地址
lui t0, %hi(symbol) # 加载符号高位
addi t0, t0, %lo(symbol) # 加载符号低位

重点指令详解

算术运算指令
指令 计算公式 示例 备注
ADD rd, rs1, rs2 rd = rs1 + rs2 add t0, t1, t2 64位加法
ADDI rd, rs1, imm rd = rs1 + imm addi t0, t1, 100 立即数加法
SUB rd, rs1, rs2 rd = rs1 - rs2 sub t0, t1, t2 64位减法
ADDW rd, rs1, rs2 rd = sext((rs1 + rs2)[31:0]) addw t0, t1, t2 32位加法,符号扩展
SUBW rd, rs1, rs2 rd = sext((rs1 - rs2)[31:0]) subw t0, t1, t2 32位减法,符号扩展
MUL rd, rs1, rs2 rd = (rs1 * rs2)[63:0] mul t0, t1, t2 乘法低64位
MULH rd, rs1, rs2 rd = (rs1 * rs2)[127:64] mulh t0, t1, t2 有符号乘法高64位
MULHU rd, rs1, rs2 rd = (rs1 * rs2)[127:64] mulhu t0, t1, t2 无符号乘法高64位
MULHSU rd, rs1, rs2 rd = (rs1 * rs2)[127:64] mulhsu t0, t1, t2 有符号×无符号乘法高64位
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中
LD t0 0(t1) 从t1指向的地址加载双字到t0
LW t0 4(t1) 从t1+4地址加载字到t0
SD t1 0(t0) 将 t1 中的值写入到 t0 中所保存的地址中
栈操作指令

RISC-V 没有专门的栈指令,使用加载/存储指令操作栈:

操作 指令序列 描述 等价效果
PUSH t0 addi sp, sp, -8 + sd t0, 0(sp) 将 t0 压栈 减少栈指针,存储值
POP t0 ld t0, 0(sp) + addi sp, sp, 8 从栈中弹出到 t0 加载值,增加栈指针
保存返回地址 addi sp, sp, -8 + sd ra, 0(sp) 保存返回地址 函数序言
恢复返回地址 ld ra, 0(sp) + addi sp, sp, 8 恢复返回地址 函数结尾

常用编程模式

循环结构
GAS
# for循环示例: for(int i=0; i<10; i++)
li t0, 0            # i = 0
li t1, 10           # 循环上界
loop:
    bge t0, t1, loop_end    # 如果 i >= 10 跳出循环
    # 循环体代码
    addi t0, t0, 1  # i++
    j loop          # 跳回循环开始
loop_end:

# while循环示例: while(condition)
while_loop:
    # 检查条件的代码
    beq t0, zero, while_end   # 条件为假则退出
    # 循环体代码
    j while_loop    # 继续循环
while_end:
条件判断
GAS
# if-else 结构
li t1, 5
blt t0, t1, else_branch     # 如果 t0 < 5 跳到 else
    # if 分支代码
    li t2, 1
    j endif
else_branch:
    # else 分支代码
    li t2, 0
endif:

# switch-case 结构
li t1, 1
beq t0, t1, case1
li t1, 2
beq t0, t1, case2
li t1, 3
beq t0, t1, case3
j default_case

case1:
    # case 1 代码
    j switch_end
case2:
    # case 2 代码
    j switch_end
case3:
    # case 3 代码
    j switch_end
default_case:
    # 默认情况代码
switch_end:
函数调用模板
GAS
# 标准函数模板
function_name:
    addi sp, sp, -32    # 为局部变量分配栈空间
    sd ra, 24(sp)       # 保存返回地址
    sd s0, 16(sp)       # 保存需要使用的寄存器
    sd s1, 8(sp)

    # 函数体
    # 参数在 a0-a7 中
    # 局部变量使用栈空间

    li a0, return_val   # 设置返回值

    ld ra, 24(sp)       # 恢复返回地址
    ld s0, 16(sp)       # 恢复寄存器
    ld s1, 8(sp)
    addi sp, sp, 32     # 释放栈空间
    ret                 # 返回

# 调用函数
    li a0, arg1         # 第一个参数
    li a1, arg2         # 第二个参数
    li a2, arg3         # 第三个参数
    li a3, arg4         # 第四个参数
    call function_name  # 调用函数
    # 返回值在 a0 中
数组操作
GAS
# 数组遍历示例
la t0, array_base       # 数组基地址
li t1, 0                # 索引 i = 0
li t2, array_size       # 数组大小

array_loop:
    bge t1, t2, array_end   # i >= size 则结束

    slli t3, t1, 3      # t3 = i * 8 (假设long数组)
    add t3, t0, t3      # t3 = 数组基地址 + i*8
    ld t4, 0(t3)        # 加载 array[i]
    # 处理 t4 中的数据

    addi t1, t1, 1      # i++
    j array_loop
array_end:

# 字符串长度计算
la t0, string_ptr       # 字符串指针
li t1, 0                # 长度计数器

strlen_loop:
    add t2, t0, t1      # t2 = 字符串地址 + 偏移
    lb t3, 0(t2)        # 加载当前字符
    beq t3, zero, strlen_end    # 检查是否为'\0'
    addi t1, t1, 1      # 长度++
    j strlen_loop
strlen_end:
    # t1 包含字符串长度
位操作技巧
GAS
# 检查第n位是否为1
li t1, 1
sll t1, t1, t0      # t1 = 1 << n
and t3, t2, t1      # 测试 t2 的第n位
bne t3, zero, bit_is_set    # 如果位为1则跳转

# 设置第n位为1
li t1, 1
sll t1, t1, t0      # t1 = 1 << n
or t2, t2, t1       # t2 |= (1 << n)

# 清除第n位
li t1, 1
sll t1, t1, t0      # t1 = 1 << n
not t1, t1          # t1 = ~(1 << n)
and t2, t2, t1      # t2 &= ~(1 << n)

# 切换第n位
li t1, 1
sll t1, t1, t0      # t1 = 1 << n
xor t2, t2, t1      # t2 ^= (1 << n)

# 计算2的幂次
li t1, 1
sll t1, t1, t0      # t1 = 2^t0

# 除以2的幂次(无符号)
srl t1, t2, t0      # t1 = t2 / (2^t0)

# 除以2的幂次(有符号)
sra t1, t2, t0      # t1 = t2 / (2^t0) (保持符号)
内存对齐和优化
GAS
# 8字节对齐检查
andi t1, t0, 7      # 检查低3位
bne t1, zero, not_aligned   # 如果不为0则未对齐

# 向上对齐到8字节边界
addi t0, t0, 7      # t0 += 7
andi t0, t0, -8     # t0 &= ~7

# 向下对齐到8字节边界
andi t0, t0, -8     # t0 &= ~7

# 快速清零内存块
mv t1, t0           # 保存起始地址
add t2, t0, t3      # 计算结束地址
clear_loop:
    bge t1, t2, clear_end
    sd zero, 0(t1)  # 存储0
    addi t1, t1, 8  # 递增地址
    j clear_loop
clear_end:
64位特有操作
GAS
# 32位运算并符号扩展
addw t0, t1, t2     # 32位加法,结果符号扩展到64位
subw t0, t1, t2     # 32位减法,结果符号扩展到64位
mulw t0, t1, t2     # 32位乘法,结果符号扩展到64位

# 64位立即数加载
li t0, 0x123456789abcdef0   # 加载64位立即数

# 混合32位和64位操作
lwu t0, 0(t1)       # 加载32位值,零扩展到64位
lw t0, 0(t1)        # 加载32位值,符号扩展到64位
sw t0, 0(t1)        # 存储32位值
sd t0, 0(t1)        # 存储64位值

# 字移位操作
slliw t0, t1, 5     # 32位左移5位,符号扩展到64位
srliw t0, t1, 5     # 32位逻辑右移5位,符号扩展到64位
sraiw t0, t1, 5     # 32位算术右移5位,符号扩展到64位

这些编程模式涵盖了RISC-V 64位汇编中最常用的结构和技巧,可以作为编写RISC-V 64位汇编程序的参考模板。