Linux 下程序的記憶體映射
Linux 下的執行檔為 ELF 格式,其啟動原理與各作業系統上的執行檔一樣,不外乎是載入檔案到記憶體上,並讀取需要的 Shared library link 清單,最後找到於 Filesystem 上對應的 symbol link 和 library,載入外部函式和執行程式。
這邊以 VIM 為例,我們透過 Linux 下的 ldd 指令可以得知執行檔需要的外部 Libraries:
由以上結果可以看到 VIM 所需的 library,以及在 filesystem 上找到的對應 library ,然後還可以得到當 library 被載入到記憶體上時的起始位置。其中比較特別的是 linux-gate.so.1,實體並不存在於 filesystem 中,這代表 kernel system call 的記憶體映射和其位址。
Kernel 其實有 Interface 提供我們取得更多 Process 的記憶體映射資料,藉由讀取 /proc/[Process ID]/maps 這檔案,我們除了可以得到 Process 連結的 library,還可以得到映射的範圍和使用權限等訊息,。
下面這例子是觀察正在執行的 VIM(PID=17605):
這些看似複雜且嚇人的訊息,每個欄位已清楚描述著此 process 的記憶體映射和對應的實體檔案,由於數量太多,我們暫且抽出 VIM 本身的映射訊息做說明:
『08048000-081c0000』和『081c0000-081ce000』代表程式在記憶體上的起始點和終點,『r-xp』和『rw-p』是存取權限,『00000000』和『00178000』是映射記憶體位址在實體檔案中的偏移量,『08:05』分別代表的是 major 和 minor device number,而『7029211』是 device node number,最後一欄則為記憶體所對應的實體檔案。
此外,在範例中我們看到了幾乎每個程式和 library 都映射了兩個以上的區塊,這意味著第一段是 code segment,而第二,第三段映射是 data segment (data + bss + heap)。
後記
更多的驗證可以閱讀[The True Story of Hello World] 一文,作者透過簡單的 Hello World 程式驗證 segment 和 memory mapping 的觀念,值得參考。
這邊以 VIM 為例,我們透過 Linux 下的 ldd 指令可以得知執行檔需要的外部 Libraries:
$ ldd /usr/bin/vim linux-gate.so.1 => (0xb7837000) libm.so.6 => /lib/i686/cmov/libm.so.6 (0xb77f2000) libncurses.so.5 => /lib/libncurses.so.5 (0xb77b8000) libselinux.so.1 => /lib/libselinux.so.1 (0xb779c000) libacl.so.1 => /lib/libacl.so.1 (0xb7795000) libgpm.so.2 => /usr/lib/libgpm.so.2 (0xb778f000) libc.so.6 => /lib/i686/cmov/libc.so.6 (0xb7649000) libdl.so.2 => /lib/i686/cmov/libdl.so.2 (0xb7645000) /lib/ld-linux.so.2 (0xb7838000) libattr.so.1 => /lib/libattr.so.1 (0xb763f000)
由以上結果可以看到 VIM 所需的 library,以及在 filesystem 上找到的對應 library ,然後還可以得到當 library 被載入到記憶體上時的起始位置。其中比較特別的是 linux-gate.so.1,實體並不存在於 filesystem 中,這代表 kernel system call 的記憶體映射和其位址。
Kernel 其實有 Interface 提供我們取得更多 Process 的記憶體映射資料,藉由讀取 /proc/[Process ID]/maps 這檔案,我們除了可以得到 Process 連結的 library,還可以得到映射的範圍和使用權限等訊息,。
下面這例子是觀察正在執行的 VIM(PID=17605):
$ cat /proc/17605/maps 08048000-081c0000 r-xp 00000000 08:05 7029211 /usr/bin/vim.basic 081c0000-081ce000 rw-p 00178000 08:05 7029211 /usr/bin/vim.basic 081ce000-081d8000 rw-p 00000000 00:00 0 089df000-08b30000 rw-p 00000000 00:00 0 [heap] b739a000-b73a4000 r-xp 00000000 08:05 4202534 /lib/i686/cmov/libnss_files-2.11.2.so b73a4000-b73a5000 r--p 00009000 08:05 4202534 /lib/i686/cmov/libnss_files-2.11.2.so b73a5000-b73a6000 rw-p 0000a000 08:05 4202534 /lib/i686/cmov/libnss_files-2.11.2.so b73a6000-b73b9000 r-xp 00000000 08:05 4202536 /lib/i686/cmov/libnsl-2.11.2.so b73b9000-b73ba000 r--p 00012000 08:05 4202536 /lib/i686/cmov/libnsl-2.11.2.so b73ba000-b73bb000 rw-p 00013000 08:05 4202536 /lib/i686/cmov/libnsl-2.11.2.so b73bb000-b73bd000 rw-p 00000000 00:00 0 b73da000-b73f7000 r--p 00000000 08:05 7074661 /usr/share/vim/vim73/lang/zh_TW.UTF-8/LC_MESSAGES/vim.mo b73f7000-b756d000 r--p 00000000 08:05 7045410 /usr/lib/locale/locale-archive b756d000-b756e000 rw-p 00000000 00:00 0 b756e000-b7572000 r-xp 00000000 08:05 4194576 /lib/libattr.so.1.1.0 b7572000-b7573000 rw-p 00003000 08:05 4194576 /lib/libattr.so.1.1.0 b7573000-b7574000 rw-p 00000000 00:00 0 b7574000-b7576000 r-xp 00000000 08:05 4202526 /lib/i686/cmov/libdl-2.11.2.so b7576000-b7577000 r--p 00001000 08:05 4202526 /lib/i686/cmov/libdl-2.11.2.so b7577000-b7578000 rw-p 00002000 08:05 4202526 /lib/i686/cmov/libdl-2.11.2.so b7578000-b76b8000 r-xp 00000000 08:05 4202537 /lib/i686/cmov/libc-2.11.2.so b76b8000-b76ba000 r--p 0013f000 08:05 4202537 /lib/i686/cmov/libc-2.11.2.so b76ba000-b76bb000 rw-p 00141000 08:05 4202537 /lib/i686/cmov/libc-2.11.2.so b76bb000-b76be000 rw-p 00000000 00:00 0 b76be000-b76c3000 r-xp 00000000 08:05 11665845 /usr/lib/libgpm.so.2.0.0 b76c3000-b76c4000 rw-p 00004000 08:05 11665845 /usr/lib/libgpm.so.2.0.0 b76c4000-b76ca000 r-xp 00000000 08:05 4194402 /lib/libacl.so.1.1.0 b76ca000-b76cb000 rw-p 00006000 08:05 4194402 /lib/libacl.so.1.1.0 b76cb000-b76e4000 r-xp 00000000 08:05 4194310 /lib/libselinux.so.1 b76e4000-b76e5000 r--p 00018000 08:05 4194310 /lib/libselinux.so.1 b76e5000-b76e6000 rw-p 00019000 08:05 4194310 /lib/libselinux.so.1 b76e6000-b76e7000 rw-p 00000000 00:00 0 b76e7000-b771e000 r-xp 00000000 08:05 4194642 /lib/libncurses.so.5.7 b771e000-b7721000 rw-p 00036000 08:05 4194642 /lib/libncurses.so.5.7 b7721000-b7745000 r-xp 00000000 08:05 4202516 /lib/i686/cmov/libm-2.11.2.so b7745000-b7746000 r--p 00023000 08:05 4202516 /lib/i686/cmov/libm-2.11.2.so b7746000-b7747000 rw-p 00024000 08:05 4202516 /lib/i686/cmov/libm-2.11.2.so b774a000-b7752000 r-xp 00000000 08:05 4202519 /lib/i686/cmov/libnss_nis-2.11.2.so b7752000-b7753000 r--p 00008000 08:05 4202519 /lib/i686/cmov/libnss_nis-2.11.2.so b7753000-b7754000 rw-p 00009000 08:05 4202519 /lib/i686/cmov/libnss_nis-2.11.2.so b7754000-b775a000 r-xp 00000000 08:05 4202515 /lib/i686/cmov/libnss_compat-2.11.2.so b775a000-b775b000 r--p 00006000 08:05 4202515 /lib/i686/cmov/libnss_compat-2.11.2.so b775b000-b775c000 rw-p 00007000 08:05 4202515 /lib/i686/cmov/libnss_compat-2.11.2.so b775c000-b7763000 r--s 00000000 08:05 1799068 /usr/lib/gconv/gconv-modules.cache b7763000-b7764000 r--p 00175000 08:05 7045410 /usr/lib/locale/locale-archive b7764000-b7766000 rw-p 00000000 00:00 0 b7766000-b7767000 r-xp 00000000 00:00 0 [vdso] b7767000-b7782000 r-xp 00000000 08:05 4194720 /lib/ld-2.11.2.so b7782000-b7783000 r--p 0001a000 08:05 4194720 /lib/ld-2.11.2.so b7783000-b7784000 rw-p 0001b000 08:05 4194720 /lib/ld-2.11.2.so bff9b000-bffb0000 rw-p 00000000 00:00 0 [stack]
這些看似複雜且嚇人的訊息,每個欄位已清楚描述著此 process 的記憶體映射和對應的實體檔案,由於數量太多,我們暫且抽出 VIM 本身的映射訊息做說明:
08048000-081c0000 r-xp 00000000 08:05 7029211 /usr/bin/vim.basic 081c0000-081ce000 rw-p 00178000 08:05 7029211 /usr/bin/vim.basic
『08048000-081c0000』和『081c0000-081ce000』代表程式在記憶體上的起始點和終點,『r-xp』和『rw-p』是存取權限,『00000000』和『00178000』是映射記憶體位址在實體檔案中的偏移量,『08:05』分別代表的是 major 和 minor device number,而『7029211』是 device node number,最後一欄則為記憶體所對應的實體檔案。
此外,在範例中我們看到了幾乎每個程式和 library 都映射了兩個以上的區塊,這意味著第一段是 code segment,而第二,第三段映射是 data segment (data + bss + heap)。
後記
更多的驗證可以閱讀[The True Story of Hello World] 一文,作者透過簡單的 Hello World 程式驗證 segment 和 memory mapping 的觀念,值得參考。
awesome!
回覆刪除