c++ - 是否跳过/删除 ELF 文件中的 `PHDR` 程序头以执行可执行文件?如果是这样,为什么?

标签 c++ c elf readelf

我正在对这个简单的 C++ 程序的二进制文件进行一些修改,以了解 ELF 的程序头:

int main(){ }

编译:
❯ make
g++ -O0 -fverbose-asm -no-pie -o main main.cpp

我使用 readelf -l main 得到以下结果:
Elf file type is EXEC (Executable file)
Entry point 0x401020
There are 11 program headers, starting at offset 64

Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  PHDR           0x0000000000000040 0x0000000000400040 0x0000000000400040
                 0x0000000000000268 0x0000000000000268  R      0x8
  INTERP         0x00000000000002a8 0x00000000004002a8 0x00000000004002a8
                 0x000000000000001c 0x000000000000001c  R      0x1
      [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
  LOAD           0x0000000000000000 0x0000000000400000 0x0000000000400000
                 0x00000000000004c0 0x00000000000004c0  R      0x1000
...

我在这个文档中看到:http://man7.org/linux/man-pages/man5/elf.5.html for PHDR:

The array element, if present, specifies the loca‐ tion and size of the program header table itself, both in the file and in the memory image of the pro‐ gram. This segment type may not occur more than once in a file. Moreover, it may occur only if the program header table is part of the memory image of the program. If it is present, it must precede any loadable segment entry.



引用中 if present 的存在让我想知道如果我跳过 PHDR 标题会发生什么。 我使用 vim 的十六进制编辑器使用 main 更改 :%!xxd 的二进制布局(确保在保存之前运行 :%!xxd -r,否则它不再是二进制文件)从以下位置获取:
00000000: 7f45 4c46 0201 0100 0000 0000 0000 0000  .ELF............
00000010: 0200 3e00 0100 0000 2010 4000 0000 0000  ..>..... .@.....
00000020: 4000 0000 0000 0000 1839 0000 0000 0000  @........9......

至:
00000000: 7f45 4c46 0201 0100 0000 0000 0000 0000  .ELF............
00000010: 0200 3e00 0100 0000 2010 4000 0000 0000  ..>..... .@.....
00000020: 7800 0000 0000 0000 1839 0000 0000 0000  @........9......

(只改变第20个字节),跳过PHDR头的长度。我再次运行 readelf 以验证它仍然是一个有效的 ELF 文件:
❯ readelf -l main

Elf file type is EXEC (Executable file)
Entry point 0x401020
There are 11 program headers, starting at offset 120

Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  INTERP         0x00000000000002a8 0x00000000004002a8 0x00000000004002a8
                 0x000000000000001c 0x000000000000001c  R      0x1
      [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
  ...

令人惊讶的是,该程序仍然可以完美地执行。为什么我们甚至需要 PHDR header ?它对链接和/或其他情况有用吗?似乎在运行时根本没有使用它,那么为什么我们要放置它呢?

最佳答案

如果主程序是 ET_EXEC (非 PIE)类型,它可能可以在没有 PT_PHDR 的情况下运行。 PT_PHDR 的主要用途是能够将头中的(未重定位的)地址与程序头的实际运行时地址(由动态链接器通过 aux vector 中的 AT_PHDR 获得)进行比较,以确定 PIE 可执行文件所在的偏移量加载。

我不确定 glibc 的动态链接器对 PT_PHDR 的要求是什么,但在 musl libc 中,我们只需要它来计算这个负载偏移,否则根本不使用它。

关于c++ - 是否跳过/删除 ELF 文件中的 `PHDR` 程序头以执行可执行文件?如果是这样,为什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61568612/

相关文章:

c++ - 将静态库链接到其他静态库

c++ - std::vector 和多维数组的连续内存

c - 如何在 C (Windows) 中读取 IP 地址?

c - 错误: expected expression before '||' token

c++ - 将非均匀数组传递给着色器

c++ - 将指针包装到迭代器中以复制到 STL 容器中

python - 松散类型的语言如何为数组提供恒定的查找时间?

c - 链接器如何创建可执行文件并链接 C 关键字

gcc - valgrind 堆栈跟踪中缺少符号

linux - Linux内核加载的基地址