【deepseek】位置无关代码介绍
好的,我来为你详细解释这些概念,并附上 RISC-V 的代码示例。PC 相对寻址auipc%pcrel_hi%pcrel_lo0x10000x800000000x200000000x80000000。
·
好的,我来为你详细解释这些概念,并附上 RISC-V 的代码示例。
1. 位置无关代码(PIC) vs 非位置无关代码
位置无关代码(Position-Independent Code, PIC)
- 定义:代码可以在内存中的任意地址加载并正确执行,不依赖固定的内存地址。
- 关键:使用相对地址(如
PC 相对寻址)而非绝对地址。 - 应用场景:动态链接库、操作系统内核(如 Linux 内核模块)。
- RISC-V 示例:
# 获取全局变量 'global_var' 的地址(PIC) auipc a0, %pcrel_hi(global_var) # 将 global_var 的高20位偏移(相对于PC)加载到a0 addi a0, a0, %pcrel_lo(global_var) # 添加低12位偏移 lw t0, 0(a0) # 读取 global_var 的值auipc会将当前 PC 的高 20 位与偏移相加,生成一个 32 位地址的高 20 位。%pcrel_hi和%pcrel_lo是链接器宏,用于计算符号的偏移。
非位置无关代码
- 定义:代码必须在特定内存地址才能运行,依赖绝对地址。
- 关键:直接使用绝对地址硬编码。
- 应用场景:Bootloader、裸机程序(如嵌入式固件)。
- RISC-V 示例:
# 直接访问固定地址 0x1000 的内存(非PIC) lui a0, 0x1000 # 将 0x1000 的高 20 位加载到 a0 addi a0, a0, 0x0 # 低 12 位为 0 lw t0, 0(a0) # 读取地址 0x1000 的值- 如果代码被加载到非预期的地址(如 0x2000),这段代码会错误地访问
0x1000,而非实际数据的位置。
- 如果代码被加载到非预期的地址(如 0x2000),这段代码会错误地访问
2. 加载地址 vs 运行地址
加载地址(Load Address)
- 定义:程序被加载到内存中的物理地址(如通过 JTAG 烧录到 Flash 的地址)。
- 示例:嵌入式系统中固件可能被加载到 Flash 地址
0x80000000。
运行地址(Runtime Address)
- 定义:程序在运行时预期的内存地址(如代码中硬编码的绝对地址)。
- 示例:如果代码中直接使用
0x20000000访问内存,但实际加载地址是0x80000000,程序会崩溃。
关键区别
- 如果代码是 非PIC,且加载地址 ≠ 运行地址,程序会崩溃。
- 如果代码是 PIC,加载地址可以和运行地址不同,因为使用相对地址。
3. 案例分析:PIC vs 非PIC
场景:
- 代码被编译为在
0x20000000运行(运行地址),但实际加载到0x80000000(加载地址)。 - 代码中需要访问一个全局变量
global_var。
非PIC 代码(失败)
# 假设 global_var 链接在 0x20000000
lui a0, 0x20000 # 高 20 位 = 0x20000
addi a0, a0, 0x004 # 假设 global_var 在 0x20000004
lw t0, 0(a0) # 实际会访问 0x20000004(错误地址)
- 结果:代码试图访问
0x20000004,但实际变量在0x80000004,导致错误。
PIC 代码(成功)
# 使用 PC 相对寻址
auipc a0, %pcrel_hi(global_var) # PC + 高20位偏移
addi a0, a0, %pcrel_lo(global_var) # 添加低12位偏移
lw t0, 0(a0) # 正确读取 global_var(无论加载地址如何)
- 结果:无论加载地址是
0x20000000还是0x80000000,都能正确计算global_var的实际地址。
4. 链接脚本中的加载地址和运行地址
在链接脚本中,可以指定运行地址(VMA)和加载地址(LMA):
MEMORY {
FLASH (rx) : ORIGIN = 0x80000000, LENGTH = 128K # 加载地址
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 32K # 运行地址
}
SECTIONS {
.text : {
*(.text)
} > RAM AT> FLASH # 代码在 FLASH 中加载,但运行时在 RAM
}
- 代码会被加载到
FLASH (0x80000000),但运行时代码期望在RAM (0x20000000)。 - 如果代码是 非PIC,需要重定位(拷贝代码到 RAM 并修正地址);如果是 PIC,可直接运行。
总结
| 特性 | 位置无关代码(PIC) | 非位置无关代码 |
|---|---|---|
| 地址依赖 | 相对地址(PC) | 绝对地址 |
| 灵活性 | 高(可任意加载) | 低(需固定地址) |
| 应用场景 | 动态库、操作系统 | Bootloader、裸机程序 |
| 指令示例 | auipc + addi |
lui + addi |
| 链接要求 | 无需重定位 | 需重定位或固定加载地址 |
理解这些概念后,可以更灵活地设计 RISC-V 程序的内存布局!
更多推荐




所有评论(0)