我是靠谱客的博主 大意枫叶,最近开发中收集的这篇文章主要介绍OpenSBI ELF rela.dyn和.dynsym动态链接过程rela.dyn.dynsym.symtab和.dynsym区别OpenSBI中对.dynsym的操作参考:,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

在OpenSBI中重定位分成了两种,根据是否配置了FW_PIC宏来区分,

配置了FW_PIC,即本文描述的rela.dyn和.dynsym的动态链接。

未配置FW_PIC是加载地址和链接地址不相等情况下的代码拷贝重定位。

rela.dyn

.rela.dyn节是什么节呢?该节保存的是重定位信息,数据内容是包含带有显式加数的重定位条目,每个条目固定大小(24个字节)。话又说回来,什么是重定位?
这里引用oracle网站的解答:重定位是连接符号引用与符号定义的过程。例如,程序调用函数时,关联的调用指令必须在执行时将控制权转移到正确的目标地址。可重定位文件必须包含说明如何修改其节内容的信息。通过此信息,可执行文件和共享目标文件可包含进程的程序映像的正确信息。重定位项即是这些数据。
了解了重定位,现在回到开头的问题,.rela.dyn节是什么节?.rela.dyn在动态链接的目标文件中保存的是需要被重定位的变量数据。
opensbi使用的是tag v1.0 实验注释代码
使用readelf读取fw_payload.elf 中的rel.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......
.....
已知每个条目固定大小24个字节,那么我们可以计算出一共有243个条目,即0x16c8/24=243,现在我们看看每个条目的构成,即每个条目的结构组成.
使用readelf命令查看动态链接表
$ 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 : 0x800160c0
r_addend : 0x80000d40

还没有重定位前,上面两个地址的内容如下

(gdb) x 0x800160c0
0x800160c0 <ipi_smode_ops+48>:  0x00000000
(gdb) x 0x80000d40
0x80000d40 <sbi_ipi_process_smode>:     0xe4221141

第一轮执行完"2:"标签后的内容如下:

(gdb) x 0x800160c0
0x800160c0 <ipi_smode_ops+48>:  0x80000d40
(gdb) x 0x80000d40
0x80000d40 <sbi_ipi_process_smode>:     0xe4221141

在0x800160c0中的内容填入了0x80000d40的地址

在查看其他.rela.dyn中的条目,

(gdb) x 0x80000e04
0x80000e04 <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                      ........

st_name: 0x197 (在符号表中的偏移量)
st_info: 0x11
st_other: 0x0
st_shndx:0x5
st_value: 0x800168c8
st_size: 0x10
说明符号名称在动态符号字符串表中的偏移量是0x197,大小为0x10。如下图从动态符号字符串表中根据索引查询到该符号名称是fdt_serial_uart8
$ 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
       00000000000020a8  0000000000000000   A       0     0     8
  [ 3] .dynstr           STRTAB           00000000800150a8  000151c8
       000000000000028b  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

为什么这里长度是16字节,不对的呀,应该是19字节呢?
使用readelf查看.dynsym段内容
$ 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

    */

参考:

ELF的.rela.dyn节 - 编程和调试

简要剖析ELF文件动态链接会用到的段 - 知乎

Executable and Linkable Format (ELF)

最后

以上就是大意枫叶为你收集整理的OpenSBI ELF rela.dyn和.dynsym动态链接过程rela.dyn.dynsym.symtab和.dynsym区别OpenSBI中对.dynsym的操作参考:的全部内容,希望文章能够帮你解决OpenSBI ELF rela.dyn和.dynsym动态链接过程rela.dyn.dynsym.symtab和.dynsym区别OpenSBI中对.dynsym的操作参考:所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(45)

评论列表共有 0 条评论

立即
投稿
返回
顶部