かぼちゃさんよりコメントを頂いて、前々回くらいにやったタイトルのテーマを検証頂きました。
かぼちゃさんのページ
Blue Sparrow
通信編目
検証ページ
コンパイルのオプションでの切り分けは全くその発想はなかったです。
かぼちゃさんのブログを見てて、なんとなくstraceを思い出したのでやってみた。
ていうかコマンドを知ってるだけで内容は全然分かりません。
でもかぼちゃさんの予測でもlibmとか出てたので全く見当はずれでもないと思うんだ。
結果をダイジェストでどうぞ。
前ふりとして、まずは定数版プログラムを用意
$ gcc kencho2.c -o teisu -Wall
続いて変数版を最適化したものと正規?の-lmをつけてコンパイリます。
gcc kencho.c -o hensu3 -Wall -O3 gcc kencho.c -o hensuseiki -Wall -lm
こちらの環境でもコンパイルは問題なかったです。
リポジトリはSLで、gcc version 4.4.7 20120313 (Red Hat 4.4.7-4) (GCC)です。
ではstraceで動きを見てみます。openに注目してみてください。
$ strace ./teisu execve("./teisu", ["./teisu"], [/* 21 vars */]) = 0 brk(0) = 0x10a9000 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f821d16f000 access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory) open("/etc/ld.so.cache", O_RDONLY) = 3 fstat(3, {st_mode=S_IFREG|0644, st_size=14638, ...}) = 0 mmap(NULL, 14638, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f821d16b000 close(3) = 0 open("/lib64/libc.so.6", O_RDONLY) = 3 read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0000\356\1\0\0\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0755, st_size=1921216, ...}) = 0 mmap(NULL, 3750152, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f821cbbd000 mprotect(0x7f821cd48000, 2093056, PROT_NONE) = 0 mmap(0x7f821cf47000, 20480, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x18a000) = 0x7f821cf47000 mmap(0x7f821cf4c000, 18696, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f821cf4c000 close(3) = 0 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f821d16a000 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f821d169000 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f821d168000 arch_prctl(ARCH_SET_FS, 0x7f821d169700) = 0 mprotect(0x7f821cf47000, 16384, PROT_READ) = 0 mprotect(0x7f821d170000, 4096, PROT_READ) = 0 munmap(0x7f821d16b000, 14638) = 0 fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f821d16e000 write(1, "10.000\n", 710.000 ) = 7 exit_group(0) = ?
$ strace ./hensu3 execve("./hensu3", ["./hensu3"], [/* 21 vars */]) = 0 brk(0) = 0x242b000 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fac367be000 access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory) open("/etc/ld.so.cache", O_RDONLY) = 3 fstat(3, {st_mode=S_IFREG|0644, st_size=14638, ...}) = 0 mmap(NULL, 14638, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fac367ba000 close(3) = 0 open("/lib64/libc.so.6", O_RDONLY) = 3 read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0000\356\1\0\0\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0755, st_size=1921216, ...}) = 0 mmap(NULL, 3750152, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fac3620c000 mprotect(0x7fac36397000, 2093056, PROT_NONE) = 0 mmap(0x7fac36596000, 20480, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x18a000) = 0x7fac36596000 mmap(0x7fac3659b000, 18696, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fac3659b000 close(3) = 0 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fac367b9000 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fac367b8000 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fac367b7000 arch_prctl(ARCH_SET_FS, 0x7fac367b8700) = 0 mprotect(0x7fac36596000, 16384, PROT_READ) = 0 mprotect(0x7fac367bf000, 4096, PROT_READ) = 0 munmap(0x7fac367ba000, 14638) = 0 fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fac367bd000 write(1, "10.000\n", 710.000 ) = 7 exit_group(0) = ?
$ strace ./hensuseiki execve("./hensuseiki", ["./hensuseiki"], [/* 21 vars */]) = 0 brk(0) = 0x17bf000 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb9bbe1a000 access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory) open("/etc/ld.so.cache", O_RDONLY) = 3 fstat(3, {st_mode=S_IFREG|0644, st_size=14638, ...}) = 0 mmap(NULL, 14638, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fb9bbe16000 close(3) = 0 open("/lib64/libm.so.6", O_RDONLY) = 3 read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0p>\0\0\0\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0755, st_size=596264, ...}) = 0 mmap(NULL, 2633912, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fb9bb978000 mprotect(0x7fb9bb9fb000, 2093056, PROT_NONE) = 0 mmap(0x7fb9bbbfa000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x82000) = 0x7fb9bbbfa000 close(3) = 0 open("/lib64/libc.so.6", O_RDONLY) = 3 read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0000\356\1\0\0\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0755, st_size=1921216, ...}) = 0 mmap(NULL, 3750152, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fb9bb5e4000 mprotect(0x7fb9bb76f000, 2093056, PROT_NONE) = 0 mmap(0x7fb9bb96e000, 20480, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x18a000) = 0x7fb9bb96e000 mmap(0x7fb9bb973000, 18696, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fb9bb973000 close(3) = 0 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb9bbe15000 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb9bbe14000 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb9bbe13000 arch_prctl(ARCH_SET_FS, 0x7fb9bbe14700) = 0 mprotect(0x7fb9bb96e000, 16384, PROT_READ) = 0 mprotect(0x7fb9bbbfa000, 4096, PROT_READ) = 0 mprotect(0x7fb9bbe1b000, 4096, PROT_READ) = 0 munmap(0x7fb9bbe16000, 14638) = 0 fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb9bbe19000 write(1, "10.000\n", 710.000 ) = 7 exit_group(0) = ?
ここで確認できたのが-lmでコンパイルしたものだけ/lib64/libm.so.6を呼び出していて、定数のものと変数を最適化したものは/lib64/libc.so.6の呼び出しだけで実行できている様子。
となると、かぼちゃさんの検証結果から。
引数に定数を入れたときバイナリに計算結果のみが反映され,オブジェクトファイルを無視する
ってことは、libcだけで実行できるから-lmはいらない。libmを呼び出す必要があるからエラー吐いてるよってことかな。
オブジェクトファイルというものも知らないし、イメージで言ってるので、見当はずれなことを言ってたらごめんなさいね。
柴田望洋さんの本もやっとこさ中級編に入って、いろいろおもしろそうです。
こんにちは,毎度毎度長々と失礼します><
システムコールをトレースできるstraceコマンドがあったんですね.
勉強になります.
オブジェクトファイル[2]は関数一つ一つをプログラムにしたものという認識で良いと思います.
一つ一つはmain関数を持っていないので実行可能ファイルとはなりませんが,新しくmain関数から任意の関数を呼び出すとき,既にコンパイルされているので連結するだけで済みます.
libc[1]はc言語において必要最低限の定義をしたライブラリみたいですね.
/usr/lib/x86_64-linux-gnu/libc.soには次のように書かれていました.
*5 GROUP ( /lib/x86_64-linux-gnu/libc.so.6 /usr/lib/x86_64-linux-gnu/libc_nonshared.a AS_NEEDED ( /lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 ) )
なお./glibc-2.19/Makefileでは126行目でmake指示がありましたが,common-objpfxがどこで定義されているのかわかりませんでした.
126 lib: $(common-objpfx)libc.so $(common-objpfx)linkobj/libc.so
さらにlibm.soについては/usr/lib/x86_64-linux-gnu/libm.soとしてオブジェクトファイルがありました.
(実装は関数一つに対して,ソースが一つかかれている.
結論
math関数群のオブジェクトファイルがリンクされないと関数の実装がないので自分のプログラムでmath関数群は使えない.
このため本来は-lmでmathライブラリにリンクしなければならない.
しかし,コンパイル時にソースファイルで関数であっても,バイナリファイルにおいて定数で置き換えられる部分はコンパイラが代わりに計算してれるのでプログラムにmath関数群が必要ない→リンクがいらない.(これは逆アセンブルしないと証拠足りえませんが,変数最適化タイプへのstrace結果からリンクされていないことがわかる(リンク指示だしてないので当然といえば当然の結果)ので間違いないでしょう.)
math.hをincludeしなければならないのは,関数のプロトタイプ宣言がなされていないから.
おそらく手作業でプロトタイプ宣言をしてもよいが,math.hを読んでみるととても面倒そうです.
参考
[1] 佐野武俊, Linux C Library (libc) について
http://linuxjf.sourceforge.jp/JFdocs/libc-intro.html ( accessed Feb. 2014)
[2] CrioCube, コンパイルとリンク
http://www.curiocube.com/mikata/hello/ch14_complink.php ( accessed Feb. 2014)
調査にお付き合いいただきありがとうございます!