
Ubuntu日本語フォーラム
ログインしていません。
こんにちは。
17.10でC言語を学んでいます。
コンパイラーは以下のバージョンです。
gcc version 7.2.0 (Ubuntu 7.2.0-8ubuntu3.2)
さてmath.hを利用して.cファイルを書き、コンパイルすると
以下のようなエラーメッセージが出ます。
/tmp/cc2VWvot.o: 関数 `main' 内:
(.text+0x4e): `sin' に対する定義されていない参照です
collect2: error: ld returned 1 exit status
このため検索したところ、
ubuntu
$ cc -v
gcc バージョン 4.6.3 (Ubuntu/Linaro 4.6.3-1ubuntu5)
×cc -lm hoge.c
○cc hoge.c -lm
FreeBSD
$ gcc -v
gcc version 4.6.3 (FreeBSD Ports Collection)
○gcc -lm hoge.c
○gcc hoge.c -lm
$ clang -v
FreeBSD clang version 3.1
○clang -lm hoge.c
○clang hoge.c -lm
という結果を得ました。
これは仕様ですか?それともバグですか?
確かに
gcc -lm hoge.c -o hoge.out
とすると、異常なく終了でき、
gcc hoge.c -lm -o hoge.out
とすると上記のエラーメッセージが出ます。
気になりましたので質問しました。
ご存じの方お教えください。
オフライン
shermansweetさん こんにちは
> 確かに
> gcc -lm hoge.c -o hoge.out
> とすると、異常なく終了でき、
> gcc hoge.c -lm -o hoge.out
> とすると上記のエラーメッセージが出ます。
ご質問があるのですが、気になって手元で動かしてみたところ自分の手元では逆になりました。
---------------
$ gcc -lm hoge.c -o hoge.out
/tmp/ccQNUDXd.o: In function `main':
hoge.c:(.text+0x27): undefined reference to `sqrt'
hoge.c:(.text+0x42): undefined reference to `sin'
collect2: error: ld returned 1 exit status
$ gcc hoge.c -lm -o hoge.out
$ ls hoge.out
hoge.out
---------------
https://forums.ubuntulinux.jp/viewtopic.php?id=13673
上記のURL のソースコードをコンパイルしてみました。
自分も幾つか調べてみましたが、Linux では-l オプションは後側につけるのが一般(?)みたいです。
オフライン
> 確かに
> gcc -lm hoge.c -o hoge.out
> とすると、異常なく終了でき、
> gcc hoge.c -lm -o hoge.out
> とすると上記のエラーメッセージが出ます。
自分の手元では逆になりました。
ごめんなさい。間違えました。逆でした。
おっしゃるとおり
gcc hoge.c -lm -o hoge.outで異常なく終了です。
自分も幾つか調べてみましたが、Linux では-l オプションは後側につけるのが一般(?)みたいです。
ありがとうございます。
郷にいては郷に従えかな。
オフライン
shermansweet さん こんにちは
ご連絡ありがとうございます。
いえいえこちらこそ唐突に申し訳ありませんでした。
昨日まだ少し調べていたことがありましてそのつじつま合わせ的に、確認の投稿をいたしました。
-lm オプションの位置によるコンパイルエラーですが、以下のページにも同様なQA がありました。
https://stackoverflow.com/questions/20731220/compile-error-gcc-lpthread-position
C言語はgcc でコンパイルすると
[C のソースコード] -- プリプロセス --> [前処理完了ソース] -- コンパイル --> [アセンブリのソース] -- アセンブル --> [バイトコード] -- リンク --> [実行可能ファイル]
というふうに処理を行いますが本問題はリンクする処理(リンカ)の問題になります。
上記URL によると、例えば
----
gcc hoge.c -lm
// ubuntu でcc はgcc と同等なのでgcc で書いていきます
----
とgcc を実施した場合、-lm オプションを読み取りライブラリのパスからlibm.so を検索します。
その結果ubuntu の場合/usr/lib/x86_64-linux-gnu/libm.so にそれがあります。
見方を変えれば先程のコマンドは以下のコマンドと同等になり、問題なくコンパイルされます。
gcc hoge.c /usr/lib/x86_64-linux-gnu/libm.so
それで次に-lm オプションをhoge.c よりも前に持ってきた場合にエラーが出る件ですが、
先程示したQA によると-lm を指定した場合、-lm よりも"前"に指定したファイル(今回の場合はhoge.c)内にlibm.so の変数や関数が記載されているかが確認されます。
今回の場合はsin 関数あたりになります。
で、なぜ"前" なのかというとそちらのほうがメモリの効率がよく、メモリが厳しく限られていた時代には非常に合理的だったとのことです。
なので、ubuntu ではまだその古いアーキファクトを守っているのではないでしょうか。
> 郷にいては郷に従えかな。
まさにおっしゃるとおりだと思います。
……でこれから先は個人的な調査なのですが、
なぜ同じgcc を使っていても-lm オプションを前に持ってきても大丈夫な環境があるのか気になったので調べてみました。
shermansweet さんが示されたものでは、FreeBSD になりますが、自分にはその環境がないのでFedora で確認しました。
Fedora でもFreeBSD と同様に
gcc -lm hoge.c
とやってコンパイルが成功します。
でリンカがどのようなコマンドやオプションでリンク処理を行っているか見たところ
ubuntu もFedora もリンカにはcollect2 を使用しています(一般的な参考書ではld コマンドが紹介されることが多いかと思います)。
それでubuntu はcollect2 コマンドの一部に--as-needed オプションをつかって必要なときのみ依存解決のためのtable が作成されるようになりますがそのオプションを削除してあげれば-lm オプションの位置に関係なくコンパイルが成功するようになります。
/usr/lib/gcc/x86_64-linux-gnu/7/collect2 \
-plugin /usr/lib/gcc/x86_64-linux-gnu/7/liblto_plugin.so \
-plugin-opt=/usr/lib/gcc/x86_64-linux-gnu/7/lto-wrapper \
-plugin-opt=-fresolution=/tmp/ccsgaCHg.res \
-plugin-opt=-pass-through=-lgcc \
-plugin-opt=-pass-through=-lgcc_s \
-plugin-opt=-pass-through=-lc \
-plugin-opt=-pass-through=-lgcc \
-plugin-opt=-pass-through=-lgcc_s \
--sysroot=/ --build-id --eh-frame-hdr -m elf_x86_64 \
- --hash-style=gnu --as-needed \
+ --hash-style=gnu \ # <- --as-needed を削除
-dynamic-linker /lib64/ld-linux-x86-64.so.2 \
-pie -z now -z relro \
/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/Scrt1.o \
/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crti.o \
/usr/lib/gcc/x86_64-linux-gnu/7/crtbeginS.o -L/usr/lib/gcc/x86_64-linux-gnu/7 \
-L/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu \
-L/usr/lib/gcc/x86_64-linux-gnu/7/../../../../lib \
-L/lib/x86_64-linux-gnu -L/lib/../lib \
-L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib \
-L/usr/lib/gcc/x86_64-linux-gnu/7/../../.. \
-lm ./hoge.o \ # <- "-lm" オプションがhoge.o よりも前にきている部分
-lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed \
/usr/lib/gcc/x86_64-linux-gnu/7/crtendS.o \
/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crtn.o
これをつかって一番最初に示したプリプロセス、コンパイル、アセンブル... という処理を細かく実行すると、例として以下のようになります。
-------------------------------------------------------------------
# 名前変更
mv hoge.c hoge_org.c
# プリプロセス
gcc -E hoge_org.c > hoge.c
# アセンブリにコンパイル
gcc -S hoge.c -o hoge.s
# アセンブル
as hoge.s -o hoge.o
# リンク
/usr/lib/gcc/x86_64-linux-gnu/7/collect2 \
-plugin /usr/lib/gcc/x86_64-linux-gnu/7/liblto_plugin.so \
-plugin-opt=/usr/lib/gcc/x86_64-linux-gnu/7/lto-wrapper \
-plugin-opt=-fresolution=/tmp/ccsgaCHg.res \
-plugin-opt=-pass-through=-lgcc \
-plugin-opt=-pass-through=-lgcc_s \
-plugin-opt=-pass-through=-lc \
-plugin-opt=-pass-through=-lgcc \
-plugin-opt=-pass-through=-lgcc_s \
--sysroot=/ --build-id --eh-frame-hdr -m elf_x86_64 \
--hash-style=gnu \
-dynamic-linker /lib64/ld-linux-x86-64.so.2 \
-pie -z now -z relro \
/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/Scrt1.o \
/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crti.o \
/usr/lib/gcc/x86_64-linux-gnu/7/crtbeginS.o -L/usr/lib/gcc/x86_64-linux-gnu/7 \
-L/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu \
-L/usr/lib/gcc/x86_64-linux-gnu/7/../../../../lib \
-L/lib/x86_64-linux-gnu -L/lib/../lib \
-L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib \
-L/usr/lib/gcc/x86_64-linux-gnu/7/../../.. \
-lm ./hoge.o \
-lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed \
/usr/lib/gcc/x86_64-linux-gnu/7/crtendS.o \
/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crtn.o
-------------------------------------------------------------------
オフライン