home ホーム search 検索 -  login ログイン  | reload edit datainfo version cmd icon diff delete  | help ヘルプ

技術/Linux/uClibc/04, 静的リンク -> Segmentation Fault について (v1)

技術/Linux/uClibc/04, 静的リンク -> Segmentation Fault について (v1)

技術 / Linux / uClibc / 04, 静的リンク -> Segmentation Fault について (v1)
id: 950 所有者: msakamoto-sf    作成日: 2011-04-11 18:56:27
カテゴリ: Linux 

技術/Linux/uClibc/03, x86(32bit) PC環境でuClibcを試す でも注記していますが、x86(32bit)PC環境でBuildrootを使わずに構築した uClibc を静的リンクしたバイナリの場合、Segmentation Fault で異常終了してしまう現象が確認されています。
自分の環境では、 DODEBUG オプションを有効化してuClibcおよびリンク先のアプリケーションをリビルドするとこの問題を回避できました。

原因までは特定できていませんが、gdbでデバッグして気になった箇所をメモしておきます。
環境は 技術/Linux/uClibc/03, x86(32bit) PC環境でuClibcを試す と同一です。

Segmentation Fault するバイナリのデバッグ

$ gdb ./ret1_static
GNU gdb (GDB) Red Hat Enterprise Linux (7.0.1-23.el5_5.2)
Copyright (C) 2009 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i386-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /home/msakamoto/reduced.linux/uclibc_test2/ret1_static...(no debugging symbols found)...done.
(gdb) run
Starting program: /home/msakamoto/reduced.linux/uclibc_test2/ret1_static

Program received signal SIGSEGV, Segmentation fault.
0x00000000 in ?? ()
(gdb) bt
#0  0x00000000 in ?? ()
#1  0x0804819e in __uClibc_init ()
#2  0x0804817f in __uClibc_init ()
#3  0x0804823f in __uClibc_main ()
#4  0x00000000 in ?? ()

__uClibc_init() というかなり初期段階の関数中で、EIPが0番地をさしてSIGSEGVしたことが分かります。

__uClibc_init()の逆アセンブラ結果、スタック、関連箇所の逆アセンブラ結果を添えておきます:

(gdb) disas 0x0804819e
Dump of assembler code for function __uClibc_init:
0x08048179 <__uClibc_init+0>:   push   %ebx
0x0804817a <__uClibc_init+1>:   call   0x8048362 <__i686.get_pc_thunk.bx>
0x0804817f <__uClibc_init+6>:   add    $0x1729,%ebx
0x08048185 <__uClibc_init+12>:  sub    $0x8,%esp
0x08048188 <__uClibc_init+15>:  mov    -0x8(%ebx),%eax
0x0804818e <__uClibc_init+21>:  cmpl   $0x0,(%eax)
0x08048191 <__uClibc_init+24>:  jne    0x804819e <__uClibc_init+37>
0x08048193 <__uClibc_init+26>:  movl   $0x1000,(%eax)
0x08048199 <__uClibc_init+32>:  call   0x0    ## <<<<< ここで落ちてるっぽい。
0x0804819e <__uClibc_init+37>:  pop    %eax
0x0804819f <__uClibc_init+38>:  pop    %edx
0x080481a0 <__uClibc_init+39>:  pop    %ebx
0x080481a1 <__uClibc_init+40>:  ret
End of assembler dump.
(gdb) x/16x $esp
0xbfffe9bc:     0x0804819e      0x00000000      0x0804817f      0x080498a8
0xbfffe9cc:     0x0804823f      0x00000000      0x00000000      0x00000000
0xbfffe9dc:     0x00000000      0x00000000      0x00000000      0x00000000
0xbfffe9ec:     0x00000000      0x00000000      0x00000000      0x00000000
(gdb) disas 0x8048362
Dump of assembler code for function __i686.get_pc_thunk.bx:
0x08048362 <__i686.get_pc_thunk.bx+0>:  mov    (%esp),%ebx
0x08048365 <__i686.get_pc_thunk.bx+3>:  ret
0x08048366 <__i686.get_pc_thunk.bx+4>:  nop
0x08048367 <__i686.get_pc_thunk.bx+5>:  nop
End of assembler dump.

DODEBUGを有効化して静的リンクしたバイナリの "__uClibc_init()"

こちらは正常に動作したバイナリ(=自分の環境ではDODEBUGを有効化してリビルド)の __uClibc_init() です:

(gdb) disas
Dump of assembler code for function __uClibc_init:
0x08048195 <__uClibc_init+0>:   push   %ebp
0x08048196 <__uClibc_init+1>:   mov    %esp,%ebp
0x08048198 <__uClibc_init+3>:   push   %ebx
0x08048199 <__uClibc_init+4>:   sub    $0x4,%esp
0x0804819c <__uClibc_init+7>:   call   0x80489e6 <__i686.get_pc_thunk.bx>
0x080481a1 <__uClibc_init+12>:  add    $0x26cb,%ebx
0x080481a7 <__uClibc_init+18>:  mov    -0x8(%ebx),%eax
0x080481ad <__uClibc_init+24>:  mov    (%eax),%eax
0x080481af <__uClibc_init+26>:  test   %eax,%eax
0x080481b1 <__uClibc_init+28>:  jne    0x80481ce <__uClibc_init+57>
0x080481b3 <__uClibc_init+30>:  mov    -0x8(%ebx),%eax
0x080481b9 <__uClibc_init+36>:  movl   $0x1000,(%eax)
0x080481bf <__uClibc_init+42>:  lea    -0x804a86c(%ebx),%eax
0x080481c5 <__uClibc_init+48>:  test   %eax,%eax
0x080481c7 <__uClibc_init+50>:  je     0x80481ce <__uClibc_init+57>
0x080481c9 <__uClibc_init+52>:  call   0x0
0x080481ce <__uClibc_init+57>:  add    $0x4,%esp
0x080481d1 <__uClibc_init+60>:  pop    %ebx
0x080481d2 <__uClibc_init+61>:  pop    %ebp
0x080481d3 <__uClibc_init+62>:  ret
End of assembler dump.

"__uClibc_init()" のソースコード

"defconfig" 時にundefとなるブロックを除去したコードです。

libc/misc/internals/__uClibc_main.c:

extern void weak_function _stdio_init(void) attribute_hidden;

...

extern void __uClibc_init(void);
libc_hidden_proto(__uClibc_init)
void __uClibc_init(void)
{
    /* Don't recurse */
    if (__pagesize)
        return;

    /* Setup an initial value.  This may not be perfect, but is
     * better than  malloc using __pagesize=0 for atexit, ctors, etc.  */
    __pagesize = PAGE_SIZE;

    /*
     * Initialize stdio here.  In the static library case, this will
     * be bypassed if not needed because of the weak alias above.
     * Thus we get a nice size savings because the stdio functions
     * won't be pulled into the final static binary unless used.
     */
    if (likely(_stdio_init != NULL))
        _stdio_init();

}
libc_hidden_def(__uClibc_init)

DODEBUG有効化したバイナリの逆アセンブラ結果と見比べてみると、Cソースとアセンブラがほぼ1対1に対応付けられると思います。

"DODEBUG"の影響

"DODEBUG"でgrepしてみると、Rules.makファイル内でコンパイルオプションを調整していることが分かります。
Rules.mak :

...
ifeq ($(DODEBUG),y)
CFLAGS += -O0 -g3
else
CFLAGS += $(OPTIMIZATION)
endif

"DODEBUG"を有効にすると最適化を無効にし(-O0)、最大限のデバッグ情報を埋め込むよう(-g3)指示しています。
逆に "DODEBUG"が無効の場合、可能な限りの最適化オプションを盛り込むようになります(OPTIMIZATIONでgrepしてみて下さい)。

サイズの違いを見てみます。

DODEBUG=y
#DOSTRIP is not set


$ wc -c dev/usr/lib/libc.a
43388656 dev/usr/lib/libc.a
$ wc -c dev/usr/lib/uclibc_nonshared.a
60030 dev/usr/lib/uclibc_nonshared.a

"libc.a"が実に40MBを超えるサイズになってしまっています。これがまるごと静的リンクされるわけではなく、libc.a内から必要に応じてオブジェクトファイルが抽出されてリンクされるわけですが、例えばhelloworldでも

$ wc -c helloworld_static
3376190 helloworld_static

と3MBを超えてしまいます。

DODEBUGを無効化した場合:

# DODEBUG is not set
# DOSTRIP is not set


$ wc -c dev/usr/lib/libc.a
1078172 dev/usr/lib/libc.a
$ wc -c dev/usr/lib/uclibc_nonshared.a
1330 dev/usr/lib/uclibc_nonshared.a

stripしてませんので1MBを超えてしまいますが、大分小さくなりました。
とはいえ、自分の環境ですとこれで静的リンクするとSegmentation Faultしてしまい使い物になりません・・・( ;∀;)。

途中経過報告

まだ原因を特定出来ていないので「途中」の経過報告となります。
現段階で考えられる原因は2つあります。

1.最適化オプションの影響。

DODEBUGを無効化した場合、デバッグ情報を埋め込まないだけでなく、可能な限りの最適化オプションを盛りこんでコンパイルされます。この影響で _uClibc_init() の流れがおかしくなっているのかもしれません。

2.リンクするオブジェクトやその順番が間違っている。

DODEBUGを有効にした場合も動作するのは「たまたま」上手く動いただけで、そもそもリンクするcrtX.oやlibc.a、およびその順番が間違っている可能性もあります。たまたまリンクは上手く出来たが、weakシンボル絡みで実は裏側では・・・というケースが考えられます。

今のところ頭に浮かんでいる原因はこの2つです。
ただ現実問題として、コンパクトなLinuxシステムの構築でuClibcを使うとき、静的リンクを使う状況はあまり無いと思います。大抵はファイルシステムの容量がシビアに制限された中での開発となりますので、uClibcを導入するとなれば最終的には動的リンクを使うことになると思います。
したがって、静的リンクでSegmentation Faultしてしまう問題は現実的には無視してしまっても良いのかもしれません。



プレーンテキスト形式でダウンロード
現在のバージョン : 1
更新者: msakamoto-sf
更新日: 2011-04-11 18:58:06
md5:f9735b1335705d0ee7103ef48c91b110
sha1:ab5471a585a9d7dc202a28f3abf64c1d09686ea6
コメント
コメントを投稿するにはログインして下さい。