计算一个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
位于文件中的哪个偏移量”的精确计算是:
- 找到“覆盖”
Elf32_Ehdr.e_entry
中包含的地址的Elf32_Phdr
。
- 使用
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
的段。此部分 通常 第一个,但这绝不是保证。另见 .
我正在构建一个 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
位于文件中的哪个偏移量”的精确计算是:
- 找到“覆盖”
Elf32_Ehdr.e_entry
中包含的地址的Elf32_Phdr
。 - 使用
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
的段。此部分 通常 第一个,但这绝不是保证。另见