我正在试图findELF文件的基地址。 我知道你可以使用readelf来find程序入口点和不同的部分细节(基地址,大小,标志等)。
例如,x86架构的程序基于链接器的0x8048000。 使用readelf我可以看到程序入口点,但输出中没有特定的字段告诉基地址。
$ readelf -e test ELF Header: Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 Class: ELF32 Data: 2's complement, little endian Version: 1 (current) OS/ABI: UNIX - System V ABI Version: 0 Type: EXEC (Executable file) Machine: Intel 80386 Version: 0x1 Entry point address: 0x8048390 Start of program headers: 52 (bytes into file) Start of section headers: 4436 (bytes into file) Flags: 0x0 Size of this header: 52 (bytes) Size of program headers: 32 (bytes) Number of program headers: 9 Size of section headers: 40 (bytes) Number of section headers: 30 Section Headers: [Nr] Name Type Addr Off Size ES Flg Lk Inf Al [ 0] NULL 00000000 000000 000000 00 0 0 0 [ 1] .interp PROGBITS 08048154 000154 000013 00 A 0 0 1 [ 2] .note.ABI-tag NOTE 08048168 000168 000020 00 A 0 0 4 [ 3] .note.gnu.build-i NOTE 08048188 000188 000024 00 A 0 0 4 [ 4] .gnu.hash GNU_HASH 080481ac 0001ac 000024 04 A 5 0 4 [ 5] .dynsym DYNSYM 080481d0 0001d0 000070 10 A 6 1 4
在细节部分,我可以看到偏移量是根据ELF的基地址计算的。
所以,.dynsym部分从地址0x080481d0开始,偏移量是0x1d0。 这意味着基地址是0x08048000。 它是否正确?
类似地,对于像PPC,ARM,MIPS这样的不同架构编译的程序,我看不到他们的基地址,但只能看到OEP,Section Headers。
你需要检查段表又名程序头( readelf -l
)。
Elf file type is EXEC (Executable file) Entry point 0x804a7a0 There are 9 program headers, starting at offset 52 Program Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align PHDR 0x000034 0x08048034 0x08048034 0x00120 0x00120 RE 0x4 INTERP 0x000154 0x08048154 0x08048154 0x00013 0x00013 R 0x1 [Requesting program interpreter: /lib/ld-linux.so.2] LOAD 0x000000 0x08048000 0x08048000 0x10fc8 0x10fc8 RE 0x1000 LOAD 0x011000 0x08059000 0x08059000 0x0038c 0x01700 RW 0x1000 DYNAMIC 0x01102c 0x0805902c 0x0805902c 0x000f8 0x000f8 RW 0x4 NOTE 0x000168 0x08048168 0x08048168 0x00020 0x00020 R 0x4 TLS 0x011000 0x08059000 0x08059000 0x00000 0x0005c R 0x4 GNU_EH_FRAME 0x00d3c0 0x080553c0 0x080553c0 0x00c5c 0x00c5c R 0x4 GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RW 0x4
第一个(最低) LOAD
段的虚拟地址是文件的默认加载基地。 你可以看到这个文件是0x08048000。
它在链接器脚本中定义。 您可以使用ld --verbose
转储默认链接器脚本。 示例输出:
GNU ld (GNU Binutils) 2.23.1 Supported emulations: elf_x86_64 elf32_x86_64 elf_i386 i386linux elf_l1om elf_k1om using internal linker script: ================================================== /* Script for -z combreloc: combine and sort reloc sections */ OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64", "elf64-x86-64") OUTPUT_ARCH(i386:x86-64) ENTRY(_start) SEARCH_DIR("/nix/store/kxf1p7l7lgm6j5mjzkiwcwzc98s9f1az-binutils-2.23.1/x86_64-unknown-linux-gnu/lib64"); SEARCH_DIR("/nix/store/kxf1p7l7lgm6j5mjzkiwcwzc98s9f1az-binutils-2.23.1/lib64"); SEARCH_DIR("/nix/store/kxf1p7l7lgm6j5mjzkiwcwzc98s9f1az-binutils-2.23.1/x86_64-unknown-linux-gnu/lib"); SEARCH_DIR("/nix/store/kxf1p7l7lgm6j5mjzkiwcwzc98s9f1az-binutils-2.23.1/lib"); SECTIONS { /* Read-only sections, merged into text segment: */ PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x400000)); . = SEGMENT_START("text-segment", 0x400000) + SIZEOF_HEADERS; .interp : { *(.interp) } .note.gnu.build-id : { *(.note.gnu.build-id) } .hash : { *(.hash) } .gnu.hash : { *(.gnu.hash) } .dynsym : { *(.dynsym) } .dynstr : { *(.dynstr) } .gnu.version : { *(.gnu.version) } .gnu.version_d : { *(.gnu.version_d) } .gnu.version_r : { *(.gnu.version_r) }
(剪断)
如果你错过了: __executable_start = SEGMENT_START("text-segment", 0x400000))
。
对我来说,当然,当我将一个简单的.o文件链接到一个二进制文件时,入口点地址非常接近0x400000。
ELF元数据中的入口点地址是该值,再加上从.text
部分开头到_start
符号的偏移量。 还要注意,可以配置_start
符号。 再次从我的默认链接器脚本示例: ENTRY(_start)
。