目次: Linux
ちょっと用事があって、ユーザプロセスの仮想アドレスから、物理アドレスへの変換を行うプログラムを作りました。名前はv2p(virt2phys)です。何の捻りもありません。ソースコードは GitHubに置いています。
やっていることは/proc/pid/pagemapを読んで、わかりやすい形式で表示しているだけです。言葉で説明するより動いている姿の方がわかりやすいと思います。
実験に使った環境はDebian GNU/Linux Testing amd64版です。別にDebianじゃなくても最近のLinuxなら動くと思います。
このツールの実行には、プロセスIDと仮想アドレスが必要です。まず解析に使うプロセスを作って、プロセスIDを調べます。
$ cat [Press Ctrl+Z] [1]+ Stopped cat $ ps PID TTY TIME CMD 2148 pts/4 00:00:00 bash 14010 pts/4 00:00:00 cat ★★catのPIDを覚えておく★★ 14015 pts/4 00:00:00 ps
プロセスIDがわかったら、物理アドレスに変換したい仮想アドレスを決めます。ここでは例としてヒープ領域の先頭アドレスを変換しましょう。
$ cat /proc/14010/maps 55ff09a4f000-55ff09a57000 r-xp 00000000 08:11 5505114 /bin/cat 55ff09c56000-55ff09c57000 r--p 00007000 08:11 5505114 /bin/cat 55ff09c57000-55ff09c58000 rw-p 00008000 08:11 5505114 /bin/cat 55ff0b2cb000-55ff0b2ec000 rw-p 00000000 00:00 0 [heap] ★★これを使う★★ 7fdbeb6a3000-7fdbeb838000 r-xp 00000000 08:11 4980859 /lib/x86_64-linux-gnu/libc-2.24.so 7fdbeb838000-7fdbeba38000 ---p 00195000 08:11 4980859 /lib/x86_64-linux-gnu/libc-2.24.so ...(snip)... 7ffdde79c000-7ffdde7be000 rw-p 00000000 00:00 0 [stack] 7ffdde7fb000-7ffdde7fd000 r--p 00000000 00:00 0 [vvar] 7ffdde7fd000-7ffdde7ff000 r-xp 00000000 00:00 0 [vdso]
プロセスIDと仮想アドレスがわかったので、物理アドレスに変換します。
$ sudo ./src/v2p 14010 0x55ff0b2cb000 0x4000 pid: 14010: virt:0x55ff0b2cb000, phys:0x73a42b000 virt:0x55ff0b2cc000, phys:(not present) virt:0x55ff0b2cd000, phys:(not present) virt:0x55ff0b2ce000, phys:(not present)
変換できました。特権のない状態で実行すると全て0x00000000になってしまいますので、お気をつけください。
表示の意味は特に何の捻りもなく、物理アドレスをそのまま示しています。ちなみに (not present) と表示される場合は、まだカーネルからページが割り当てられておらず、仮想アドレスに対応する物理アドレスが存在しないことを意味しています。
これだけだとGitHubに書いたReadmeそのものだし、へぇー…って感じがするだけで面白くないので、catにgdbでアタッチしてヒープ領域を触ったらどうなるか見てみます。
$ sudo ./src/v2p 14010 0x55ff0b2cb000 0x6000 pid: 14010: virt:0x55ff0b2cb000, phys:0x73a42b000 virt:0x55ff0b2cc000, phys:(not present) virt:0x55ff0b2cd000, phys:(not present) ★これを読む★ virt:0x55ff0b2ce000, phys:(not present) virt:0x55ff0b2cf000, phys:(not present) ★これを読む★ virt:0x55ff0b2d0000, phys:(not present) $ gdb -p 14010 GNU gdb (Debian 7.12-6) 7.12.0.20161007-git Copyright (C) 2016 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> ... 0x00007fdbeb77e690 in __read_nocancel () at ../sysdeps/unix/syscall-template.S:84 84 ../sysdeps/unix/syscall-template.S: そのようなファイルやディレクトリはありません. (gdb) x/4x 0x55ff0b2cd000 0x55ff0b2cd000: 0x00000000 0x00000000 0x00000000 0x00000000 (gdb) x/4x 0x55ff0b2cf000 0x55ff0b2cf000: 0x00000000 0x00000000 0x00000000 0x00000000 $ sudo ./src/v2p 14010 0x55ff0b2cb000 0x4000 pid: 14010: virt:0x55ff0b2cb000, phys:0x73a42b000 virt:0x55ff0b2cc000, phys:(not present) virt:0x55ff0b2cd000, phys:0x1cb695000★ゼロページが割り当たる★ virt:0x55ff0b2ce000, phys:(not present) virt:0x55ff0b2cf000, phys:0x1cb695000★ゼロページが割り当たる★ virt:0x55ff0b2d0000, phys:(not present)
試しに3番目と5番目の領域からデータを読み出してみました、どちらも0データが見えます。読み出したあとは0x1cb695000(数字は環境によって違います)という同じアドレスが割り当たりました。このアドレスは全て0で埋まっている特殊なページを指していて、読み出したときにとにかく0データを返せば良い領域に使われるらしいです。
じゃあ今度は書いてみたらどうなるでしょう?
$ sudo ./src/v2p 14010 0x55ff0b2cb000 0x6000 pid: 14010: virt:0x55ff0b2cb000, phys:0x73a42b000 virt:0x55ff0b2cc000, phys:(not present) ★これに書く★ virt:0x55ff0b2cd000, phys:0x1cb695000 ★これに書く★ virt:0x55ff0b2ce000, phys:(not present) virt:0x55ff0b2cf000, phys:0x1cb695000 virt:0x55ff0b2d0000, phys:(not present) (gdb) set *((long *)(0x55ff0b2cc000))=0x89abcdef (gdb) x/4x 0x55ff0b2cc000 0x55ff0b2cc000: 0x89abcdef 0x00000000 0x00000000 0x00000000 (gdb) set *((long *)(0x55ff0b2cd000))=0x12345678 (gdb) x/4x 0x55ff0b2cd000 0x55ff0b2cd000: 0x12345678 0x00000000 0x00000000 0x00000000 $ sudo ./src/v2p 14010 0x55ff0b2cb000 0x4000 pid: 14010: virt:0x55ff0b2cb000, phys:0x73a42b000 virt:0x55ff0b2cc000, phys:0x73c76a000★ページが割り当たる★ virt:0x55ff0b2cd000, phys:0x72edd0000★ゼロページが外され、別のページが割り当たる★ virt:0x55ff0b2ce000, phys:(not present) virt:0x55ff0b2cf000, phys:0x1cb695000 virt:0x55ff0b2d0000, phys:(not present)
2番目の (not present) な領域にも書けますし、3番目のゼロページが割り当たっている領域にも書けます。読み出してみても、ちゃんと書けていることがわかります。
書き込んだあとの仮想アドレスと物理アドレスの対応を見ると、2番目はreadした時とは違ってゼロページではなさそうなアドレスが割当たります。3番目はゼロページが外され、別のページが割り当てられます。
不思議に見えるかも知れませんが、ゼロページは0データを返す専門なので、書き込みができません、というかカーネルが書き込みを許しません。ゼロページに書き込もうとした場合はゼロページを外して別のページを割り当て、以降はそのページを使って下さい、という仕組みになっています。
< | 2017 | > | ||||
<< | < | 07 | > | >> | ||
日 | 月 | 火 | 水 | 木 | 金 | 土 |
- | - | - | - | - | - | 1 |
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 | 31 | - | - | - | - | - |
合計:
本日:
管理者: Katsuhiro Suzuki(katsuhiro( a t )katsuster.net)
This is Simple Diary 1.0
Copyright(C) Katsuhiro Suzuki 2006-2023.
Powered by PHP 8.2.15.
using GD bundled (2.1.0 compatible)(png support.)