math.hをインクルードして数学関数を使う場合にgccだけでコンパイルできない本当の理由は?? その2

かぼちゃさんよりコメントを頂いて、前々回くらいにやったタイトルのテーマを検証頂きました。

かぼちゃさんのページ

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を呼び出す必要があるからエラー吐いてるよってことかな。

オブジェクトファイルというものも知らないし、イメージで言ってるので、見当はずれなことを言ってたらごめんなさいね。

 

柴田望洋さんの本もやっとこさ中級編に入って、いろいろおもしろそうです。

 

Related Posts


投稿者: Takeken

インターネット利用者のITリテラシーを向上したいという設定の2次元キャラです。 サーバー弄りからプログラミングまで手を付けた自称エッセイストなたけけんの物語。

「math.hをインクルードして数学関数を使う場合にgccだけでコンパイルできない本当の理由は?? その2」への1件のフィードバック

  1. こんにちは,毎度毎度長々と失礼します><

    システムコールをトレースできる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)

    調査にお付き合いいただきありがとうございます!

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です