2014-02-20 64 views
3

我想了解ELF格式,现在有一些东西我没有得到关于在程序头中定义的段。我有这个小代码,我转换为一个ELF文件与G ++(在Linux x86_x64):ELF程序头段的大小和偏移

#include <stdlib.h> 
#include <iostream> 

using namespace std; 

int main(int argc, char *argv[]) 
{ 
    if (argc == 1) 
    { 
     cout << "Hello world!" << endl; 
    } 
    return 0; 
} 

随着g++ -c -m64 -D ACIS64 main.cpp -o main.og++ -s -O1 -o Main main.o。 现在,readelf我得到段的这个名单:

Program Headers: 
Type   Offset    VirtAddr   PhysAddr 
       FileSiz   MemSiz    Flags  Align 
PHDR   0x0000000000000040 0x0000000000400040 0x0000000000400040 
       0x00000000000001f8 0x00000000000001f8 R E  8 
INTERP   0x0000000000000238 0x0000000000400238 0x0000000000400238 
       0x000000000000001c 0x000000000000001c R   1 
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2] 
LOAD   0x0000000000000000 0x0000000000400000 0x0000000000400000 
       0x0000000000000afc 0x0000000000000afc R E  200000 
LOAD   0x0000000000000df8 0x0000000000600df8 0x0000000000600df8 
       0x0000000000000270 0x00000000000003a0 RW   200000 
DYNAMIC  0x0000000000000e18 0x0000000000600e18 0x0000000000600e18 
       0x00000000000001e0 0x00000000000001e0 RW   8 
NOTE   0x0000000000000254 0x0000000000400254 0x0000000000400254 
       0x0000000000000044 0x0000000000000044 R   4 
GNU_EH_FRAME 0x00000000000009a4 0x00000000004009a4 0x00000000004009a4 
       0x0000000000000044 0x0000000000000044 R   4 
GNU_STACK  0x0000000000000000 0x0000000000000000 0x0000000000000000 
       0x0000000000000000 0x0000000000000000 RW   10 
GNU_RELRO  0x0000000000000df8 0x0000000000600df8 0x0000000000600df8 
       0x0000000000000208 0x0000000000000208 R   1 

随着祝福十六进制编辑器我在看代码,并试图找到这些片段中的每一个。

  • 我在ELF标题后面找到了PHDR段,它的大小是整个程序头的大小。它有一个8字节的队列,可读/可执行。 [!]我不明白为什么可执行PHDR

  • 我在PHDR后面找到声明解释器的段。它具有解释器路径的大小和1个字节的对齐。正确
    INTERP

  • 现在我有一个段是可读和可执行文件,[!] 我想是的代码段。我不明白为什么它从0x0000000000000000开始。不应该从入口点位置开始吗?为什么它有一个0xafc字节的大小?大小不只是代码的大小?有多少文件是可执行的?另外,我不明白为什么对齐是0x200000字节。是否为内存中的LOAD段保留了多少空间?。这是本段结束,并且764个0x0字节的大写金额如下它:
    LOAD1

  • 下一个(可读写)我想是其中变量存储的段[!]。它结束的地方就像节标题可能开始
    LOAD2
  • 现在下一个是DYNAMIC标题。它从0xe18开始,位于上面的内部。 [!] 我认为这是一个段,其中存储对外部函数和变量的引用,但我不确定。它是可读写的。我只是不知道什么段是这样,为什么它是“内部” LOAD段以上 DYNAM
  • 注段,包含一些信息,我想并不重要,现在
  • GNU特定部分,一个他们有任何偏移和大小等于0x0000000000000000,其他人干扰其他部分,我不明白,或者。

我来自PE的世界里,每一件事情都定义了它自己以及偏移和大小,在这里我看到这些奇怪的地址和大小,我感到困惑。

回答

5

readelf输出显示程序头表。它包含ELF文件中的段(可能是可加载的或不可加载的)列表。如此处所示,一个细分受众群通常包含其他细分。

我在ELF头后面找到PHDR段,它的大小为 。它具有8个字节的对齐,并且是可读/可执行的 。 [!]我不明白为什么可执行文件。

如果仔细阅读readelf输出,您会注意到,PHDR实际上是代码段的一部分(请注意VirtAddr和MemSiz字段)。这解释了为什么它与代码段(RX)共享相同的权限。

现在我有一段可读且可执行的段,其中[!] I 假设是代码段。我不明白为什么它开始于 0x0000000000000000。不应该在入口点为 的位置开始吗?为什么它有一个0xafc字节的大小?代码的大小是不是只有大小 ?有多少文件是可执行的?另外,我 不明白为什么对齐是0x200000字节。这是多少 空间为内存中的LOAD段保留?这是 段结束的地方,其后面有764个0x0字节:

是的,这是代码段。它从文件的开始处(即偏移量0)开始,并扩展到文件中的0xafc字节。标题指定在加载ELF时,文件的这部分映射到内存中的0x0000000000400000。该段不仅由C++文件中的main()组成,还由编译器添加了一些其他可执行文件。对齐仅指定下一个段的开始位置,而不是段的大小。可加载段应具有VirtAddr和PhysAddr字段模数页面大小(或对齐字段,如果对齐!= 0 & &对齐!= 1)的一致值。这就解释了为什么VirtAddr数据段0x0000000000600df8(0x0000000000600df8 - 0x0000000000000df8%0x200000 == 0)。文本区段和数据区段之间(即0xafc和0xdf8之间)的文件区域用零填充。

下一个(可读写)[!]我想是其中 变量存储的段。它结束了像 标题部分可能开始的地方。

正确,这是存储全局和静态变量(以及其他东西)的数据段。它在节标题之前结束。

现在下一个是DYNAMIC标题。它从0xe18开始,在上面的内部是 。 [!]我认为这是其中引用 外部函数和变量的存储段,但我不知道。它 是可读写的。我只是不知道什么段是这一点, 为什么它是“内部” LOAD段以上

就像PHDR段是代码段的一部分,动态段是数据段的一部分。这就是为什么相同的权限(RW)。它包含.dynamic部分,其中包含一些结构数组,如符号和字符串表的地址。

GNU特定的细分,其中一人有任何偏移和大小等于 到0x0000000000000000,其他的与其他细分,我 不要干涉,无论是。

GNU_EH_FRAME是代码段的一部分,GNU_RELRO是数据段的一部分(请参阅VirtAddr和MemSiz字段)。 GNU_STACK只是一个程序头文件,告诉系统在ELF加载到内存时如何控制堆栈。 (FileSiz和MemSiz是0)。

参考文献:

  1. ELF File format specification
  2. 链接器和加载,由John R. Levine的
+0

更清晰了。这真的有帮助。非常感谢。 – ali