ldd(1) の動的な側面が悪い方向に際立つ例
思えば1年半も前のことだったが... 分かりやすい例に仕立ててみよう.以下の内容のCソース hugebss.c
を作る:
char arr[sizeGb*1024ULL*1024ULL*1024ULL]; int main() { }
長い配列 arr
のサイズを変えて2通りにコンパイルし, ldd(1)
してみる:
$ gcc -DsizeGb=1ULL hugebss.c -o 1gb.exe $ ldd 1gb.exe linux-vdso.so.1 => (0x00007fffa1799000) libc.so.6 => /lib/libc.so.6 (0x00007f338e5f6000) /lib64/ld-linux-x86-64.so.2 (0x00007f338e96a000) $ gcc -DsizeGb=64ULL hugebss.c -o 64gb.exe $ ldd 64gb.exe not a dynamic executable
初めて見ると「え? あれ?」みたいな(笑
"not a dynamic executable" とか適当なことをほざく ldd(1)
というシェルスクリプトが,中で何をやってるのか見ればすぐ分かるんだけどね.実際の仕事はOSコアの一部の動的ローダ ld-linux(8)
(/lib/ld-linux.so
) が実際にロードすることで行っており, ldd(1)
は引数の処理とかエラー処理とかのラッピング担当なんだが,その際に逆に ld-linux(8)
がエラー時にきちんと出している診断を握りつぶしておかしなメッセージを出しているのだった.このいい加減さがLinuxらしいと言うのかな.中を追うには,まず cp `which ldd` ldd; vim ldd
して,2行目に set -x
して実行すると((私の環境の dash と bash では /bin/sh -x
が使えるけど,これってどのくらい可搬性あるんだろうか... bash で使えるならubiquitousと言ってよいか.)),各行の実行がトレースされるので分かりやすい.
(追記: 新しくファイル作らなくても bash -x `which ldd` your-command
でいける.)
肝である動的ローダの実行だけ取り出して自分の手でやってみよう.bssセクションで 64 GB もメモリを消費する 64gb.exe のロードに失敗し*1,以下のように適切なエラーを表示するのが分かる:
$ /lib/ld-linux-x86-64.so.2 --list ./1gb.exe linux-vdso.so.1 => (0x00007fff545bf000) libc.so.6 => /lib/libc.so.6 (0x00007fefa4db0000) /lib64/ld-linux-x86-64.so.2 => /lib/ld-linux-x86-64.so.2 (0x00007fefa5124000) $ /lib/ld-linux-x86-64.so.2 --list ./64gb.exe ./64gb.exe: error while loading shared libraries: ./64gb.exe: cannot map zero-fill pages: Cannot allocate memory
これが "not a dynamic executable" に化けるんだからひどい話だ.Windows用ツール dependencyWalker は同じ役割だがPE (Portable Executable) ヘッダを静的に解析してる気がする.だから上記のような問題は起きない.動的に解析しない方がよいと思うんだが... まぁパスの解決の問題がややこしいし,上記のような妙な場合以外は「有り物」を使って楽に済ませるのがよいということか.
Linuxにだって実行形式ファイルを静的に解析して属性を表示するツールは readelf(1)
, objdump(1)
など何種類もあるが,一番単純な binutils の size(1)
の動作を見てみよう:
$ size 1gb.exe text data bss dec hex filename 1052 512 1073741856 1073743420 4000063c 1gb.exe $ size 64gb.exe text data bss dec hex filename 1052 512 68719476768 68719478332 100000063c 64gb.exe
これこそが静的なツールの動作で,いくらbssセクションがでかくたって数字を読み上げるだけのかんたんなお仕事だ.
あーもちろん,ldd(1)
は動的リンクされる .so を探すだけじゃなくて,マップされる仮想アドレスの情報という貴重なものも出してくれるんだけど... 世の中の 99.99 % の人は => より右を読んでないと思うんだよな.もし読むなら以下と合わせてどうぞ: