了解 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 部分)。