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

kir*_*eos 2 linux emulation elf virtual-memory riscv

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

到目前为止,我使用了 risc-v 基金会提供的预编译测试二进制文件,该二进制文件在本节的开头处方便地有一个入口点.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>
...
Run Code Online (Sandbox Code Playgroud)

进入这个项目时,我对 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$>
...
Run Code Online (Sandbox Code Playgroud)

因此,在阅读了有关 ELF 文件的更多信息后,我发现实际的入口点地址是由EntryELF 标头上的条目提供的:

> riscv32-unknown-elf-readelf a.out -h | grep Entry
  Entry point address:               0x1008c
Run Code Online (Sandbox Code Playgroud)

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

多读一点,我听到人们谈论有关程序头偏移量等的计算,但没有人给出具体的答案。

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

需要明确的是,我的模拟器不支持虚拟内存,并且二进制文件是加载到模拟器内存​​中的唯一内容,因此我没有使用虚拟内存的抽象。我只想将每个内存地址作为磁盘上的物理地址。

Emp*_*ian 5

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

首先,忘记sections。只有 在运行时才重要

其次,用readelf -Wl来看细分。它们准确地告诉您文件的哪个块 ( [.p_offset, .p_offset + .p_filesz)) 进入哪个内存区域 ( [.p_vaddr, .p_vaddr + .p_memsz))。

“文件中的偏移量所在的_start位置”的精确计算是:

  1. 查找Elf32_Phdr哪个“覆盖”了 中包含的地址Elf32_Ehdr.e_entry
  2. 使用它phdr,文件偏移量_start为:ehdr->e_entry - phdr->p_vaddr + phdr->p_offset

更新:

那么,我总是在寻找第一个程序头吗?

不。

另外,“覆盖”是指第一个 phdr->p_vaddr 始终等于 e_entry?

不。

ehdr->e_entry您正在寻找与内存重叠的程序头(描述内存中数据和文件数据之间的关系) 。也就是说,您正在寻找 的段phdr->p_vaddr <= ehdr->e_entry && ehdr->e_entry < phdr->p_vaddr + phdr->p_memsz。该部分通常是第一个,但这并不能保证。另请参阅此答案