西田亙氏の「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 をもう一度指定することでリンクは成功する。 #more|| ---- 下準備: #pre||> $ cat hello.c #include #define MESSAGE "Hello, World" int main() { printf("%s\n", MESSAGE); return 123; } $ gcc -c -o hello.o hello.c ; echo $? 0 $ file hello.o hello.o: ELF 32-bit LSB relocatable, Intel 80386, version 1 (SYSV), not stripped ||< リンク失敗: #pre||> $ ld \ > /usr/lib/crt1.o \ > /usr/lib/crti.o \ > hello.o \ > /usr/lib/libc.a \ > `gcc -print-libgcc-file-name` \ > /usr/lib/crtn.o /usr/lib/libc.a(ioputs.o): In function `puts': (.text+0x146): undefined reference to `_Unwind_Resume' /usr/lib/libc.a(ioputs.o):(.eh_frame+0xde): undefined reference to `__gcc_personality_v0' /usr/lib/libc.a(syslog.o): In function `openlog': (.text+0x332): undefined reference to `_Unwind_Resume' ... /usr/lib/libc.a(iogetdelim.o): In function `getdelim': (.text+0x26c): undefined reference to `_Unwind_Resume' /usr/lib/libc.a(iogetdelim.o):(.eh_frame+0xde): undefined reference to `__gcc_personality_v0' $ echo $? 1 ||< "_Unwind_Resume"というのと"__gcc_personality_v0"の2つのシンボルが見つからない。 試しに、普通にgccから"-static"オプションをつけて実行してみる。"-v"付で途中経過を表示させてみたところ: /usr/libexec/gcc/i386-redhat-linux/4.1.2/collect2 -m elf_i386 --hash-style=gnu -static -o hello_static /usr/lib/gcc/i386-redhat-linux/4.1.2/../../../crt1.o /usr/lib/gcc/i386-redhat-linux/4.1.2/../../../crti.o /usr/lib/gcc/i386-redhat-linux/4.1.2/crtbeginT.o -L/usr/lib/gcc/i386-redhat-linux/4.1.2 -L/usr/lib/gcc/i386-redhat-linux/4.1.2 -L/usr/lib/gcc/i386-redhat-linux/4.1.2/../../.. /tmp/ccxsa3ji.o --start-group -lgcc -lgcc_eh -lc --end-group /usr/lib/gcc/i386-redhat-linux/4.1.2/crtend.o /usr/lib/gcc/i386-redhat-linux/4.1.2/../../../crtn.o なんとなく --start-group -lgcc -lgcc_eh -lc --end-group というのが怪しそうな感じがする。とくに"-lgcc_eh"というのは何だろう。 というわけで、libgcc.aのあるディレクトリに潜ってみる。 $ gcc -print-libgcc-file-name /usr/lib/gcc/i386-redhat-linux/4.1.2/libgcc.a $ cd /usr/lib/gcc/i386-redhat-linux/4.1.2/ $ ls *.a libgcc.a libgcc_eh.a ... "libgcc_eh.a"のシンボルをgrepしてみる。 $ nm libgcc_eh.a | grep "_Unwind_Resume" nm: unwind-sjlj.o: no symbols 00002160 T _Unwind_Resume 00002060 T _Unwind_Resume_or_Rethrow $ nm libgcc_eh.a | grep "__gcc_personality_v0" nm: unwind-sjlj.o: no symbols 00000220 T __gcc_personality_v0 このように、libgcc_eh.aの中に格納されていた。 というわけで、libgcc_eh.aを追加してみる。 #pre||> $ ld \ > /usr/lib/crt1.o \ > /usr/lib/crti.o \ > hello.o \ > /usr/lib/libc.a \ > `gcc -print-libgcc-file-name` \ > `gcc -print-file-name=libgcc_eh.a` \ > /usr/lib/crtn.o /usr/lib/gcc/i386-redhat-linux/4.1.2/libgcc_eh.a(unwind-dw2.o): In function `.L125': (.text+0x958): undefined reference to `__stack_chk_fail_local' /usr/lib/gcc/i386-redhat-linux/4.1.2/libgcc_eh.a(unwind-dw2.o): In function `__frame_state_for': (.text+0xf26): undefined reference to `__stack_chk_fail_local' /usr/lib/gcc/i386-redhat-linux/4.1.2/libgcc_eh.a(unwind-dw2.o): In function `.L413': (.text+0x1896): undefined reference to `__stack_chk_fail_local' /usr/lib/gcc/i386-redhat-linux/4.1.2/libgcc_eh.a(unwind-dw2.o): In function `_Unwind_Backtrace': (.text+0x1ce1): undefined reference to `__stack_chk_fail_local' /usr/lib/gcc/i386-redhat-linux/4.1.2/libgcc_eh.a(unwind-dw2.o): In function `_Unwind_RaiseException': (.text+0x1f43): undefined reference to `__stack_chk_fail_local' /usr/lib/gcc/i386-redhat-linux/4.1.2/libgcc_eh.a(unwind-dw2.o):(.text+0x2155): more undefined references to `__stack_chk_fail_local' follow /usr/lib/gcc/i386-redhat-linux/4.1.2/libgcc_eh.a(unwind-dw2-fde-glibc.o): In function `_Unwind_Find_FDE': (.text+0x1508): undefined reference to `dl_iterate_phdr' $ echo $? 1 ||< まだ終了ステータスが1、失敗である。先ほどまでのシンボル未定義は解消されたようだが、新たに次の2つが未定義となってしまった。 __stack_chk_fail_local dl_iterate_phdr ところが、いろいろnmで漁っていると "libc.a" の方のTextセクションに定義されている。 $ nm /usr/lib/libc.a 2>/dev/null | grep __stack_chk_fail_local 00000000 T __stack_chk_fail_local $ nm /usr/lib/libc.a 2>/dev/null | grep dl_iterate_phdr 00000000 T __dl_iterate_phdr 000000f0 T dl_iterate_phdr ldは引数で指定された順番でオブジェクトファイルを結合していく・・・筈だったような気がする・・・ので、試しにlibgcc_eh.aの後ろにもう一度libc.aを置いてみる。 #pre||> $ ld \ > /usr/lib/crt1.o \ > /usr/lib/crti.o \ > hello.o \ > /usr/lib/libc.a \ > `gcc -print-libgcc-file-name` \ > `gcc -print-file-name=libgcc_eh.a` \ > /usr/lib/libc.a \ > /usr/lib/crtn.o $ echo $? 0 ||< 今度は上手く行ったようである。 $ ./a.out Hello, World $ file a.out a.out: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), \ for GNU/Linux 2.6.9, statically linked, for GNU/Linux 2.6.9, not stripped $ ldd ./a.out not a dynamic executable 今回は練習なので「リンク成功、動いてオッケー」で済ませたが、それにしてもlibgcc_eh.aファイルが何物か?など疑問が尽きない。 他にも今回の試行錯誤の過程で、実は "/usr/lib/libc_nonshared.a" というのをリンクさせようとして、いったんはそれで上手く行きそうに見えたのだけれど結局 "libc.a" を後ろにもう一度置くだけでOKになった。で、この"libc_nonshared.a"というのも分からない。 ここら辺を突っ込もうと思ったらinfoやgcc/binutilsのソースコードまで踏み込まないと駄目なんだろうなーと思うけど、それは後の楽しみに取っておく事にする・・・というか調べるだけの気力がまだ無いのでスルーしただけです。根性無し。