/proc/PID/maps 中发现的分散地图

Scattered maps found in /proc/PID/maps

我在 Linux 系统 上检查 Python 进程 的内存映射,发现了一些非常令人惊讶的东西。通常,当我检查 Python 进程的地图时,它们看起来像这样:

00400000-00401000 r-xp 00000000 fe:01 2904802                            python3.9
00600000-00601000 r--p 00000000 fe:01 2904802                            python3.9
00601000-00602000 rw-p 00001000 fe:01 2904802                            python3.9
00637000-00abe000 rw-p 00000000 00:00 0                                  [heap]
...
7f67d8565000-7f67d8593000 rw-p 00000000 00:00 0
7f67d8593000-7f67d88ea000 r-xp 00000000 fe:01 2904547                    libpython3.9.so.1.0
7f67d88ea000-7f67d8ae9000 ---p 00357000 fe:01 2904547                    libpython3.9.so.1.0
7f67d8ae9000-7f67d8aef000 r--p 00356000 fe:01 2904547                    libpython3.9.so.1.0
7f67d8aef000-7f67d8b29000 rw-p 0035c000 fe:01 2904547                    libpython3.9.so.1.0
7f67d8b29000-7f67d8b4b000 rw-p 00000000 00:00 0
...
7fff72a4f000-7fff72a70000 rw-p 00000000 00:00 0                          [stack]
7fff72a7c000-7fff72a80000 r--p 00000000 00:00 0                          [vvar]
7fff72a80000-7fff72a82000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]

它具有以下结构:

  • 与同一 binary/shared 对象关联的所有地图都是连续的。
  • 可执行文件 (python3.9) 的映射首先出现,打开的共享库的映射出现在可执行文件中的之后。这是有道理的,因为首先加载可执行文件,然后加载程序加载 DT_NEEDED 部分中的共享对象。

但是我找到的地图是这样的:

00400000-00401000 r-xp 00000000 fd:00 67488961                           python3.9
00600000-00601000 r--p 00000000 fd:00 67488961                           python3.9
00601000-00602000 rw-p 00001000 fd:00 67488961                           python3.9
0067b000-00a58000 rw-p 00000000 00:00 0                                  [heap]
...
7f7b46014000-7f7b46484000 r--p 0050b000 fd:00 1059871                    libpython3.9.so.1.0
7f7b46484000-7f7b46485000 ---p 00000000 00:00 0
7f7b46485000-7f7b46cda000 rw-p 00000000 00:00 0
7f7b46cda000-7f7b46d16000 r--p 00a3d000 fd:00 1059871                    libpython3.9.so.1.0
7f7b46d16000-7f7b46d6f000 rw-p 00000000 00:00 0
7f7b46d6f000-7f7b46d92000 r--p 00001000 fd:00 67488961                   python3.9
7f7b46d92000-7f7b46d93000 ---p 00000000 00:00 0
7f7b46d93000-7f7b475d3000 rw-p 00000000 00:00 0
...
7f7b5a35d000-7f7b5a827000 r-xp 00000000 fd:00 1059871                    libpython3.9.so.1.0
7f7b5a827000-7f7b5aa27000 ---p 004ca000 fd:00 1059871                    libpython3.9.so.1.0
7f7b5aa27000-7f7b5aa2c000 r--p 004ca000 fd:00 1059871                    libpython3.9.so.1.0
7f7b5aa2c000-7f7b5aa67000 rw-p 004cf000 fd:00 1059871                    libpython3.9.so.1.0
7f7b5aa67000-7f7b5aa8b000 rw-p 00000000 00:00 0
...
7fff26f8e000-7fff27020000 rw-p 00000000 00:00 0                          [stack]
7fff27102000-7fff27106000 r--p 00000000 00:00 0                          [vvar]
7fff27106000-7fff27108000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  /vsyscall]

这有以下奇怪之处:

  • 没有与 python3.9libpython3.9.so.1.0 共享对象关联的连续段块。确实,这些有分散的块。
  • 在共享库中之后可以找到可执行文件的映射:
7f7b46cda000-7f7b46d16000 r--p 00a3d000 fd:00 1059871                    libpython3.9.so.1.0
7f7b46d16000-7f7b46d6f000 rw-p 00000000 00:00 0
7f7b46d6f000-7f7b46d92000 r--p 00001000 fd:00 67488961                   python3.9
7f7b46d92000-7f7b46d93000 ---p 00000000 00:00 0

您知道什么会导致这种影响或在什么情况下会发生这种情况吗?您知道在 几个共享对象之后 加载可执行文件的内存映射是怎么可能的吗?

注意:这是使用内核 Kernel 5.13.12-100.fc33.x86_64

Do you know what can cause this effect or in what conditions this can happen?

一个 executable 可以简单地 mmap(部分)自身。这可以做到例如检查它自己的符号 table(打印崩溃堆栈跟踪所必需的),或提取一些嵌入的资源。

The maps for the executable (python3.9) appear first and the map for a shared library that is opened appear after the ones in the executable.

这只是偶然的,并且仅适用于非 PIE executables。

x86_64 上的非 PIE executables 传统上 link 加载地址 0x400000,并且共享库通常从主堆栈下方开始加载。

如果您 link 一个非 PIE executable 加载,例如0x7ff000000000,那么它很可能会出现在 /proc/$pid/maps after 共享库中。

更新:

the python binary here is certainly not mmapping itself, so that explanation doesn't apply

  1. 不知道——你几乎可以肯定没有阅读所有 Python 3.9 中的代码您加载的每个模块。
  2. 没有必要猜测这些mmaped区域来自哪里,你可以

要查看,运行 您在 GDB 下的程序并使用 catch syscall mmap,然后使用 where。这将允许您查看每个映射的来源。