タイトル/名前 | 更新者 | 更新日 |
---|---|---|
日記/2009/12/05/gcc4.x:"error: static declaration of ‘xxxx’ follows non-static declaration" | msakamoto-sf | 2009-12-05 20:35:23 |
日記/2009/12/05/_exit(2)の日本語manページが・・・ | msakamoto-sf | 2009-12-05 14:09:29 |
技術/UNIX/BusyBox | msakamoto-sf | 2009-12-02 16:35:34 |
日記/2009/12/02/Linux-kernel、謎だらけだ・・・。 | msakamoto-sf | 2009-12-02 16:21:30 |
日記/2009/11/29/ld-linux.soってshared objectなのに何で実行できるんだろう? | msakamoto-sf | 2009-11-29 20:11:12 |
日記/2009/11/28/gccでstaticリンクさせようと手動でldコマンド動かしたメモ | msakamoto-sf | 2009-11-28 16:39:08 |
C言語系/memos/gcc | msakamoto-sf | 2009-11-28 13:28:12 |
技術/UNIX/GLOBAL(gtags,htags) | msakamoto-sf | 2009-11-25 14:04:42 |
日記/2009/11/24/「式」は「評価」され、「文」は「実行」される。 | msakamoto-sf | 2009-11-24 18:24:48 |
日記/2009/11/24/やってみたい事 | msakamoto-sf | 2009-11-24 12:53:01 |
今更Embedded UNIX vol.6の特集記事を参考に、linux kernel-2.6.0をビルドしようとするも・・・
... HOSTCC scripts/fixdep HOSTCC scripts/kconfig/conf.o HOSTCC scripts/kconfig/mconf.o /home/msakamoto/in_vitro/linux-2.6.0/scripts/kconfig/mconf.c:91: error: static declaration of ‘current_menu’ follows non-static declaration /home/msakamoto/in_vitro/linux-2.6.0/scripts/kconfig/lkc.h:63: error: previous declaration of ‘current_menu’ was here make[2]: *** [scripts/kconfig/mconf.o] エラー 1 make[1]: *** [allnoconfig] エラー 2 make: *** [allnoconfig] エラー 2
なんというか、設定ツールのビルドの時点でエラーになる。
暫くgoogleをさまよった結果、GCC-4.0になってから、グローバルシンボルと同じ名前のstaticシンボルを使うとエラーにするように「なった」っぽい。以前は最後に宣言されたのを尊重していたのだけれど、gcc-4.0になってエラーで止めるようになったとのこと。
例により苛立たされたのは、この情報が何故かAppleのDeveloperセンターのページに載っていて、肝心のGCC-4系列のChange情報に載っていなかった点。
Apple Developer センターでの、gcc 3.x系列から4.x系列へのポーティングの注意点:
肝心のGCC-4.0.x系列でのChange情報
ちなみにgcc-4.1, 4.2 系列のChange情報も確認したが、それらしき情報は載っていなかった。
Apple Developerセンターの人達はどこでこの情報GETしたのだろうか・・・。
http://www.linux.or.jp/JM/html/LDP_man-pages/man2/_exit.2.html
説明:
その際、このプロセスが所有しているディスクリプタ (descriptor) で、
オープンされているものは全てクローズされる。
注意:
一方で、 _exit() はオープンされているファイルディスクリプタをクローズしないため、
未決定になっている出力がフラッシュされるのを待つのに不確定な遅れが発生する。
クローズするのかしないのか、どっちなんだよ( ゚Д゚)ゴルァ!!
FreeBSD5.4の日本語manページを見ると・・・
呼び出し元プロセス内のすべての記述子が閉じられます。これには遅延が伴
うことがあります。たとえば、出力がすべて出されてしまうのを待機するこ
とです。この状態のプロセスは既に死にかけているので、新たに kill はで
きません。
やっぱりクローズするみたい。
組み込みLinuxで人気があるBusyBoxの極々簡単なメモ。
基本的には、本家サイトのFAQやREADME、あとソースコードに付属のINSTALLファイルを読めば特に問題ない。
"/proc"ファイルシステムの有無で若干環境変数(CONFIG_BUSYBOX_EXEC_PATH)の調整が入るみたい。
設定については"make menuconfig"でLinuxカーネル設定と同様な、ncursesを使ったテキストUIで調整出来る。
静的リンクにしたい場合は、
"Busybox Settings" -> "Build Options" -> "Build BusyBox as a static binary (no shared libs)"
にチェックをつける。なお、上記のメニュー階層やメニューラベルはバージョン1.14.4のものなので、将来やこれ以前のバージョンの場合は異なる可能性があるので注意。
なお、うっかり
make install
としちゃっても、デフォルトで"./_install"ディレクトリに構築されるので安心。
少なくとも、BusyBox 1.14以降であれば、以下のようにしてコンパイルツリーを別ディレクトリにできる。Linuxカーネル2.6系と一緒で"O"(アルファベット大文字の"オー")パラメータで出力ディレクトリを指定すればOK.
$ cd busybox-1.14.4/ $ make O=../busybox_build menuconfig ... $ cd ../busybox_build $ make
さらにインストール先を指定したい場合は CONFIG_PREFIX を指定すればOK.
$ make CONFIG_PREFIX=/mnt/loopback01 install
書籍リソース:
kernel-2.4を弄ってるんだけど・・・なんでmodutilsが無いとコンパイルすらできんのだ?そしてなぜそんな重要なパーツがkernelソースツリーに入ってないのだ?(だからkernel-2.6で取り込まれたのだろうけど)
modutilsだけじゃなくて、util-linuxも別立てなのは何故だ・・・。mountコマンドとか。
ばらばらで訳分からん・・・。
BusyBoxも便利だけど、util-linuxに入ってるコマンドまで取り込むことが出来ているのは何故だ?
あと、initプロセス起動するのは良いけど、なんでinitプロセスの中でfstab読んでrootファイルシステムをmountし直せるんだ?というのは、initプロセス・・・/sbin/initなり/etc/initなり認識出来たと言うことは、ファイルシステムを認識出来ていると言う事じゃないの?その時点で"/"ってmountできている訳ではないのか?だとすればinit中で"/"をmountし直せるものなの?
勉強不足なのは当然としても、謎だらけで一向に頭がすっきりしない。もう少し色々と本を読み込んで、どこかで一気につなぎ合わせたい・・・。
今更kernel-2.4を弄っているのは、Embedded UNIX vol.2 の「Linuxシステム縮小化計画」で扱っているのがkernel-2.4だから。で、試してみたのだけれど・・・loopbackデバイス上で構築したExt2ファイルシステムを2枚目のFDとして読み込ませ、「Ext2のrootファイルシステムがマウント出来たよ」+「使われていないkernelメモリを88kbほど解放したよ」という趣旨のメッセージがコンソールに流れた後、ストップする・・・。何も動かない。FDアクセスも発生していない。どうなってるんだ・・・?bochs上なのが何か悪さしてるのだろうか?一応skyfree.orgでの正誤表も確認し、inittabも修正してるんだけど・・・。
う~ん・・・Embedded UNIX vol.6 でkernel-2.6で最小構成を作る記事があるので、とりあえずvol.2で上手く行かなかったのは置いておいて、vol.6に進んでみよう。
Interface誌2002年7月号は・・・少し、諦めモード。CD-ROM縮刷版も含めて。gcc/glibc/kernelが揃ってバージョンが大きく上がっているので、参考になるか不明な上、多分次から次へとまた謎が出てきそうなので・・・。ちょい据え置き。
hello.oに対して、libc.aではなくてlibc.soの方をリンクさせようと、"-lc"にしてみる。
ld \ /usr/lib/crt1.o \ /usr/lib/crti.o \ hello.o \ `gcc -print-libgcc-file-name` \ `gcc -print-file-name=libgcc_eh.a` \ /usr/lib/crtn.o \ -lc
これで生成されたa.outを実行しようとすると、
$ ./a.out -bash: ./a.out: /usr/lib/libc.so.1: bad ELF interpreter: \ そのようなファイルやディレクトリはありません
何故かというと
$ readelf -l a.out Elf file type is EXEC (Executable file) Entry point 0x80482b0 There are 7 program headers, starting at offset 52 Program Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align PHDR 0x000034 0x08048034 0x08048034 0x000e0 0x000e0 R E 0x4 INTERP 0x000114 0x08048114 0x08048114 0x00013 0x00013 R 0x1 [Requesting program interpreter: /usr/lib/libc.so.1]
・・・ということで動的リンカの部分が"ld-linux.so"になってなかったから。
ここまでは良い。まぁ納得できる。でも、次が動くのは何で?
$ /lib/ld-linux.so.2 ./a.out Hello, World $ echo $? 123
"ld-linux.so.2"って実行可能なのか?だって、fileコマンドで見てみたら
$ file /lib/ld-linux.so.2 /lib/ld-linux.so.2: symbolic link to `ld-2.5.so' $ file /lib/ld-2.5.so /lib/ld-2.5.so: ELF 32-bit LSB shared object, \ Intel 80386, version 1 (SYSV), not stripped
ってなってる。共有オブジェクトのように見えるが、コマンドラインからも実行可能なのか?どういう構造になってるんだろう・・・。
謎が多いなあ・・・。
他にも謎なのが、"-fPIC"と"-shared"が分かれている件。"-shared"が共有オブジェクトのためのオプションなのは分かるが、なんで"-fPIC"で位置独立コードを生成するようわざわざ指定する必要があるんだろう?わざわざ分けているということは・・・
位置独立コードだけど実行可能なファイルとか、位置独立でないけど共有オブジェクトなファイルとかがあり得る、と言う事だろうか・・・。
→試しに"-v"で途中経過を見てみたら、"-fPIC"がアセンブラコードの生成時に "cc1" に渡されている。"-shared"の方は "collect2" の方に渡されている。
西田亙氏の「GNU DEVELOPMENT TOOLS」を読み返していて、第6章の静的リンクのところで躓いた。GCCやbinutilsのバージョンが大分変わっているのもあったと思う。
自分の環境:CentOS 5.2
$ rpm -q --qf "%{NAME},%{VERSION}\n" gcc binutils gcc,4.1.2 binutils,2.17.50.0.6
具体的には、hello.cをstaticリンクさせようとして手動でldコマンド実行したが、crt1.o, crti.o, hello.o, libc.a, libgcc.a, crtn.o だけだと未解決シンボルが出てリンクに失敗する。
結論を先に書くと、libgcc.aの後ろに libgcc_eh.a を置き、さらにその後ろに libc.a をもう一度指定することでリンクは成功する。
gccのオプションなど自分なりのmemo
#include <stdio.h> int main(int argc, char **argv) { printf("Hello, World!\n"); return 0; }
何も指定しない場合はa.outで出力する。
$ gcc hello.c $ ls a.out* hello.c $ ./a.out Hello, World!
出力ファイル名を指定したい場合は、 "-o" オプションを使う。
$ gcc -o hello hello.c $ ls hello* hello.c $ ./hello Hello, World!
オブジェクトファイル(*.o)までの生成にしておきたい場合は "-c" オプションを使う。
$ gcc -c hello.c $ ls hello.c hello.o
"-c" オプションと "-o" オプションの併用例:
$ gcc -c -o hello_obj.o hello.c $ ls hello.c hello_obj.o
$ gcc --save-temps -v hello.c Using built-in specs. Target: i386-redhat-linux (...) as -V -Qy -o hello.o hello.s GNU assembler version 2.17.50.0.6-6.el5 (i386-redhat-linux) using BFD version 2.17.50.0.6-6.el5 20061020 /usr/libexec/gcc/i386-redhat-linux/4.1.2/collect2 (...) $ ls a.out hello.c hello.i hello.o hello.s $ ./a.out Hello, World!
gccに "-E" or "-S" を指定する事で、成果物の段階を指定できる。
"-E"オプションを指定すると、プリプロセッサまでが実行される。"-o"をつけないと標準出力に出力される。
$ gcc -E hello.c # 1 "hello.c" (...) # 2 "hello.c" 2 int main(int argc, char **argv) { printf("Hello, World!\n"); return 0; } $ ls hello.c
"-o"オプションを指定してみる。
$ gcc -E -o hello.i hello.c $ ls hello.c hello.i (プリプロセスの出力ファイルからコンパイルを継続することも出来る) $ gcc hello.i $ ls a.out* hello.c hello.i $ ./a.out Hello, World!
"-v"をつけてみると、cppパッケージの"cc1"コマンドの実行まで処理している事が確認できる。
$ gcc -v -E -o hello.i hello.c Using built-in specs. Target: i386-redhat-linux コンフィグオプション: ../configure --prefix=/usr (...) スレッドモデル: posix gcc バージョン 4.1.2 20071124 (Red Hat 4.1.2-42) /usr/libexec/gcc/i386-redhat-linux/4.1.2/cc1 \ -E -quiet -v hello.c -o hello.i -mtune=generic 存在しないディレクトリ \ "/usr/lib/gcc/i386-redhat-linux/4.1.2/../../../../i386-redhat-linux/include" \ を無視します #include "..." の探索はここから始まります: #include <...> の探索はここから始まります: /usr/local/include /usr/lib/gcc/i386-redhat-linux/4.1.2/include /usr/include 探索リストの終わり $
※CentOS 5.2 の場合、cppコマンドは "cpp" というRPMパッケージに"cc1"と併せて収録されている。では"cpp"パッケージは"gcc"とは別物か?となるが、RPMパッケージ情報を調べると下記の通り Source RPMは"gcc"になっている。RPMパッケージングの際に、バージョニングの都合などで分離されたものと思われる(cc1まで入れてしまったら分離する意味が無いような気もするが・・・)。
$ rpm -qi cpp Name : cpp Relocations: (not relocatable) Version : 4.1.2 Vendor: CentOS Release : 42.el5 Build Date: 2008年05月27日 07時17分35秒 Install Date: 2008年08月02日 13時40分43秒 Build Host: builder16.centos.org Group : Development/Languages Source RPM: gcc-4.1.2-42.el5.src.rpm ^^^^^^^^^^^^^^^^^^^^^^^^^
"-S"オプションを指定すると、アセンブラまでが実行される。
$ ls hello.c hello.i $ gcc -v -S hello.i Using built-in specs. Target: i386-redhat-linux コンフィグオプション: ../configure --prefix=/usr (...) スレッドモデル: posix gcc バージョン 4.1.2 20071124 (Red Hat 4.1.2-42) /usr/libexec/gcc/i386-redhat-linux/4.1.2/cc1 -fpreprocessed hello.i -quiet \ -dumpbase hello.i -mtune=generic -auxbase hello -version -o hello.s GNU C version 4.1.2 20071124 (Red Hat 4.1.2-42) (i386-redhat-linux) compiled by GNU C version 4.1.2 20071124 (Red Hat 4.1.2-42). GGC heuristics: --param ggc-min-expand=64 --param ggc-min-heapsize=64436 Compiler executable checksum: 6b8fcfb678815bd99e272234accc87cd $ ls hello.c hello.i hello.s
この後、"-c"オプションでオブジェクトファイルの生成までを行える。
$ gcc -v -c hello.s Using built-in specs. Target: i386-redhat-linux コンフィグオプション: ../configure --prefix=/usr (...) スレッドモデル: posix gcc バージョン 4.1.2 20071124 (Red Hat 4.1.2-42) as -V -Qy -o hello.o hello.s GNU assembler version 2.17.50.0.6-6.el5 (i386-redhat-linux) using BFD version 2.17.50.0.6-6.el5 20061020 $ ls hello.c hello.i hello.o hello.s
最後にオブジェクトファイルを入力として、実行形式のファイルを作成する。
$ gcc -o hello hello.o Using built-in specs. Target: i386-redhat-linux コンフィグオプション: ../configure --prefix=/usr (...) スレッドモデル: posix gcc バージョン 4.1.2 20071124 (Red Hat 4.1.2-42) /usr/libexec/gcc/i386-redhat-linux/4.1.2/collect2 (...) $ ls hello* hello.c hello.i hello.o hello.s $ ./hello Hello, World!
"#include"パスの追加指定は"-I"オプションを使う。
gcc (...) -I/usr/include/xxxyyy -I/opt/include/zzzz (...)
コンパイル時の"*.so"のディレクトリ指定は"-L"オプションを使う。
gcc (...) -L/usr/lib/xxxyyyy (...)
"libxyz(.<major>.<minor>.<minor2>).so"を指定したい場合は、"-l"オプションで"xyz"の部分だけを指定する。
gcc (...) -lxyz (...)
"-I", "-L", "-l" 共に、値との間に空白は挟まない。
NG: gcc (...) -I /usr/include/xxyyzz (...) ^
静的・動的のそれぞれの作成とリンクのサンプルを載せる。(なお、以降出てくるgccのオプションで"-Wall"が付いていないのはサンプルだから、ということでご了承願います)
ソースコード構成:
$ ls calc/ call_calc.c $ ls calc/ calc.h calc1.c calc2.c
call_calc.c:
#include <stdio.h> #include "calc.h" int main(int argc, char **argv) { int x = 1; int y = 2; int z1 = calc1(x, y); int z2 = calc2(x, y); printf("calc1(%d, %d) = %d\n", x, y, z1); printf("calc2(%d, %d) = %d\n", x, y, z2); return 0; }
calc/calc.h:
int calc1(int a, int b); int calc2(int a, int b);
calc/calc1.c: (足し算)
int calc1(int a, int b) { return a + b; }
calc/calc1.c: (掛け算)
int calc2(int a, int b) { return a * b; }
calc1, calc2を含んだライブラリを静的・動的それぞれのパターンで作成し、call_calc.cとリンクさせてみる。
binutilsのarコマンドでlibcalc.aを生成する。"-r"で格納するアーカイブファイル名を指定する。"-s"オプションで、リンクを早くする為に予め索引を作っておく。作成された索引は "nm -s" で確認できる。
$ cd ./calc $ ls calc.h calc1.c calc2.c $ gcc -c -o calc1_nofpic.o calc1.c $ gcc -c -o calc2_nofpic.o calc2.c $ ar -s -r libcalc.a calc1_nofpic.o calc2_nofpic.o ar: creating libcalc.a $ ar t libcalc.a calc1_nofpic.o calc2_nofpic.o [msakamoto@akane calc]$ nm -s libcalc.a Archive index: calc1 in calc1_nofpic.o calc2 in calc2_nofpic.o calc1_nofpic.o: 00000000 T calc1 calc2_nofpic.o: 00000000 T calc2 $ ls calc.h calc1.c calc1_nofpic.o calc2.c calc2_nofpic.o libcalc.a
続いてcall_calc.cをコンパイル・リンクする。"-I"でcalc.hのあるディレクトリを指定する。リンク時には、libcalc.aを後ろに指定する。
$ cd ../ $ ls calc call_calc.c $ gcc -Wall -I./calc -c -o call_calc.o call_calc.c $ ls calc call_calc.c call_calc.o $ gcc -v -o call_calc_static call_calc.o ./calc/libcalc.a Using built-in specs. Target: i386-redhat-linux コンフィグオプション: ../configure (...) スレッドモデル: posix gcc バージョン 4.1.2 20071124 (Red Hat 4.1.2-42) /usr/libexec/gcc/i386-redhat-linux/4.1.2/collect2 \ (...) -o call_calc_static (...) call_calc.o ./calc/libcalc.a (...) $ ls calc call_calc.c call_calc.o call_calc_static* $ ./call_calc_static calc1(1, 2) = 3 calc2(1, 2) = 2
calc1.c, calc2.cを "-fPIC" 付で位置独立コードとしてコンパイルする。
$ cd ./calc $ ls calc.h calc1.c calc1_nofpic.o calc2.c calc2_nofpic.o libcalc.a $ gcc -fPIC -c -o calc1_pic.o calc1.c $ gcc -fPIC -c -o calc2_pic.o calc2.c $ gcc -shared -o libcalc.so calc1_pic.o calc2_pic.o $ ls calc.h calc1.c calc1_nofpic.o calc1_pic.o calc2.c calc2_nofpic.o calc2_pic.o libcalc.a libcalc.so*
※今回のサンプルでは"-Wl,-soname=xxxx"は使わない。後日このオプションについて検証する。
続いてcall_calc.cをコンパイル・リンクする。"-I"でcalc.hのあるディレクトリを指定する。リンク時には、"-L"でlibcalc.soの存在するディレクトリを指定し、"-l"でライブラリ名("calc")を指定する。今回はシステムのライブラリディレクトリにはインストールしないので、一時的にLD_LIBRARY_PATHでlibcalc.soがあるディレクトリを指定し、実行する。
$ cd ../ $ ls calc call_calc.c call_calc.o call_calc_static* $ gcc -v -L./calc -lcalc -o call_calc_so call_calc.o Using built-in specs. Target: i386-redhat-linux コンフィグオプション: ../configure (...) スレッドモデル: posix gcc バージョン 4.1.2 20071124 (Red Hat 4.1.2-42) /usr/libexec/gcc/i386-redhat-linux/4.1.2/collect2 (...) \ -o call_calc_so (...) -L./calc (...) -lcalc call_calc.o (...) $ export LD_LIBRARY_PATH=./calc; ./call_calc_so calc1(1, 2) = 3 calc2(1, 2) = 2
gcc-4.1.2 のmanページから抜粋:
-Wa,option option をアセンブラに対するオプションとして渡します。 option がコンマを含む場合は、そのコンマで区切られた複数のオプションとして与えられます。 -Wl,option オプション option をリンカに渡します。 option がコンマを含む場合は、それらのコンマで複数のオプションとして分割されます。
他、書籍・雑誌記事・Webなどから拾い読みしたTIPSなど。
gccオプション:
-print-prog-name=XXXXX : gccがkickする外部コマンドの絶対パス表示 -Wall : 全ての警告を表示 -Werror : 警告をエラーと見なす
cppオプション:
-H : indent形式でインクルードの状況を表示 -M : Makefile形式で外部依存ファイルリストを表示 -dM : 全てのマクロ定義文表示。空のファイルを読ませれば、ビルトイン定義を表示してくれる。 -nostdinc : ヘッダーファイル検索パスを初期化 -v : 詳細表示
ソースコードタグシステムの GNU GLOBAL メモ。日本人が作ったGNUツール。
ソースコードを解析するのに便利。特に、htagsコマンドでHTML化してくれる機能が特徴的。
結構有名で、ブログとかでもまとめてる人がいるので適当に検索すれば大体分かる。
まずTAGデータを作成するには、ソースコードツリーのTOPにcdして、
gtags
すればOK。カレントディレクトリ=ソースコードツリーのTOPに、GTAGSを初めとするTAGデータファイルが作られる。
TAGデータファイルを別のディレクトリに作成したい場合は(例:CDROM中のソースコードを解析したい)、
gtags 別ディレクトリ
とすればOK.
あと、gtags.confを"/etc/gtags.conf" or "$HOME/.globalrc"に置いておくと(アーカイブなりパッケージなりに入ってるので適当にコピー)、色々設定を変更できる。
GLOBAL自体はC/C++/Java/PHP4/yaccに対応しているのだけれど、例えばvimにくっついてくるctagsとかを使うともっと対応種別が増やせるのでそっちを使いたい、という場合に、設定ファイルを編集する。
↑の「パーサを変更する。」が参考になるかも。あと本家ドキュメント参照。
HTMLを生成させたい場合は、GTAGSとGRTAGSのあるディレクトリにcdして
htags
コマンドを実行すれば基本的にOK. "HTML"というディレクトリがカレントディレクトリに作成されて、その中に沢山HTMLファイルが出来る。
GTAGSのあるディレクトリを指定したい時とか、HTMLを作成したいディレクトリを指定する場合は
htags -d GTAGS,GRTAGSディレクトリ HTML作成ディレクトリ
とする。
オススメオプションを自動適用したい場合は "--sugest" が良いよ、とあるけど個人的には以下の組み合わせで充分。
htags -anosT --tabs 4
こうすると、関数の前後のナビゲーションリンクであるとか、検索CGI用のフォームとかが消えるので、スッキリシンプルなHTMLになる。"--sugest"だとそういうのも付いてしまうので、HTMLがごてごてしてしまう。
他、参考URL:
スタイルを弄りたい場合などは "-x" or "--xhtml" つけた方が良いかも。MSYSでコンパイルされたWindowsバイナリだと、"(...)/style.cssが見つからないよ~"と怒られるかも知れないが、アーカイブ中のstyle.cssを適当に探して、見つかったらHTMLの下に放り込めばOK.
なるほど、だからcrowbarでは
"Expression(式)" → eval.c(評価:evaluate)
"Statement(文)" → execute.c(実行:execute)
という対応になっているのか。
出来るかどうかは別として、今現在「やってみたいな~」と前向きにさせてくれるTODO。
というのを書いてみると、少しは気分が前向きになってくる。・・・が、一向に金儲けと結びつく気配がない。