RISCV AS汇编器

文章目录

    • [1. 编译流程](#1. 编译流程)
    • [2. 可执行文件](#2. 可执行文件)
      • [2.1 可重定位目标文件与可执行目标文件](#2.1 可重定位目标文件与可执行目标文件)
    • [3. 汇编语法](#3. 汇编语法)
      • [3.1 注释](#3.1 注释)
      • [3.2 标签和符号](#3.2 标签和符号)
      • [3.3 指令伪指令](#3.3 指令伪指令)
        • [3.3.1 对齐伪指令](#3.3.1 对齐伪指令)
        • [3.3.2 数据定义伪指令](#3.3.2 数据定义伪指令)
        • [3.3.3 函数相关伪指令](#3.3.3 函数相关伪指令)
        • [3.3.4 与段相关的伪指令](#3.3.4 与段相关的伪指令)
        • [3.3.5 宏](#3.3.5 宏)
        • [3.3.6 文件相关的伪指令](#3.3.6 文件相关的伪指令)
    • [4. RISCV 汇编编译选项](#4. RISCV 汇编编译选项)

1. 编译流程

通常情况下,编译器编译一个可执行文件包含以下几个步骤:

  1. 预处理
  2. 编译
  3. 汇编
  4. 链接

输入层
gcc -E
gcc -S
gcc -c
gcc -o
阶段4: 链接 (Linker)
合并多个 .o 文件
解析符号引用

跨文件函数调用
链接标准库

libc / libm / libstdc++
链接启动代码

crt0 / crt1
地址重定位

分配最终内存地址
输出: 可执行文件

ELF / PE / Mach-O
阶段3: 汇编 (Assembler)
汇编指令 → 机器码

二进制操作码
生成符号表

函数/变量地址映射
生成重定位信息

未解析的外部引用
输出: 目标文件

*.o / *.obj
阶段2: 编译 (Compiler Proper)
词法分析

Token化
语法分析

生成 AST/GENERIC
语义分析

类型检查
中间表示转换

GENERIC → GIMPLE → RTL
优化 Pass

SSA、内联、循环优化等
代码生成

输出汇编指令
输出: 汇编代码

*.s 文件
阶段1: 预处理 (Preprocessor)
处理 #include

头文件展开
处理 #define

宏替换
条件编译 #ifdef

选择性保留代码
删除注释

添加行号标记
输出: 纯净C代码

*.i 文件
源代码文件

*.c / *.cpp / *.h

2. 可执行文件

可执行与可链接格式(Executable and Linkable Format, ELF),一种文件格式

常见的段:

  • 代码段(.text):存放程序的可执行机器指令(编译后的二进制代码)。
  • 只读数据段(.rodata):存放只读常量数据。
  • 数据段(.data):存放已初始化的全局变量和静态变量。
  • BSS段(.bss):存放未初始化或初始化为 0 的全局/静态变量。
  • 符号段(.symtab):记录程序中的符号信息(函数名、变量名及其地址)。
  • 可重定位代码段(.rel.text):记录代码中需要重定位的位置。
  • 可重定位数据段(.rel.data):记录数据段中需要重定位的位置。
  • 调试符号段(.debug):存放调试信息,供 GDB 等调试器使用。

2.1 可重定位目标文件与可执行目标文件

可重定位目标文件:在链接阶段和其他可重定位目标文件合并成一个可执行文件

  • 包含代码和数据
  • 所有段的起始地址不确定,用0填充
  • 可重定位代码段(.rel.text)
  • 可重定位数据段(.rel.data)

可执行目标文件:可以执行的目标文件

  • 包含代码和数据
  • 所有段的起始地址都已经确定

3. 汇编语法

3.1 注释

在.S文件中支持三种注释:

  • "//" 单行注释
  • "#" 在一行开始表示注释整行
  • "/**/" 多行注释

注: .S文件会经过预处理器处理,可以使用C语言中的注释方式,经过预处理以后会被丢弃,.s文件中只能用汇编注释#。

3.2 标签和符号

  • 标签 (Label):后面紧跟冒号 :,代表所在位置的地址。常用于跳转或函数入口。start: 或 loop_start: 都是标签。
  • 符号 (Symbol):指代一段数据或代码的名字,但不一定需要紧跟冒号。通过 .equ、.set、#define(仅 .S)定义的常量,或用 .globl 导出的函数名,都是符号。
asm 复制代码
label_name:          # 普通标签(冒号结尾)
    instruction

1:                   # 局部标签(纯数字,用于临时跳转)
    instruction
    jmp 1f           # 1f = 向前(forward)找下一个 "1:"
    jmp 1b           # 1b = 向后(backward)找上一个 "1:"
类型 示例 说明
全局符号 main:_start: 默认局部可见,需 .global 导出
局部符号 main:_start: 默认局部可见,通常用.local声明
局部标签 1:2: 临时标记,配合 f/b 使用
符号赋值 symbol = 0x1000 等价于常量,不占内存
等值伪指令 .set name, 0x100 定义符号常量

符号代表所在的地址,也可以当作变量或者函数来使用,符号由字母,数字,'_' 和'.'和"$"组成。

局部标签不一定是符号(数字标签就不是),局部符号一定是符号表中的条目。

3.3 指令伪指令

指令是真正的机器指令,汇编后直接生成对应的机器码,并且寄存器必须小写,指令可以是全大写或者全小写。

txt 复制代码
[前缀] 操作码 [源操作数], [目的操作数]

伪指令以点 . 开头,是汇编器的命令,不生成机器码,控制汇编过程。

伪指令可以实现如下功能:

  • 符号定义
  • 数据定义和对齐
  • 汇编控制
  • 汇编宏
  • 段描述
3.3.1 对齐伪指令
  1. .align 对齐,填充数据来实现对齐,可以填入0或者nop指令
  2. 告诉汇编器,.align 后面的汇编必须从下一个能被2^n整除的地址开始分配
  3. RISCV中第一个参数表示2^n大小
txt 复制代码
.align  expression [, fill_value [, max_bytes]]
参数 说明 默认值
expression 对齐边界(含义因架构而异) 必需
fill_value 填充字节值 0(x86)/ 0x00 或 nop(其他)
max_bytes 最大填充字节数,超过则跳过对齐 无限制
3.3.2 数据定义伪指令

基本数据定义伪指令:

伪指令 数据大小 说明 示例
.byte 8-bit (1 字节) 定义字节 .byte 0xFF, -1, 'A'
.short / .hword 16-bit (2 字节) 定义半字 .short 0x1234, -300
.long / .int/.word 32-bit (4 字节) 定义字 .long 0x12345678
.quad 64-bit (8 字节) 定义四字 .quad 0x123456789ABCDEF0
.octa 128-bit (16 字节) 定义八字 .octa 0x...
.single / .float 32-bit 单精度浮点 .float 3.14, -0.5
.double 64-bit 双精度浮点 .double 3.1415926535

字符串定义伪指令:

伪指令 自动补 \0 说明 示例
.ascii ❌ 否 ASCII 字符串(原始) .ascii "Hello"
.asciz ✅ 是 ASCII + 自动补零 .asciz "Hello"
.string ✅ 是 .asciz .string "Hello"
.string8 ✅ 是 8-bit 字符 .string8 "Hello"
.string16 ✅ 是 16-bit 宽字符 .string16 "Hello"
.string32 ✅ 是 32-bit 宽字符 .string32 "Hello"
3.3.3 函数相关伪指令

.global 定义一个全局符号
.include 引用头文件
.if , .else, .endif 控制语句

3.3.4 与段相关的伪指令
  1. .section
txt 复制代码
.section name,"flags"

.section: 表示接下来的数据或者指令会链接到哪个段中,每一个段以段名为开始,以下一个段名或者文件末尾为结束。

字符 全称 含义 ELF 段头标志
a Allocatable 段在运行时分配内存 SHF_ALLOC
e Exclude 链接时排除该段 SHF_EXCLUDE
w Writable 段内容可写 SHF_WRITE
x Executable 段内容可执行 SHF_EXECINSTR
M Mergeable 相同内容段可合并 SHF_MERGE
S Strings 段包含 null 结尾字符串 SHF_STRINGS
G Group 段属于 COMDAT 组 SHF_GROUP
T TLS 线程局部存储 SHF_TLS
? 保留 未知/保留标志 ---
  1. .pushsection.popsection
txt 复制代码
.pushsection name, "flags", @type
    # ... 在新段中定义内容 ...
.popsection
伪指令 作用
.pushsection 保存当前段上下文,切换到指定新段
.popsection 恢复之前保存的段上下文
  1. .section.previous
txt 复制代码
初始状态: 当前在 .text 段

.section .data, "aw", @progbits   ──► 切换到 .data
                                    记住: 上一个段是 .text
    [在 .data 中定义数据]

.previous                         ──► 回到 .text(上一个段)
    [继续在 .text 写代码]

.section .rodata, "a", @progbits  ──► 切换到 .rodata
                                    记住: 上一个段是 .text(.previous 后的当前段)
    [定义只读数据]

.previous                         ──► 回到 .text
特性 .section + .previous .pushsection + .popsection
记忆层数 仅一层(上一个段) 多层栈(支持嵌套)
切换方式 直接切换,覆盖记忆 压栈保存,精确恢复
嵌套支持 ❌ 不支持嵌套(会丢失更早的段) ✅ 支持任意深度嵌套
适用场景 简单两段切换 复杂多段嵌套、编译器生成
安全性 容易误用(忘记当前段) 栈结构,成对使用更安全
3.3.5 宏
  1. 汇编中的宏通过.macro.endm 定义
  2. .macro 后面是宏的名称,然后是跟宏的参数
  3. 在宏里面使用参数要加"\"
asm 复制代码
.macro add_1 p1 p2
add x0, \p1, \p2
.endm
  1. 宏定义的时候可以设置一个初始值:
asm 复制代码
.macro reserve_str p0=0,p2
.endm

# 由于第一个参数有一个默认值,可以省略默认值
reserve_str ,b
reserve_str a,b
  1. 宏参数后面加.req表示在宏调用过程中必须传递一个值,否则会编译出错
3.3.6 文件相关的伪指令

.incbin 伪指令可以把文件的二进制数据嵌入到当前位置。

.include 伪指令可以把汇编代码插入到当前指令所在位置。

4. RISCV 汇编编译选项

选项 作用 常用值
-fpic / -fPIC 位置无关代码(共享库) -fPIC(大模型)
-fno-pic 绝对地址代码(裸机/静态) 默认裸机
-mabi= 整数/浮点/指针宽度 lp64d, ilp32
-march= 指令集扩展 rv64gc, rv32imac
-misa-spec= ISA 规范版本 20191213
-mlittle-endian 小端存储(默认) -
-mbig-endian 大端存储 -
相关推荐
嵌入式小企鹅18 小时前
UiPath推出AI编程“总指挥台”,SiFive发布RISC-V第三代猛兽
人工智能·学习·google·程序员·ai编程·risc-v·开源工具
阿祖_in_coding3 天前
RISC-V ACT测试
risc-v
yusur3 天前
开芯院院长唐丹一行来访中科驭数 共探RISC-V与DPU算力协同创新之路
risc-v
加强洁西卡3 天前
【RISC-V】RVV选摘
risc-v
加强洁西卡4 天前
【RISC-V】fclass数值类型对照表
risc-v
国科安芯5 天前
空间辐射环境下电机伺服系统的抗扰动控制:AS32S601 抗辐射 MCU 在航天机电执行机构中的多场景应用与可靠性评估
单片机·嵌入式硬件·mcu·cocos2d·risc-v
国科安芯5 天前
AS32S601 抗辐射 MCU 在星载高速光通信链路的集成设计与性能验证
网络·单片机·嵌入式硬件·risc-v·安全性测试
国科安芯5 天前
抗辐射 MCU 赋能商业航天电源系统:基于 AS32S601 的高可靠能量管理控制器设计与辐照验证
stm32·单片机·嵌入式硬件·mcu·risc-v·空间计算
硬汉嵌入式8 天前
实现H7-TOOL脱机烧录沁恒RISC-V内核单线模式的CH32V003,至此单线和双线模式都支持了
risc-v·ch32v003·h7-tool·脱机烧录·1拖4脱机烧录·1拖16脱机烧录·ch32v20x