ELF - 验证可执行内存区域

ELF - Verifying executable memory regions

我正在学习 elf 二进制文件。我想手动验证程序中编写的代码是否在内存的可执行区域中(对于将要链接的共享库代码也是如此)。

我有一个简单的程序:

int main() { return 0; }

当我这样做时:

readelf -a myprog

我得到以下信息:

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 000020 04   A  5   0  4
  [ 5] .dynsym           DYNSYM          080481cc 0001cc 000040 10   A  6   1  4
  [ 6] .dynstr           STRTAB          0804820c 00020c 000045 00   A  0   0  1
  [ 7] .gnu.version      VERSYM          08048252 000252 000008 02   A  5   0  2
  [ 8] .gnu.version_r    VERNEED         0804825c 00025c 000020 00   A  6   1  4
  [ 9] .rel.dyn          REL             0804827c 00027c 000008 08   A  5   0  4
  [10] .rel.plt          REL             08048284 000284 000010 08   A  5  12  4
  [11] .init             PROGBITS        08048294 000294 00002e 00  AX  0   0  4
  [12] .plt              PROGBITS        080482d0 0002d0 000030 04  AX  0   0 16
  [13] .text             PROGBITS        08048300 000300 00016c 00  AX  0   0 16
  [14] .fini             PROGBITS        0804846c 00046c 00001a 00  AX  0   0  4
  [15] .rodata           PROGBITS        08048488 000488 000008 00   A  0   0  4
  [16] .eh_frame_hdr     PROGBITS        08048490 000490 000034 00   A  0   0  4
  [17] .eh_frame         PROGBITS        080484c4 0004c4 0000c4 00   A  0   0  4
  [18] .ctors            PROGBITS        08049f14 000f14 000008 00  WA  0   0  4
  [19] .dtors            PROGBITS        08049f1c 000f1c 000008 00  WA  0   0  4
  [20] .jcr              PROGBITS        08049f24 000f24 000004 00  WA  0   0  4
  [21] .dynamic          DYNAMIC         08049f28 000f28 0000c8 08  WA  6   0  4
  [22] .got              PROGBITS        08049ff0 000ff0 000004 04  WA  0   0  4
  [23] .got.plt          PROGBITS        08049ff4 000ff4 000014 04  WA  0   0  4
  [24] .data             PROGBITS        0804a008 001008 000008 00  WA  0   0  4
  [25] .bss              NOBITS          0804a010 001010 000008 00  WA  0   0  4
  [26] .comment          PROGBITS        00000000 001010 00002a 01  MS  0   0  1
  [27] .shstrtab         STRTAB          00000000 00103a 0000fc 00      0   0  1
  [28] .symtab           SYMTAB          00000000 0015e8 000400 10     29  45  4
  [29] .strtab           STRTAB          00000000 0019e8 0001ea 00      0   0  1

要检查程序的代码是否可执行,我可以看到带有标记 AX.text 部分。此处的 X 是否用于使我的 main() 函数中的代码可执行?

运行时动态链接程序时,共享库(如glibc)会加载到哪个段?我在网上找到了讨论在动态链接上下文中使用 GOT、PLT 的解释。唯一带有标记 X 的部分是 initpltfini(除了 text)。共享库是否链接到这些部分之一以确保它们的代码在程序开始执行时是可执行的?

(回答上述问题时能指出一些参考就更好了)

Is the X here for making the code inside my main() function is executable ?

正确。尽管涉及的不仅仅是 main() 函数;还有一些链接到您的可执行文件的其他代码也包含在本节中。

Which section will the shared library (e.g. glibc) be loaded into when the program is dynamically linked at run time ?

None 个。共享库是一个单独的 ELF 对象,有自己的部分,其中一些是可执行的;它实际上是 您的可执行文件一起加载,而不是 加载到 中。也就是说,内存中的结果图像将包含可执行文件的 .text 部分和它加载的所有共享库中的许多其他 .text(和其他类型的)部分。

例如,这是我的 Linux 系统上 运行 /bin/cat 进程中 /proc/self/maps 的内容。此输出的格式与 readelf 向您显示的格式不同,但一些相似之处应该显而易见:

00400000-0040b000 r-xp 00000000 08:00 32968                              /bin/cat
0060a000-0060b000 r--p 0000a000 08:00 32968                              /bin/cat
0060b000-0060c000 rw-p 0000b000 08:00 32968                              /bin/cat
0242a000-0244b000 rw-p 00000000 00:00 0                                  [heap]
7fdf299a2000-7fdf29c6b000 r--p 00000000 08:00 949                        /usr/lib/locale/locale-archive
7fdf29c6b000-7fdf29e26000 r-xp 00000000 08:00 18508                      /lib/x86_64-linux-gnu/libc-2.19.so
7fdf29e26000-7fdf2a025000 ---p 001bb000 08:00 18508                      /lib/x86_64-linux-gnu/libc-2.19.so
7fdf2a025000-7fdf2a029000 r--p 001ba000 08:00 18508                      /lib/x86_64-linux-gnu/libc-2.19.so
7fdf2a029000-7fdf2a02b000 rw-p 001be000 08:00 18508                      /lib/x86_64-linux-gnu/libc-2.19.so
7fdf2a02b000-7fdf2a030000 rw-p 00000000 00:00 0
7fdf2a030000-7fdf2a053000 r-xp 00000000 08:00 18255                      /lib/x86_64-linux-gnu/ld-2.19.so
7fdf2a246000-7fdf2a249000 rw-p 00000000 00:00 0
7fdf2a250000-7fdf2a252000 rw-p 00000000 00:00 0
7fdf2a252000-7fdf2a253000 r--p 00022000 08:00 18255                      /lib/x86_64-linux-gnu/ld-2.19.so
7fdf2a253000-7fdf2a254000 rw-p 00023000 08:00 18255                      /lib/x86_64-linux-gnu/ld-2.19.so
7fdf2a254000-7fdf2a255000 rw-p 00000000 00:00 0
7ffd516aa000-7ffd516cb000 rw-p 00000000 00:00 0                          [stack]
7ffd517f9000-7ffd517fb000 r--p 00000000 00:00 0                          [vvar]
7ffd517fb000-7ffd517fd000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]

特别是,您可以看到从顶部的 /bin/cat 加载了三个部分:第一个是可执行的 (r-xp),第二个是只读的 (r--p),第三个是读写(rw-p)。此外,还有许多可执行和不可执行的段从下面的 libc-2.19.so 以及 ld-2.19.so(动态链接器)映射而来。

(这个转储中还出现了一些有点神秘的段,包括[vdso][vsyscall]。这些是内核映射到进程中的,很难解释;我不会'此处不赘述。)