为什么使用 gdb 时,某些库和其他部分会在 Linux 虚拟内存中重复?

为什么使用 gdb 时,某些库和其他部分会在 Linux 虚拟内存中重复?

在此输入图像描述

这是在 gdb 中查看进程虚拟内存的结果;我对此有一些疑问:

  1. 为什么虚拟内存的某些部分是重复的?例如,我们的程序(stack6)和libc库重复了4次;如果他们把它们分成不同的部分,那为什么呢?为什么不把它们全部放在一起呢?

  2. 最上面的路径(/opt/pro...)是我们虚拟内存的指令部分(文本部分)并且只包含指令吗?

  3. 为什么 4 个 libc 的大小不同?偏移量有什么用,如果我们已经有了大小和起始地址,那么偏移量有什么用呢?

  4. 数据、bss、内核和堆部分在哪里?为什么上图中的某些部分没有关于它们的信息? gdb 中有没有更好的选项可以实际显示所有部分?

  5. 有没有比 gdb 更好的程序可以更好地显示我们进程的虚拟内存部分?我只是想清楚地看到实际的虚拟内存,哪个调试程序可以提供最佳结果。

我提到的部分:

在此输入图像描述

答案1

的输出中缺少一条重要信息gdb:页面的权限。 (他们显示在 Solaris 和 FreeBSD 上,但在 Linux 上不行。)您可以通过查看/proc/<pid>/maps;来查看这些内容。 Protostar 示例的地图显示

$ cat /proc/.../maps
08048000-08049000 r-xp 00000000 00:0f 2925       /opt/protostar/bin/stack6
08049000-0804a000 rwxp 00000000 00:0f 2925       /opt/protostar/bin/stack6
b7e96000-b7e97000 rwxp 00000000 00:00 0
b7e97000-b7fd5000 r-xp 00000000 00:0f 759        /lib/libc-2.11.2.so
b7fd5000-b7fd6000 ---p 0013e000 00:0f 759        /lib/libc-2.11.2.so
b7fd6000-b7fd8000 r-xp 0013e000 00:0f 759        /lib/libc-2.11.2.so
b7fd8000-b7fd9000 rwxp 00140000 00:0f 759        /lib/libc-2.11.2.so
b7fd9000-b7fdc000 rwxp 00000000 00:00 0
b7fe0000-b7fe2000 rwxp 00000000 00:00 0
b7fe2000-b7fe3000 r-xp 00000000 00:00 0          [vdso]
b7fe3000-b7ffe000 r-xp 00000000 00:0f 741        /lib/ld-2.11.2.so
b7ffe000-b7fff000 r-xp 0001a000 00:0f 741        /lib/ld-2.11.2.so
b7fff000-b8000000 rwxp 0001b000 00:0f 741        /lib/ld-2.11.2.so
bffeb000-c0000000 rwxp 00000000 00:0f 0          [stack]

(Protostar 示例在易于破解的 VM 中运行,大概是为了使练习易于处理:没有 NX 保护,也没有 ASLR。)

您将在上面看到,看似重复的映射实际上gdb对应于具有不同权限的不同映射。文本段被映射为只读且可执行;数据段被映射为只读; BSS 和堆被映射为读写。理想情况下,数据段、BSS 和堆是不可执行的,但此示例缺乏 NX 支持,因此它们是可执行的。每个共享库都有自己的文本段、数据段和 BSS 映射。第四个映射是一个不可读、不可写、不可执行的段,通常用于防止缓冲区溢出(尽管考虑到此处使用的内核和 C 库的年龄,这可能有所不同)。

给出的偏移量表示文件内数据的偏移量,这不一定与其在地址空间中的位置有太大关系。加载时,会受到对齐约束;例如,libc-2.11.2.so的程序头指定了两个“LOAD”头:

Type           Offset   VirtAddr   PhysAddr   FileSiz  MemSiz   Flg Align
LOAD           0x000000 0x00000000 0x00000000 0x13d2f4 0x13d2f4 R E 0x1000
LOAD           0x13e1cc 0x0013f1cc 0x0013f1cc 0x027b0  0x0577c  RW  0x1000

(用readelf -l来看这个。)

如果映射到段的部分具有不同的保护标志,则这些可能会导致在相同偏移处具有不同虚拟地址的多个映射。在stack6的情况下:

Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
LOAD           0x000000 0x08048000 0x08048000 0x00604 0x00604 R E 0x1000
LOAD           0x000604 0x08049604 0x08049604 0x00114 0x00128 RW  0x1000

proc info mappings(这也解释了for显示的小尺寸stack6:每个标头请求小于 4KiB,采用 4KiB 对齐方式,因此它会在不同地址获得两个具有相同偏移量的 4KiB 映射。)

空白映射对应匿名映射;看man 5 proc了解详情。您需要深入mmap了解gdb它们对应的内容。

您看不到内核映射(除了vsyscall某些体系结构上的遗留映射),因为从进程的角度来看它们并不重要(它们是不可访问的)。

我不知道有更好的gdb选择,我总是使用/proc/$$/maps.

程序如何运行:ELF 二进制文件有关内核读取的 ELF 格式的详细信息,以及它如何映射到内存分配;它指向更多参考材料。

相关内容