计算一个ELF文件的入口点作为一个物理地址(从0开始的偏移量)

Calculate the entry point of an ELF file as a physical address (offset from 0)

我正在构建一个 RISC-V 模拟器,它基本上将整个 ELF 文件加载到内存中。

到目前为止,我使用 risc-v 基金会提供的 pre-compiled 测试二进制文件,它方便地在 .text 部分的开头有一个入口点。

例如:

> riscv32-unknown-elf-objdump ../riscv32i-emulator/tests/simple -d

../riscv32i-emulator/tests/simple:     file format elf32-littleriscv


Disassembly of section .text.init:

80000000 <_start>:
80000000:       0480006f                j       80000048 <reset_vector>
...

进入这个项目时,我对 ELF 文件了解不多,所以我只是假设每个 ELF 的入口点与 .text 部分的开始点完全相同。

当我编译自己的二进制文件时出现问题,我发现实际的入口点并不总是与 .text 部分的开始相同,但它可能在其中的任何地方,就像这里:

> riscv32-unknown-elf-objdump a.out -d

a.out:     file format elf32-littleriscv


Disassembly of section .text:

00010074 <register_fini>:
   10074:       00000793                li      a5,0
   10078:       00078863                beqz    a5,10088 <register_fini+0x14>
   1007c:       00010537                lui     a0,0x10
   10080:       43850513                addi    a0,a0,1080 # 10438 <__libc_fini_array>
   10084:       3a00006f                j       10424 <atexit>
   10088:       00008067                ret

0001008c <_start>:
   1008c:       00002197                auipc   gp,0x2
   10090:       cec18193                addi    gp,gp,-788 # 11d78 <__global_pointer$>
...

所以,在阅读了更多有关 ELF 文件的信息后,我发现实际的入口点地址是由 ELF 的 header:

上的 Entry 条目提供的
> riscv32-unknown-elf-readelf a.out -h | grep Entry
  Entry point address:               0x1008c

现在的问题是这个地址不是文件上的实际地址(从 0 偏移)而是一个虚拟地址,所以很明显,如果我将模拟器的程序计数器设置为这个地址,模拟器就会崩溃.

阅读更多,我听到人们谈论有关程序 header 的偏移量的计算等等,但没有人有具体的答案。

我的问题是:关于如何准确获取 _start 过程的入口点地址作为距字节 0 的偏移量的实际“公式”是什么?

需要说明的是,我的模拟器不支持虚拟内存,二进制文件是唯一加载到我的模拟器内存​​中的东西,所以我对虚拟内存的抽象没有任何用处。我只希望每个内存地址都作为磁盘上的物理地址。

My question is: what is the actual "formula" of how exactly you get the entry point address of the _start procedure as an offset from byte 0?

首先,忘记 部分 。仅 matter at runtime.

其次,使用readelf -Wl查看细分。他们准确地告诉您哪个文件块 ([.p_offset, .p_offset + .p_filesz)) 进入哪个 in-memory 区域 ([.p_vaddr, .p_vaddr + .p_memsz))。

_start 位于文件中的哪个偏移量”的精确计算是:

  1. 找到“覆盖”Elf32_Ehdr.e_entry中包含的地址的Elf32_Phdr
  2. 使用 phdr_start 的文件偏移量是:ehdr->e_entry - phdr->p_vaddr + phdr->p_offset.

更新:

So, am I always looking for the 1st program header?

没有

Also by "covers" you mean that the 1st phdr->p_vaddr is always equal to e_entry?

没有

您正在寻找与内存中的 ehdr->e_entry 重叠的程序头(描述 in-memory 和 on-file 数据之间的关系)。也就是说,您正在寻找 phdr->p_vaddr <= ehdr->e_entry && ehdr->e_entry < phdr->p_vaddr + phdr->p_memsz 的段。此部分 通常 第一个,但这绝不是保证。另见 .