了解 RISCV 汇编中的 <__libc_init_array>
Understanding <__libc_init_array> in RISCV assembly
我的目标是 运行 在我的 RISCV
模拟器中编译 C
代码。我用 RISCV32I(32 位)编译器编译了一个 C 代码,它以 ELF 格式输出我的代码(然后我选择了 .text
部分并在我的 RISC-V
模拟器中使用它)。完美的。然后,我尝试 运行 在我的模拟器中编译代码,但我发现在 <__libc_init_array>
函数中有 "some" 代码总是跳转到我程序的地址 0,这显然我不想。
反汇编我的输出文件:
000102ec <__libc_init_array>:
102ec: ff010113 addi sp,sp,-16
102f0: 00812423 sw s0,8(sp)
102f4: 01212023 sw s2,0(sp)
102f8: 00001417 auipc s0,0x1
102fc: 3c840413 addi s0,s0,968 # 116c0 <__init_array_start>
10300: 00001917 auipc s2,0x1
10304: 3c090913 addi s2,s2,960 # 116c0 <__init_array_start>
10308: 40890933 sub s2,s2,s0
1030c: 00112623 sw ra,12(sp)
10310: 00912223 sw s1,4(sp)
10314: 40295913 srai s2,s2,0x2
10318: 00090e63 beqz s2,10334 <__libc_init_array+0x48>
1031c: 00000493 li s1,0
10320: 00042783 lw a5,0(s0)
10324: 00148493 addi s1,s1,1
10328: 00440413 addi s0,s0,4
1032c: 000780e7 jalr a5
10330: fe9918e3 bne s2,s1,10320 <__libc_init_array+0x34>
10334: 00001417 auipc s0,0x1
10338: 38c40413 addi s0,s0,908 # 116c0 <__init_array_start>
1033c: 00001917 auipc s2,0x1
10340: 38c90913 addi s2,s2,908 # 116c8 <__init_array_end>
10344: 40890933 sub s2,s2,s0
10348: 40295913 srai s2,s2,0x2
1034c: dbdff0ef jal ra,10108 <_fini>
10350: 00090e63 beqz s2,1036c <__libc_init_array+0x80>
10354: 00000493 li s1,0
10358: 00042783 lw a5,0(s0)
1035c: 00148493 addi s1,s1,1
10360: 00440413 addi s0,s0,4
10364: 000780e7 jalr a5
10368: fe9918e3 bne s2,s1,10358 <__libc_init_array+0x6c>
1036c: 00c12083 lw ra,12(sp)
10370: 00812403 lw s0,8(sp)
10374: 00412483 lw s1,4(sp)
10378: 00012903 lw s2,0(sp)
1037c: 01010113 addi sp,sp,16
10380: 00008067 ret
在10350
行我们可以找到一个比较,如果为真,将跳过几行。但由于某种原因,这不是真的,所以我们必须继续。在这里它变得有趣。下一条指令将 0
加载到寄存器 s1
中,很好,但下一条指令试图从地址 0(s0)
中将一些值加载到寄存器 a5
中。但是读取的输出会是0
,因为内存里什么都没有。我找不到任何对此代码开头的特定地址的引用(来自 _start
)。事实上,除了这个函数之外,根本没有尝试写入或读取该地址。
10350: 00090e63 beqz s2,1036c <__libc_init_array+0x80>
10354: 00000493 li s1,0
10358: 00042783 lw a5,0(s0)
1035c: 00148493 addi s1,s1,1
10360: 00440413 addi s0,s0,4
10364: 000780e7 jalr a5
我错过了什么吗?这是程序跳转到主要部分之前的最后一步,所以我不想回到地址 0
。
感谢您的帮助
编辑
有 .init_array
和 .data
的转储,但我看不出它们如何影响有问题的地址上的值(甚至还有压缩指令和一些未知的 FLD,两者我的模拟器不支持其中的一些)。
Disassembly of section .init_array:
000116c0 <__init_array_start>:
116c0: 00ac addi a1,sp,72
116c2: 0001 nop
000116c4 <__frame_dummy_init_array_entry>:
116c4: 01a8 addi a0,sp,200
116c6: 0001 nop
Disassembly of section .fini_array:
000116c8 <__do_global_dtors_aux_fini_array_entry>:
116c8: 0160 addi s0,sp,140
116ca: 0001 nop
Disassembly of section .data:
000116d0 <__DATA_BEGIN__>:
116d0: 0000 unimp
116d2: 0000 unimp
116d4: 19bc addi a5,sp,248
116d6: 0001 nop
116d8: 1a24 addi s1,sp,312
116da: 0001 nop
116dc: 1a8c addi a1,sp,368
116de: 0001 nop
...
11778: 0001 nop
1177a: 0000 unimp
1177c: 0000 unimp
1177e: 0000 unimp
11780: 330e fld ft6,224(sp)
11782: abcd j 11d74 <__BSS_END__+0x230>
11784: 1234 addi a3,sp,296
11786: e66d bnez a2,11870 <__DATA_BEGIN__+0x1a0>
11788: deec sw a1,124(a3)
1178a: 0005 c.nop 1
1178c: 0000000b 0xb
...
您指出的代码扫描 .init_array
部分的内容,从 __init_array_start
到 __init_array_end
。此部分包含全局构造函数的地址,在执行 main
之前需要 运行 objdump -s
而不是 objdump -d
)。可以看到这里初始化了地址:
10338: 38c40413 addi s0,s0,908 # 116c0 <__init_array_start>
...
10340: 38c90913 addi s2,s2,908 # 116c8 <__init_array_end>
然后用于计算节的大小(以 4 字节为单位):
10344: 40890933 sub s2,s2,s0
10348: 40295913 srai s2,s2,0x2
然后启动代码继续遍历 .init_array
中的指针并执行它们:
10350: 00090e63 beqz s2,1036c <__libc_init_array+0x80> ; Skip if .init_array is empty
10354: 00000493 li s1,0 ; Initialize loop counter
10358: 00042783 lw a5,0(s0) ; Load address of global ctor
1035c: 00148493 addi s1,s1,1 ; Increment loop counter
10360: 00440413 addi s0,s0,4 ; Compute address which holds address of next global pointer
10364: 000780e7 jalr a5 ; Execute global ctor
10368: fe9918e3 bne s2,s1,10358 <__libc_init_array+0x6c> ; Loop if within .init_array
根据您问题中的数据,您的 .init_array
部分中没有 NULL 条目,因此我唯一的猜测是由于某种原因数据段 (.data
、.rodata
、.init_array
, 等)在模拟开始时没有从 ELF 文件正确加载(您还提到您只向模拟器提供 .text
部分)。
我的目标是 运行 在我的 RISCV
模拟器中编译 C
代码。我用 RISCV32I(32 位)编译器编译了一个 C 代码,它以 ELF 格式输出我的代码(然后我选择了 .text
部分并在我的 RISC-V
模拟器中使用它)。完美的。然后,我尝试 运行 在我的模拟器中编译代码,但我发现在 <__libc_init_array>
函数中有 "some" 代码总是跳转到我程序的地址 0,这显然我不想。
反汇编我的输出文件:
000102ec <__libc_init_array>:
102ec: ff010113 addi sp,sp,-16
102f0: 00812423 sw s0,8(sp)
102f4: 01212023 sw s2,0(sp)
102f8: 00001417 auipc s0,0x1
102fc: 3c840413 addi s0,s0,968 # 116c0 <__init_array_start>
10300: 00001917 auipc s2,0x1
10304: 3c090913 addi s2,s2,960 # 116c0 <__init_array_start>
10308: 40890933 sub s2,s2,s0
1030c: 00112623 sw ra,12(sp)
10310: 00912223 sw s1,4(sp)
10314: 40295913 srai s2,s2,0x2
10318: 00090e63 beqz s2,10334 <__libc_init_array+0x48>
1031c: 00000493 li s1,0
10320: 00042783 lw a5,0(s0)
10324: 00148493 addi s1,s1,1
10328: 00440413 addi s0,s0,4
1032c: 000780e7 jalr a5
10330: fe9918e3 bne s2,s1,10320 <__libc_init_array+0x34>
10334: 00001417 auipc s0,0x1
10338: 38c40413 addi s0,s0,908 # 116c0 <__init_array_start>
1033c: 00001917 auipc s2,0x1
10340: 38c90913 addi s2,s2,908 # 116c8 <__init_array_end>
10344: 40890933 sub s2,s2,s0
10348: 40295913 srai s2,s2,0x2
1034c: dbdff0ef jal ra,10108 <_fini>
10350: 00090e63 beqz s2,1036c <__libc_init_array+0x80>
10354: 00000493 li s1,0
10358: 00042783 lw a5,0(s0)
1035c: 00148493 addi s1,s1,1
10360: 00440413 addi s0,s0,4
10364: 000780e7 jalr a5
10368: fe9918e3 bne s2,s1,10358 <__libc_init_array+0x6c>
1036c: 00c12083 lw ra,12(sp)
10370: 00812403 lw s0,8(sp)
10374: 00412483 lw s1,4(sp)
10378: 00012903 lw s2,0(sp)
1037c: 01010113 addi sp,sp,16
10380: 00008067 ret
在10350
行我们可以找到一个比较,如果为真,将跳过几行。但由于某种原因,这不是真的,所以我们必须继续。在这里它变得有趣。下一条指令将 0
加载到寄存器 s1
中,很好,但下一条指令试图从地址 0(s0)
中将一些值加载到寄存器 a5
中。但是读取的输出会是0
,因为内存里什么都没有。我找不到任何对此代码开头的特定地址的引用(来自 _start
)。事实上,除了这个函数之外,根本没有尝试写入或读取该地址。
10350: 00090e63 beqz s2,1036c <__libc_init_array+0x80>
10354: 00000493 li s1,0
10358: 00042783 lw a5,0(s0)
1035c: 00148493 addi s1,s1,1
10360: 00440413 addi s0,s0,4
10364: 000780e7 jalr a5
我错过了什么吗?这是程序跳转到主要部分之前的最后一步,所以我不想回到地址 0
。
感谢您的帮助
编辑
有 .init_array
和 .data
的转储,但我看不出它们如何影响有问题的地址上的值(甚至还有压缩指令和一些未知的 FLD,两者我的模拟器不支持其中的一些)。
Disassembly of section .init_array:
000116c0 <__init_array_start>:
116c0: 00ac addi a1,sp,72
116c2: 0001 nop
000116c4 <__frame_dummy_init_array_entry>:
116c4: 01a8 addi a0,sp,200
116c6: 0001 nop
Disassembly of section .fini_array:
000116c8 <__do_global_dtors_aux_fini_array_entry>:
116c8: 0160 addi s0,sp,140
116ca: 0001 nop
Disassembly of section .data:
000116d0 <__DATA_BEGIN__>:
116d0: 0000 unimp
116d2: 0000 unimp
116d4: 19bc addi a5,sp,248
116d6: 0001 nop
116d8: 1a24 addi s1,sp,312
116da: 0001 nop
116dc: 1a8c addi a1,sp,368
116de: 0001 nop
...
11778: 0001 nop
1177a: 0000 unimp
1177c: 0000 unimp
1177e: 0000 unimp
11780: 330e fld ft6,224(sp)
11782: abcd j 11d74 <__BSS_END__+0x230>
11784: 1234 addi a3,sp,296
11786: e66d bnez a2,11870 <__DATA_BEGIN__+0x1a0>
11788: deec sw a1,124(a3)
1178a: 0005 c.nop 1
1178c: 0000000b 0xb
...
您指出的代码扫描 .init_array
部分的内容,从 __init_array_start
到 __init_array_end
。此部分包含全局构造函数的地址,在执行 main
之前需要 运行 objdump -s
而不是 objdump -d
)。可以看到这里初始化了地址:
10338: 38c40413 addi s0,s0,908 # 116c0 <__init_array_start>
...
10340: 38c90913 addi s2,s2,908 # 116c8 <__init_array_end>
然后用于计算节的大小(以 4 字节为单位):
10344: 40890933 sub s2,s2,s0
10348: 40295913 srai s2,s2,0x2
然后启动代码继续遍历 .init_array
中的指针并执行它们:
10350: 00090e63 beqz s2,1036c <__libc_init_array+0x80> ; Skip if .init_array is empty
10354: 00000493 li s1,0 ; Initialize loop counter
10358: 00042783 lw a5,0(s0) ; Load address of global ctor
1035c: 00148493 addi s1,s1,1 ; Increment loop counter
10360: 00440413 addi s0,s0,4 ; Compute address which holds address of next global pointer
10364: 000780e7 jalr a5 ; Execute global ctor
10368: fe9918e3 bne s2,s1,10358 <__libc_init_array+0x6c> ; Loop if within .init_array
根据您问题中的数据,您的 .init_array
部分中没有 NULL 条目,因此我唯一的猜测是由于某种原因数据段 (.data
、.rodata
、.init_array
, 等)在模拟开始时没有从 ELF 文件正确加载(您还提到您只向模拟器提供 .text
部分)。