在OpenSBI中重定位分成了两种,根据是否配置了FW_PIC宏来区分,
配置了FW_PIC,即本文描述的rela.dyn和.dynsym的动态链接。
未配置FW_PIC是加载地址和链接地址不相等情况下的代码拷贝重定位。
rela.dyn
$ riscv64-linux-gnu-readelf -S build/platform/generic/firmware/fw_payload.elf
There are 26 section headers, starting at offset 0xf66c0:
Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .text PROGBITS 0000000080000000 00000120
0000000000012d20 0000000000000000 WAX 0 0 8
[ 2] .rodata PROGBITS 0000000080013000 00013120
00000000000020a0 0000000000000000 A 0 0 8
[ 3] .dynstr STRTAB 00000000800150a0 000151c0
000000000000028b 0000000000000000 A 0 0 1
[ 4] .gnu.hash GNU_HASH 0000000080015330 00015450
0000000000000100 0000000000000000 A 10 0 8
[ 5] .data PROGBITS 0000000080016000 00016120
0000000000000ef8 0000000000000000 WA 0 0 8
[ 6] .dynamic DYNAMIC 0000000080016ef8 00017018
0000000000000110 0000000000000010 WA 3 0 8
[ 7] .got PROGBITS 0000000080017008 00017128
0000000000000120 0000000000000008 WA 0 0 8
[ 8] .got.plt PROGBITS 0000000080017128 00017248
0000000000000010 0000000000000008 WA 0 0 8
[ 9] .htif PROGBITS 0000000080017138 00017258
0000000000000010 0000000000000000 WA 0 0 8
[10] .dynsym DYNSYM 0000000080017148 00017268
0000000000000378 0000000000000018 A 3 2 8
[11] .rela.dyn RELA 00000000800174c0 000175e0
00000000000016c8 0000000000000018 A 10 0 8
[12] .bss NOBITS 0000000080019000 00018ca8
0000000000022d70 0000000000000000 WA 0 0 8
[13] .payload PROGBITS 0000000080200000 00018cb0
0000000000002128 0000000000000000 AX 0 0 16
[14] .debug_line PROGBITS 0000000000000000 0001add8
0000000000034428 0000000000000000 0 0 1
[15] .debug_info PROGBITS 0000000000000000 0004f200
000000000003cc8c 0000000000000000 0 0 1
[16] .debug_abbrev PROGBITS 0000000000000000 0008be8c
000000000000ba21 0000000000000000 0 0 1
[17] .debug_aranges PROGBITS 0000000000000000 000978b0
0000000000000fd0 0000000000000000 0 0 16
[18] .debug_str PROGBITS 0000000000000000 00098880
0000000000006b7e 0000000000000001 MS 0 0 1
[19] .debug_ranges PROGBITS 0000000000000000 0009f400
0000000000009930 0000000000000000 0 0 16
[20] .debug_loc PROGBITS 0000000000000000 000a8d30
000000000003e388 0000000000000000 0 0 1
[21] .comment PROGBITS 0000000000000000 000e70b8
0000000000000029 0000000000000001 MS 0 0 1
[22] .debug_frame PROGBITS 0000000000000000 000e70e8
0000000000007560 0000000000000000 0 0 8
[23] .symtab SYMTAB 0000000000000000 000ee648
0000000000004c38 0000000000000018 24 404 8
[24] .strtab STRTAB 0000000000000000 000f3280
0000000000003351 0000000000000000 0 0 1
[25] .shstrtab STRTAB 0000000000000000 000f65d1
00000000000000ed 0000000000000000 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
p (processor specific)
.rela.dyn 偏移为0x175e0, 大小为0x16c8
读取.rela.dyn
$ xxd -s 0x175e0 -l 0x16c8 build/platform/generic/firmware/fw_payload.elf
000175e0: c060 0180 0000 0000 0300 0000 0000 0000 .`..............
000175f0: 400d 0080 0000 0000 f860 0180 0000 0000 @........`......
00017600: 0300 0000 0000 0000 040e 0080 0000 0000 ................
00017610: 0061 0180 0000 0000 0300 0000 0000 0000 .a..............
00017620: 0061 0180 0000 0000 0861 0180 0000 0000 .a.......a......
00017630: 0300 0000 0000 0000 0061 0180 0000 0000 .........a......
00017640: 3061 0180 0000 0000 0300 0000 0000 0000 0a..............
00017650: 4a2b 0080 0000 0000 3861 0180 0000 0000 J+......8a......
00017660: 0300 0000 0000 0000 ee2b 0080 0000 0000 .........+......
00017670: 4061 0180 0000 0000 0300 0000 0000 0000 @a..............
00017680: ca2a 0080 0000 0000 a061 0180 0000 0000 .*.......a......
00017690: 0300 0000 0000 0000 b861 0180 0000 0000 .........a......
000176a0: b061 0180 0000 0000 0300 0000 0000 0000 .a..............
000176b0: a07e 0380 0000 0000 b861 0180 0000 0000 .~.......a......
000176c0: 0300 0000 0000 0000 f434 0080 0000 0000 .........4......
000176d0: c061 0180 0000 0000 0300 0000 0000 0000 .a..............
000176e0: fa35 0080 0000 0000 c861 0180 0000 0000 .5.......a......
000176f0: 0300 0000 0000 0000 1c35 0080 0000 0000 .........5......
00017700: d061 0180 0000 0000 0300 0000 0000 0000 .a..............
00017710: 4235 0080 0000 0000 e861 0180 0000 0000 B5.......a......
00017720: 0300 0000 0000 0000 ae35 0080 0000 0000 .........5......
00017730: f061 0180 0000 0000 0300 0000 0000 0000 .a..............
00017740: 9435 0080 0000 0000 f861 0180 0000 0000 .5.......a......
00017750: 0300 0000 0000 0000 c835 0080 0000 0000 .........5......
.....
$ riscv64-linux-gnu-readelf -rW build/platform/generic/firmware/fw_payload.elf
Relocation section '.rela.dyn' at offset 0x175e0 contains 243 entries:
Offset Info Type Symbol's Value Symbol's Name + Addend
00000000800160c0 0000000000000003 R_RISCV_RELATIVE 80000d40
00000000800160f8 0000000000000003 R_RISCV_RELATIVE 80000e04
......
0000000080017108 0000000000000003 R_RISCV_RELATIVE 8000278e
0000000080017110 0000000000000003 R_RISCV_RELATIVE 8000b448
0000000080017118 0000000000000003 R_RISCV_RELATIVE 80016d60
0000000080017120 0000000000000003 R_RISCV_RELATIVE 80016590
0000000080016798 0000000200000002 R_RISCV_64 00000000800168c8 fdt_serial_uart8250 + 0
0000000080016a08 0000002100000002 R_RISCV_64 0000000080016ac8 fdt_poweroff_gpio + 0
和我们计算出来的条目数一致。
typedef struct
{
Elf64_Addr r_offset; /* Address */
Elf64_Xword r_info; /* Relocation type and symbol index */
Elf64_Sxword r_addend; /* Addend */
} Elf64_Rela;
每个条目分别由3个部分组成:r_offset、r_info和r_addend,每个部分占8个字节,正好3*8=24个字节(每个条目的大小)
r_offset
本字段保存的是重定位所作用的位置。对于重定位文件来说,此值是受重定位作用的存储单元在节中的字节偏移量;对于可执行文件或共享目标文件来说,此值是受重定位作用的存储单元的虚拟地址。(可变值(通常是位置无关的)虚拟内存地址,在重定位过程中保存“patched”值)
r_info
该字段指定必须对其进行重定位的符号表索引以及要应用的重定位类型。例如,调用指令的重定位项包含所调用的函数的符号表索引。如果索引是未定义的符号索引 STN_UNDEF,则重定位将使用零作为符号值。重定位类型特定于处理器。重定位项的重定位类型或符号表索引是将 ELF64_R_TYPE 或 ELF64_R_SYM 分别应用于项的 r_info 成员所得的结果,计算过程如下:
#define ELF64_R_SYM(i) ((i) >> 32)
#define ELF64_R_TYPE(i) ((i) & 0xffffffff)
#define ELF64_R_INFO(sym,type) ((((Elf64_Xword) (sym)) << 32) + (type))
r_addend
该字段是一个常量加数,用于计算存储在可重定位字段中的值。
第一个条目分析
我们先看看.rela.dyn节的第一个条目,已知偏移量是0x175e0,那么我们从0x175e0的地方开始取24个字节,如下:
$ xxd -s 0x175e0 -l 24 build/platform/generic/firmware/fw_payload.elf
000175e0: c060 0180 0000 0000 0300 0000 0000 0000 .`..............
000175f0: 400d 0080 0000 0000 @.......
r_offset
r_offset是0x800160c0, 该处的代码或数据会被(修改)重定位,需要进行扫描类型的重定位得看r_info
r_info
r_info是0x3,类型是R_RISCV_RELATIVE
/* RISC-V relocations. */
#define R_RISCV_NONE 0
#define R_RISCV_32 1
#define R_RISCV_64 2
#define R_RISCV_RELATIVE 3
#define R_RISCV_COPY 4
#define R_RISCV_JUMP_SLOT 5
#define R_RISCV_TLS_DTPMOD32 6
#define R_RISCV_TLS_DTPMOD64 7
#define R_RISCV_TLS_DTPREL32 8
#define R_RISCV_TLS_DTPREL64 9
#define R_RISCV_TLS_TPREL32 10
#define R_RISCV_TLS_TPREL64 11
#define R_RISCV_BRANCH 16
#define R_RISCV_JAL 17
根据类型可以进行计算存储在重定位字段中的值,由于是RELATIVE类型,所以计算法是B+A,B表示执行过程中将共享目标文件装入内存的基本地址。通常,生成的共享目标文件的基本虚拟地址为0,A表示常量加数,用于计算存储在可重定位字段中的值。
r_addend
r_addend是0x80000d40,该字段即上面说的B+A中的A
现在我们可以看出B是0,A是0x80000d40,B+A=0x80000d40,符号名是0x80000d40,说明第一条目标是符号名称为0x80000d40的重定位条目。
以上第一个条目和使用命令riscv64-linux-gnu-readelf -rW build/platform/generic/firmware/fw_payload.elf看到的一模一样
在qemu上运行验证结果
查看opensbi重定位,究竟定位的是什么
启动一个终端
$ qemu-system-riscv64 -M virt -m 256M -nographic -bios build/platform/generic/firmware/fw_payload.bin -s -S
另启动一个终端
$ gdb-multiarch --tui build/platform/generic/firmware/fw_payload.elf -ex 'target remote localhost:1234'
(gdb) b _start
Breakpoint 1 at 0x80000000: file /workspace/richard/project/risc-v/opensbi/firmware/fw_base.S, line 51.
(gdb) layout regs
(gdb) c
执行s指令将程序运行到 fw_base.S中 "2:标签"
2: /*判断.rele.dyn每个条目的类型*/
REG_L t5, -(REGBYTES*2)(t0) /* t5 <-- relocation info:type */
li t3, R_RISCV_RELATIVE /* reloc type R_RISCV_RELATIVE */
/*如果type不是R_RISCV_RELATIVE,则跳转到3f*/
bne t5, t3, 3f
/*
当 type为R_RISCV_RELATIVE,程序走这里
以第一个条目为例
t3是r_offset 0x800160c0
t5是r_addend 0x80000d40
将运行时地址存放在Got表项中
*/
REG_L t3, -(REGBYTES*3)(t0) /*将r_offset加载到t3中*/
REG_L t5, -(REGBYTES)(t0) /* t5 <-- r_addend */
add t5, t5, t2 /*t2(加载地址-链接地址= offset),t2是0*/
add t3, t3, t2
REG_S t5, 0(t3) /* store runtime address to the GOT entry */
j 5f
根据之前的分析,第一个.rela.dyn条目的内容如下
r_offset : 0x800160c0r_addend : 0x80000d40
还没有重定位前,上面两个地址的内容如下
(gdb) x 0x800160c00x800160c0 <ipi_smode_ops+48>: 0x00000000(gdb) x 0x80000d400x80000d40 <sbi_ipi_process_smode>: 0xe4221141
第一轮执行完"2:"标签后的内容如下:
(gdb) x 0x800160c00x800160c0 <ipi_smode_ops+48>: 0x80000d40(gdb) x 0x80000d400x80000d40 <sbi_ipi_process_smode>: 0xe4221141
在0x800160c0中的内容填入了0x80000d40的地址
在查看其他.rela.dyn中的条目,
(gdb) x 0x80000e040x80000e04 <sbi_ipi_process_halt>: 0xe0221141
这些函数都来自lib中
.dynsym
.dynsym 中存放了共享库所需要在runtime加载的函数对应的symbols。
.dynsym 保存的是一个动态符号表,其中每个条目的大小是固定的24个字节,先看看.dynsym节的属性,即描述条目信息
$ riscv64-linux-gnu-readelf -S build/platform/generic/firmware/fw_payload.elf
There are 26 section headers, starting at offset 0xf66c0:
Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
......
[ 6] .dynamic DYNAMIC 0000000080016ef8 00017018
0000000000000110 0000000000000010 WA 3 0 8
[ 7] .got PROGBITS 0000000080017008 00017128
0000000000000120 0000000000000008 WA 0 0 8
[ 8] .got.plt PROGBITS 0000000080017128 00017248
0000000000000010 0000000000000008 WA 0 0 8
[ 9] .htif PROGBITS 0000000080017138 00017258
0000000000000010 0000000000000000 WA 0 0 8
[10] .dynsym DYNSYM 0000000080017148 00017268
0000000000000378 0000000000000018 A 3 2 8
[11] .rela.dyn RELA 00000000800174c0 000175e0
00000000000016c8 0000000000000018 A 10 0 8
[12] .bss NOBITS 0000000080019000 00018ca8
0000000000022d70 0000000000000000 WA 0 0 8
.......
dynsym section的offset为0x17268, 大小为0x378
.dynsym 中每个条目大小是0x18字节,所以0x378/0x18 = 37,一共37个条目。每个条目的构成都是相同的。下面查看哪些字段构成一个条目:
typedef struct
{
Elf64_Word st_name; /* Symbol name (string tbl index) 4字节*/
unsigned char st_info; /* Symbol type and binding 1字节*/
unsigned char st_other; /* Symbol visibility 1字节*/
Elf64_Section st_shndx; /* Section index 2字节*/
Elf64_Addr st_value; /* Symbol value 8字节*/
Elf64_Xword st_size; /* Symbol size 8字节*/
} Elf64_Sym;
我是64位系统,所以看64位的符号表结构体
st_name:长度是4个字节,该字段表示符号名称在动态符号字符串表中的偏移量或者说索引
st_info: 长度是1个字节,该字段表示符号的类型和绑定属性。
st_other: 长度是1个字节,表示符号的可见性。
st_shndx:长度是2个字节,每个符号条目的定义都与某些节对应,该字段保存的是相关节头表的索引,为0表示未定义。
st_value:长度是8个字节,表示符号的值,可能是地址或者位置偏移量
st_size: 长度是8个字节,表示的是符号的大小,如全局函数指针的大小。大多数符号都有大小,如果没有则此字段的内容是0。
直接用xxd命令查看第二个段
第二个段的偏移:0x17268+24*2 = 0x17298
$ xxd -s 0x17298 -l 24 build/platform/generic/firmware/fw_payload.elf
00017298: 9701 0000 1100 0500 c868 0180 0000 0000 .........h......
000172a8: 1000 0000 0000 0000 ........
$ riscv64-linux-gnu-readelf -S build/platform/generic/firmware/fw_payload.elfThere are 26 section headers, starting at offset 0xf66c0:Section Headers:[Nr] Name Type Address OffsetSize EntSize Flags Link Info Align[ 0] NULL 0000000000000000 000000000000000000000000 0000000000000000 0 0 0[ 1] .text PROGBITS 0000000080000000 000001200000000000012d20 0000000000000000 WAX 0 0 8[ 2] .rodata PROGBITS 0000000080013000 0001312000000000000020a8 0000000000000000 A 0 0 8[ 3] .dynstr STRTAB 00000000800150a8 000151c8000000000000028b 0000000000000000 A 0 0 1...
符号表的偏移是0x151c8, 字符串在符号表的偏移是0x197, 0x151c8+0x197 = 0x1535f
$ xxd -s 0x1535f -l 0x10 build/platform/generic/firmware/fw_payload.elf
0001535f: 6664 745f 7365 7269 616c 5f75 6172 7438 fdt_serial_uart8
$ riscv64-linux-gnu-readelf --dyn-syms build/platform/generic/firmware/fw_payload.elf
Symbol table '.dynsym' contains 37 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000080000000 0 SECTION LOCAL DEFAULT 1
2: 00000000800168c8 16 OBJECT GLOBAL DEFAULT 5 fdt_serial_uart8250
3: 00000000800165f0 48 OBJECT GLOBAL DEFAULT 5 ecall_vendor
4: 00000000800381e0 1024 OBJECT GLOBAL DEFAULT 12 hartid_to_domain_table
5: 0000000080002854 26 FUNC GLOBAL DEFAULT 1 sbi_tlb_local_fence_i
6: 00000000800165c0 48 OBJECT GLOBAL DEFAULT 5 ecall_time
7: 0000000080016cc0 32 OBJECT GLOBAL DEFAULT 5 fdt_ipi_mswi
8: 0000000080002948 134 FUNC GLOBAL DEFAULT 1 sbi_tlb_local_hfence_vvma
9: 0000000080016c58 32 OBJECT GLOBAL DEFAULT 5 fdt_irqchip_plic
10: 0000000080016530 48 OBJECT GLOBAL DEFAULT 5 ecall_srst
11: 000000008000a3a0 0 NOTYPE GLOBAL DEFAULT 1 __sbi_expected_trap_hext
12: 000000008000b448 0 NOTYPE GLOBAL DEFAULT 1 __thead_pre_start_warm
13: 0000000080016038 4 OBJECT GLOBAL DEFAULT 5 last_hartid_having_scratc
14: 0000000080016590 48 OBJECT GLOBAL DEFAULT 5 ecall_rfence
15: 0000000080016470 48 OBJECT GLOBAL DEFAULT 5 ecall_base
16: 00000000800027e2 114 FUNC GLOBAL DEFAULT 1 sbi_tlb_local_sfence_vma_
17: 000000008000b460 0 NOTYPE GLOBAL DEFAULT 1 __reset_thead_csr_stub
18: 0000000080037a50 1024 OBJECT GLOBAL DEFAULT 12 hartid_to_scratch_table
19: 000000008003bb70 512 OBJECT GLOBAL DEFAULT 12 custom_csr
20: 00000000800163d0 144 OBJECT GLOBAL DEFAULT 5 root
21: 0000000080016d60 24 OBJECT GLOBAL DEFAULT 5 fdt_gpio_sifive
22: 0000000080016d30 16 OBJECT GLOBAL DEFAULT 5 fdt_i2c_adapter_sifive
23: 0000000080016560 48 OBJECT GLOBAL DEFAULT 5 ecall_ipi
24: 00000000800164a0 48 OBJECT GLOBAL DEFAULT 5 ecall_hsm
25: 00000000800028e2 102 FUNC GLOBAL DEFAULT 1 sbi_tlb_local_hfence_gvma
26: 00000000800029ce 108 FUNC GLOBAL DEFAULT 1 sbi_tlb_local_hfence_gvma
27: 000000008000278e 84 FUNC GLOBAL DEFAULT 1 sbi_tlb_local_sfence_vma
28: 000000008000286e 116 FUNC GLOBAL DEFAULT 1 sbi_tlb_local_hfence_vvma
29: 0000000080016250 64 OBJECT GLOBAL DEFAULT 5 sifive_fu540
30: 000000008000b440 0 NOTYPE GLOBAL DEFAULT 1 __fdt_reset_thead_csrr
31: 0000000080016620 8 OBJECT GLOBAL DEFAULT 5 sbi_hart_expected_trap
32: 0000000080016330 64 OBJECT GLOBAL DEFAULT 5 sifive_fu740
33: 0000000080016ac8 16 OBJECT GLOBAL DEFAULT 5 fdt_poweroff_gpio
34: 0000000080016730 32 OBJECT GLOBAL DEFAULT 5 fdt_timer_mtimer
35: 00000000800164d0 48 OBJECT GLOBAL DEFAULT 5 ecall_legacy
36: 0000000080016500 48 OBJECT GLOBAL DEFAULT 5 ecall_pmu
.symtab和.dynsym区别
.symtab和.dynsym两个不同的symbol table, 它们有什么区别?
.dynsym是.symtab的一个子集, 大家都有疑问, 为什么要两个信息重合的结构?
需要先了解allocable/non-allocable ELF section, ELF文件包含一些sections(如code和data)是在运行时需要的, 这些sections被称为allocable; 而其他一些sections仅仅是linker,debugger等工具需要, 在运行时并不需要, 这些sections被称为non-allocable的. 当linker构建ELF文件时, 它把allocable的数据放到一个地方, 将non-allocable的数据放到其他地方. 当OS加载ELF文件时, 仅仅allocable的数据被映射到内存, non-allocable的数据仍静静地呆在文件里不被处理. strip就是用来移除某些non-allocable sections的.
.symtab包含大量linker,debugger需要的数据, 但并不为runtime必需, 它是non-allocable的; .dynsym包含.symtab的一个子集, 比如共享库所需要在runtime加载的函数对应的symbols, 它是allocable的.
因此, 得到答案:
1. strip移除的应是.symtab.
2. nm读取的应是.symtab: 上面发现的libattr等nm结果为空, libpthread nm结果非空应是正常的. 3. 共享库包含的.dynsym是runtime必需的, 是allocable的.
OpenSBI中对.dynsym的操作
在fw_base.S中的第"3:"和"4:"标签是对.dynsym的操作
$ riscv64-linux-gnu-readelf -rW build/platform/generic/firmware/fw_payload.elf
Relocation section '.rela.dyn' at offset 0x175e0 contains 243 entries:
Offset Info Type Symbol's Value Symbol's Name + Addend
......
0000000080017120 0000000000000003 R_RISCV_RELATIVE 80016590
0000000080016798 0000000200000002 R_RISCV_64 00000000800168c8 fdt_serial_uart8250 + 0
0000000080016a08 0000002100000002 R_RISCV_64 0000000080016ac8 fdt_poweroff_gpio + 0
操作的是下面两个条目,分别为第241和242条目
Offset Info Type Symbol's Value Symbol's Name + Addend
0000000080016798 0000000200000002 R_RISCV_64 00000000800168c8 fdt_serial_uart8250 + 0
0000000080016a08 0000002100000002 R_RISCV_64 0000000080016ac8 fdt_poweroff_gpio + 0
分析第一个条目的r_offset r_info和r_addend
$ riscv64-linux-gnu-readelf -S build/platform/generic/firmware/fw_payload.elf
There are 26 section headers, starting at offset 0xf66c0:
Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
.......
[10] .dynsym DYNSYM 0000000080017148 00017268
0000000000000378 0000000000000018 A 3 2 8
[11] .rela.dyn RELA 00000000800174c0 000175e0
00000000000016c8 0000000000000018 A 10 0 8
.......
.rela.dyn在整个文件中的偏移0x175e0, 第241个条目,地址为0x175e0+241*24 = 0x18c78
$ xxd -s 0x18c78 -l 24 build/platform/generic/firmware/fw_payload.elf
00018c78: 9867 0180 0000 0000 0200 0000 0200 0000 .g..............
00018c88: 0000 0000 0000 0000 ........
r_offset: 0x80016798(需要链接的符号地址)
r_info: 0x200000002
r_addend: 0x0
以下是链接详细过程
3:
/*读取__dyn_sym_start起始地址到t4*/
lla t4, __dyn_sym_start
4:/*此时读取的.rely.dyn的type为R_RISCV_64
$ riscv64-linux-gnu-readelf -rW build/platform/generic/firmware/fw_payload.elf
Offset Info Type Symbol's Value Symbol's Name + Addend
0000000080017120 0000000000000003 R_RISCV_RELATIVE 80016590
0000000080016798 0000000200000002 R_RISCV_64 00000000800168c8 fdt_serial_uart8250 + 0
0000000080016a08 0000002100000002 R_RISCV_64 0000000080016ac8 fdt_poweroff_gpio + 0
*/
/*读取.rely.dyn中的r_info信息,r_info为0x200000002*/
REG_L t5, -(REGBYTES*2)(t0) /* t5 <-- relocation info:type */
/*将t5的值逻辑右移0x20位,t6则为0x2,即sym table index为2。在sym table中的第几个条目*/
srli t6, t5, SYM_INDEX /* t6 <--- sym table index */
/*t5为0x2,即relocation type为2*/
andi t5, t5, 0xFF /* t5 <--- relocation type */
/*RELOC_TYPE在64位系统里面为2,t3为2*/
li t3, RELOC_TYPE
/*t3和t5不等才会跳转到5f*/
bne t5, t3, 5f
/* address R_RISCV_64 or R_RISCV_32 cases*/
/*加载r_offset到t3,即t3=0x80016798*/
REG_L t3, -(REGBYTES*3)(t0)
/*RV64中SYM_SIZE=24,t5=24*/
li t5, SYM_SIZE
/*t6=t5*t6 = 24*2 = 48*/
mul t6, t6, t5
/*s5 = t4为.dynsym的起始地址 + t6 */
add s5, t4, t6
/*t0=.rely.dyn 中的r_addend 为0*/
REG_L t6, -(REGBYTES)(t0) /* t0 <-- addend */
/*RV64中,REGBYTES=8,从s5+8地址处读取内存数据到t5中,t5为st_value: 0x800168c8*/
REG_L t5, REGBYTES(s5)
/*t6(r_addend) + t5(st_value) = 0+0x800168c8*/
add t5, t5, t6
/*t2(加载地址-链接地址= offset) = 0*/
add t5, t5, t2 /* t5 <-- location to fix up in RAM */
add t3, t3, t2 /* t3 <-- location to fix up in RAM */
/*将t5(st_value) 存储到t3(r_offset)地址中
t5寄存器的值如下
(gdb) x 0x800168c8
0x800168c8 <fdt_serial_uart8250>: 0x800168d8
t3寄存器的值如下
(gdb) x 0x80016798
0x80016798 <serial_drivers>: 0x00000000
这里将0x800168c8存储到0x80016798对应的内存中,完成动态链接
*/
REG_S t5, 0(t3) /* store runtime address to the variable */
/*
(gdb) x 0x80016798
0x80016798 <serial_drivers>: 0x800168c8
*/