両方のアサーションは実際には正しい(ある程度までは)。
実行可能なELFファイルの場合、リンカはリンカスクリプトを使用して仮想空間内のアドレスをプログラムのすべてのシンボルに割り当てます(これらはすべて開始アドレスとサイズを持つセクションにグループ化されています)。 ld --verbose
を呼び出すことによって使用される既定のスクリプトが表示されます。バイナリのセクションとそのアドレスはreadelf
またはobjdump
などのツールを使用して表示できます。 readelf -l /bin/cat
。次にcat /proc/self/maps
を実行する場合は、/bin/cat
がマップされているアドレスが一致する必要があります。したがって、execve
カーネルシステムコールは、現在のプロセスのアドレス空間を、引数として与えられた実行可能ファイルがマップされている新しいものに置き換えます。
もちろん、コードのすべてのビットに静的アドレスが割り当てられていれば、共有ライブラリの問題を解決できます。共有ライブラリは位置に依存しないコードを使用するため、プロセスアドレス空間のどこにでもマップすることができます。ここで、カーネルはどのように進めるかについて決定を下します。
mmap
(1)または(2)のいずれも、アドレス空間の所定のアドレスにメモリまたはファイルの一部をマップしません(またはカーネルに使用するアドレスを決定させる)。実際には、プログラムが使用する共有ライブラリをマップするために使用されます。方法を見るには、strace /bin/true
を実行し、最初にexecve
が呼び出され、プロセスのアドレス空間がバイナリファイルから作成され、libcファイルがどのように開かれ、適切なセクションが適切な権限でmmapされているかを確認してください。
execve("/bin/true", ["/bin/true"], [/* 69 vars */]) = 0
...
open("/lib/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
mmap(NULL, 3804080, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f224f351000
mprotect(0x7f224f4e8000, 2097152, PROT_NONE) = 0
mmap(0x7f224f6e8000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x197000) = 0x7f224f6e8000
以下の記事も読んで価値があるかもしれない: