[#28230] bcc32 memory manager — "H.Yamamoto" <ocean@...2.ccsnet.ne.jp>

山本です。

15 messages 2006/01/18

[#28243] FUNCTION_CALL_MAY_RETURN_TWICE — Hajimu UMEMOTO <ume@...>

梅本です。

18 messages 2006/01/20

[#28270] Re: [PATCH] solaris 10 isinf and ruby_setenv fixes — "H.Yamamoto" <ocean@...2.ccsnet.ne.jp>

山本です。

17 messages 2006/01/23
[#28271] Re: [PATCH] solaris 10 isinf and ruby_setenv fixes — "H.Yamamoto" <ocean@...2.ccsnet.ne.jp> 2006/01/23

山本です。

[#28272] Re: [PATCH] solaris 10 isinf and ruby_setenv fixes — WATANABE Hirofumi <eban@...> 2006/01/23

わたなべです。

[#28273] Re: [PATCH] solaris 10 isinf and ruby_setenv fixes — "H.Yamamoto" <ocean@...2.ccsnet.ne.jp> 2006/01/23

山本です。

[#28274] Re: [PATCH] solaris 10 isinf and ruby_setenv fixes — "H.Yamamoto" <ocean@...2.ccsnet.ne.jp> 2006/01/24

山本です。

[#28275] Re: [PATCH] solaris 10 isinf and ruby_setenv fixes — "U.Nakamura" <usa@...> 2006/01/24

こんにちは、なかむら(う)です。

[#28286] SEGV with zlib — Tanaka Akira <akr@...17n.org>

最近、Data オブジェクトの free 関数が気になっているのですが、

24 messages 2006/01/30
[#28303] Re: SEGV with zlib — "H.Yamamoto" <ocean@...2.ccsnet.ne.jp> 2006/02/06

山本です。

[#28304] Re: SEGV with zlib — Yukihiro Matsumoto <matz@...> 2006/02/06

まつもと ゆきひろです

[#28305] Re: SEGV with zlib — "H.Yamamoto" <ocean@...2.ccsnet.ne.jp> 2006/02/06

山本です。

[#28306] Re: SEGV with zlib — "H.Yamamoto" <ocean@...2.ccsnet.ne.jp> 2006/02/06

山本です。

[#28307] Re: SEGV with zlib — Tietew <tietew-ml-ruby-dev@...> 2006/02/06

[#28308] Re: SEGV with zlib — Yukihiro Matsumoto <matz@...> 2006/02/06

まつもとゆきひろです。

[ruby-dev:28263] Re: FUNCTION_CALL_MAY_RETURN_TWICE

From: Tanaka Akira <akr@...17n.org>
Date: 2006-01-21 18:52:22 UTC
List: ruby-dev #28263
In article <yge8xt9y17d.wl%ume@mahoroba.org>,
  Hajimu UMEMOTO <ume@mahoroba.org> writes:

>   --enable-pthread なんですね。

はい。FUNCTION_CALL_MAY_RETURN_TWICE が使われるのは
主に --enable-pthread のときです。
(例外としては 1.8 かつ IA64 の場合があります)

>   さっきダメだったのは --enable-pthread でしたが、試しに指定せずにやっ
> てみたら、無事作れて、make test も成功しました。
>   FreeBSD の pthread 回りの秘孔を突いた?

AMD でなくても FreeBSD 5.4 で -mpentium4 としたら再現したので追いかけました。

コンパイルが compiling Win32API で止まるというところからテス
トケースを作ると次のようになります。

% cat z.rb
begin
  raise StandardError
rescue StandardError
  p $!
end
p :end

これは次のように #<StandardError: StandardError> と :end の
2行が表示されるのが適切な動作です。

% ruby z.rb      
#<StandardError: StandardError>
:end

しかし、FreeBSD 5.4 で --enable-pthread とすると :end とだけ
表示して終わります。

% ./miniruby z.rb
:end

getcontext の直後と setcontext の直前で報告させるようにする
と、奇妙な挙動が観察されます。

Index: eval.c
===================================================================
RCS file: /src/ruby/eval.c,v
retrieving revision 1.871
diff -u -p -r1.871 eval.c
--- eval.c	18 Jan 2006 15:00:58 -0000	1.871
+++ eval.c	21 Jan 2006 17:51:23 -0000
@@ -94,6 +94,7 @@ rb_jump_context(env, val)
     int val;
 {
     env->status = val;
+    fprintf(stderr, "before setcontext: %p %d\n", &env->context, env->status), \
     setcontext(&env->context);
     abort();			/* ensure noreturn */
 }
@@ -175,6 +176,7 @@ int function_call_may_return_twice_false
     (just_before_setjmp), \
     FUNCTION_CALL_MAY_RETURN_TWICE, \
     getcontext(&(j)->context), \
+    fprintf(stderr, " after getcontext: %p %d\n", &(j)->context, (j)->status), \
     FUNCTION_CALL_MAY_RETURN_TWICE, \
     (j)->status)
 #else

% ./miniruby z.rb
 after getcontext: 0xbfbfe7d0 0
 after getcontext: 0xbfbfe7e0 0
 after getcontext: 0xbfbfe7c0 0
 after getcontext: 0xbfbfd7f0 0
before setcontext: 0xbfbfd7f0 6
:end
 after getcontext: 0xbfbfe7a0 0
 after getcontext: 0xbfbfe450 0

ひとつだけある setcontext が raise による大域脱出に対応しま
すが、その setcontext とその直後の getcontext の引数の
ucontext_t * と status の値が一致していません。

| % gdb miniruby
| GNU gdb 6.1.1 [FreeBSD]
| Copyright 2004 Free Software Foundation, Inc.
| GDB is free software, covered by the GNU General Public License, and you are
| welcome to change it and/or distribute copies of it under certain conditions.
| Type "show copying" to see the conditions.
| There is absolutely no warranty for GDB.  Type "show warranty" for details.
| This GDB was configured as "i386-marcel-freebsd"...

まず gdb 上で、再現することを確認します。

| (gdb) run z.rb 
| Starting program: /pub/akr/ruby/19/ruby/miniruby z.rb
|  after getcontext: 0xbfbfe7a0 0
|  after getcontext: 0xbfbfe7b0 0
|  after getcontext: 0xbfbfe790 0
|  after getcontext: 0xbfbfd7c0 0
| before setcontext: 0xbfbfd7c0 6
| :end
|  after getcontext: 0xbfbfe770 0
|  after getcontext: 0xbfbfe420 0
| 
| Program exited normally.

よくわからないんですが、libc の symbol を読んでくれなくて
getcontext, setcontext に breakpoint を設定できなかったので
明示的に読ませます。

| (gdb) break main
| Breakpoint 1 at 0x8054eb2: file main.c, line 40.
| (gdb) run z.rb
| Starting program: /pub/akr/ruby/19/ruby/miniruby z.rb
| 
| Breakpoint 1, main (argc=0, argv=0x0, envp=0xbfbfeb2c) at main.c:40
| 40              RUBY_INIT_STACK
| (gdb) info  sharedlibrary 
| From        To          Syms Read   Shared Object Library
| 0x28142ec0  0x2815b418  No          /usr/lib/libpthread.so.1
| 0x28162e88  0x28166cc8  No          /lib/libcrypt.so.2
| 0x2817cb00  0x2818cc40  No          /lib/libm.so.3
| 0x281b2780  0x2824b870  No          /lib/libc.so.5
| 0x281159d8  0x2812d0a9  No          /libexec/ld-elf.so.1
| (gdb) sharedlibrary libc.so
| Reading symbols from /lib/libc.so.5...done.
| Loaded symbols for /lib/libc.so.5

getcontext, setcontext に break point を設定します。

| (gdb) break getcontext
| Breakpoint 2 at 0x281ba224
| (gdb) break setcontext
| Breakpoint 3 at 0x281df7f4

4回目の getcontext が setcontext で使われるものなのでそこま
で進めます。

| (gdb) c
| Continuing.
| 
| Breakpoint 2, 0x281ba224 in getcontext () from /lib/libc.so.5
| (gdb) 
| Continuing.
|  after getcontext: 0xbfbfe7a0 0
| 
| Breakpoint 2, 0x281ba224 in getcontext () from /lib/libc.so.5
| (gdb) 
| Continuing.
|  after getcontext: 0xbfbfe7b0 0
| 
| Breakpoint 2, 0x281ba224 in getcontext () from /lib/libc.so.5
| (gdb) 
| Continuing.
|  after getcontext: 0xbfbfe790 0
| 
| Breakpoint 2, 0x281ba224 in getcontext () from /lib/libc.so.5

問題の getcontext は NODE_RESCUE の EXEC_TAG から呼ばれてい
ます。

| (gdb) up
| #1  0x0805ff7d in rb_eval (self=135678472, n=0xbfbfd7c0) at eval.c:3107
| 3107                if ((state = EXEC_TAG()) == 0) {
| (gdb) l
| 3102            {
| 3103                volatile VALUE e_info = ruby_errinfo;
| 3104                volatile int rescuing = 0;
| 3105
| 3106                PUSH_TAG(PROT_NONE);
| 3107                if ((state = EXEC_TAG()) == 0) {
| 3108                  retry_entry:
| 3109                    result = rb_eval(self, node->nd_head);
| 3110                }
| 3111                else if (rescuing) {
| (gdb) down
| #0  0x281ba224 in getcontext () from /lib/libc.so.5

getcontext の中身を調べます。

ソースは http://www.FreeBSD.org/cgi/cvsweb.cgi/src/lib/libc/i386/sys/getcontext.S?rev=1.1&content-type=text/x-cvsweb-markup
ですが、getcontext の大半はカーネルで実装されていますが、
return address を ECX に入れておいて、setcontext から帰って
きた時にはすでに存在しない getcontext の stack frame にアク
セスせずに return するというところがユーザレベルで実装されて
います。

| (gdb) disassemble 
| Dump of assembler code for function getcontext:
| 0x281ba224 <getcontext+0>:      mov    (%esp),%ecx
| 0x281ba227 <getcontext+3>:      mov    $0x1a5,%eax
| 0x281ba22c <getcontext+8>:      int    $0x80
| 0x281ba22e <getcontext+10>:     jb     0x281ba235 <getcontext+17>
| 0x281ba230 <getcontext+12>:     add    $0x4,%esp
| 0x281ba233 <getcontext+15>:     jmp    *%ecx
| 0x281ba235 <getcontext+17>:     push   %ebx
| 0x281ba236 <getcontext+18>:     call   0x281ba23b <getcontext+23>
| 0x281ba23b <getcontext+23>:     pop    %ebx
| 0x281ba23c <getcontext+24>:     add    $0x9f8d1,%ebx
| 0x281ba242 <getcontext+30>:     jmp    0x281afe5c <_init+4284>
| 0x281ba247 <getcontext+35>:     nop    
| End of assembler dump.

命令ごとにステップ実行していきます。

| (gdb) display/i $pc
| 1: x/i $pc  0x281ba224 <getcontext>:    mov    (%esp),%ecx
| (gdb) si
| 0x281ba227 in getcontext () from /lib/libc.so.5
| 1: x/i $pc  0x281ba227 <getcontext+3>:  mov    $0x1a5,%eax
| (gdb) 
| 0x281ba22c in getcontext () from /lib/libc.so.5
| 1: x/i $pc  0x281ba22c <getcontext+8>:  int    $0x80

システムコールを行う前に、ここの getcontext での保存先の
prot_tag と register の内容を調べます。

| (gdb) p prot_tag
| $1 = (struct tag *) 0xbfbfd7c0
| (gdb) p *prot_tag
| $2 = {buf = {{context = {uc_sigmask = {__bits = {0, 0, 0, 0}}, uc_mcontext = {
|           mc_onstack = 0, mc_gs = 0, mc_fs = 0, mc_es = 0, mc_ds = 0, 
|           mc_edi = 0, mc_esi = 0, mc_ebp = 0, mc_isp = 0, mc_ebx = 0, 
|           mc_edx = 0, mc_ecx = 0, mc_eax = 0, mc_trapno = 0, mc_err = 0, 
|           mc_eip = 0, mc_cs = 0, mc_eflags = 0, mc_esp = 0, mc_ss = 0, 
|           mc_len = 0, mc_fpformat = 0, mc_ownedfp = 0, mc_spare1 = {0}, 
|           mc_fpstate = {0 <repeats 128 times>}, mc_spare2 = {0, 0, 0, 0, 0, 0, 
|             0, 0}}, uc_link = 0x0, uc_stack = {ss_sp = 0x0, ss_size = 0, 
|           ss_flags = 0}, uc_flags = 0, __spare__ = {0, 0, 0, 0}}, 
|       status = 0}}, frame = 0x811a4e0, iter = 0xbfbfe760, tag = 0, retval = 4, 
|   scope = 0x8165d2c, dst = 0, prev = 0xbfbfe790, blkid = 0}
| (gdb) info registers 
| eax            0x1a5    421
| ecx            0x805ff7d        134610813
| edx            0xbfbfd7c0       -1077946432
| ebx            0x8119cf4        135372020
| esp            0xbfbfcffc       0xbfbfcffc
| ebp            0xbfbfe738       0xbfbfe738
| esi            0x811100c        135335948
| edi            0xbfbfe760       -1077942432
| eip            0x281ba22c       0x281ba22c
| eflags         0x207    519
| cs             0x1f     31
| ss             0x2f     47
| ds             0x2f     47
| es             0x2f     47
| fs             0x2f     47
| gs             0x97     151

システムコールを行います。

| (gdb) si
| 0x281ba22e in getcontext () from /lib/libc.so.5
| 1: x/i $pc  0x281ba22e <getcontext+10>: jb     0x281ba235 <getcontext+17>

システムコールから出てきた後で prot_tag と register の内容を
調べると、prot_tag にいろいろと保存されています。

| (gdb) p prot_tag
| $3 = (struct tag *) 0xbfbfd7c0
| (gdb) p *prot_tag
| $4 = {buf = {{context = {uc_sigmask = {__bits = {0, 0, 0, 0}}, uc_mcontext = {
|           mc_onstack = 0, mc_gs = 151, mc_fs = 47, mc_es = 47, mc_ds = 47, 
|           mc_edi = -1077942432, mc_esi = 135335948, mc_ebp = -1077942472, 
|           mc_isp = -866099852, mc_ebx = 135372020, mc_edx = 0, 
|           mc_ecx = 134610813, mc_eax = 0, mc_trapno = -866100612, 
|           mc_err = 582, mc_eip = 672899630, mc_cs = 31, mc_eflags = 775, 
|           mc_esp = -1077948420, mc_ss = 47, mc_len = 640, mc_fpformat = 65538, 
|           mc_ownedfp = 131074, mc_spare1 = {-1050295680}, mc_fpstate = {
|             2101887, 66912256, 134785089, 31, 135619620, 47, 8064, 65535, 
|             0 <repeats 25 times>, -822083584, 16380, 0, -1564784640, 
|             -1376234408, 16384, 0, 0, 1087476736, 0 <repeats 86 times>}, 
|           mc_spare2 = {-1041346560, -1041346560, 582, 0, -866100040, 
|             -1067356287, 0, -1041346560}}, uc_link = 0x0, uc_stack = {
|           ss_sp = 0x0, ss_size = 0, ss_flags = 0}, uc_flags = 0, __spare__ = {
|           0, 0, 0, 0}}, status = 0}}, frame = 0x811a4e0, iter = 0xbfbfe760, 
|   tag = 0, retval = 4, scope = 0x8165d2c, dst = 0, prev = 0xbfbfe790, 
|   blkid = 0}
| (gdb) info registers 
| eax            0x0      0
| ecx            0x805ff7d        134610813
| edx            0xbfbfd7c0       -1077946432
| ebx            0x8119cf4        135372020
| esp            0xbfbfcffc       0xbfbfcffc
| ebp            0xbfbfe738       0xbfbfe738
| esi            0x811100c        135335948
| edi            0xbfbfe760       -1077942432
| eip            0x281ba22e       0x281ba22e
| eflags         0x206    518
| cs             0x1f     31
| ss             0x2f     47
| ds             0x2f     47
| es             0x2f     47
| fs             0x2f     47
| gs             0x97     151

ここで eflags が奇数なので carry flag (eflags の最下位ビット)
は 0 です。

http://www.int80h.org/bsdasm/
によれば、FreeBSD では carry flag で system call の失敗を表
すので、system call が成功したことを意味します。

getcontext の残りを進めると、carry flag は 0 なので jb では
分岐せず、ECX へ jump するコードに進みます。

| (gdb) si
| 0x281ba230 in getcontext () from /lib/libc.so.5
| 1: x/i $pc  0x281ba230 <getcontext+12>: add    $0x4,%esp
| (gdb) 
| 0x281ba233 in getcontext () from /lib/libc.so.5
| 1: x/i $pc  0x281ba233 <getcontext+15>: jmp    *%ecx

ECX へ jump すると、rb_eval 内の呼出元へ戻ります。

| (gdb) 
| 0x0805ff7d in rb_eval (self=135678472, n=0xbfbfd7c0) at eval.c:3107
| 3107                if ((state = EXEC_TAG()) == 0) {
| 1: x/i $pc  0x805ff7d <rb_eval+10919>:  mov    0xc894(%ebx),%edx

setcontext まで進めます。

| (gdb) c
| Continuing.
|  after getcontext: 0xbfbfd7c0 0
| before setcontext: 0xbfbfd7c0 6
| 
| Breakpoint 3, 0x281df7f4 in setcontext () from /lib/libc.so.5
| 1: x/i $pc  0x281df7f4 <setcontext>:    mov    $0x1a6,%eax

setcontext が呼び出された時点での prot_tag を調べると、上の
getcontext で得たものがそのまま入っています。

| (gdb) p prot_tag
| $5 = (struct tag *) 0xbfbfd7c0
| (gdb) p *prot_tag
| $6 = {buf = {{context = {uc_sigmask = {__bits = {0, 0, 0, 0}}, uc_mcontext = {
|           mc_onstack = 0, mc_gs = 151, mc_fs = 47, mc_es = 47, mc_ds = 47, 
|           mc_edi = -1077942432, mc_esi = 135335948, mc_ebp = -1077942472, 
|           mc_isp = -866099852, mc_ebx = 135372020, mc_edx = 0, 
|           mc_ecx = 134610813, mc_eax = 0, mc_trapno = -866100612, 
|           mc_err = 582, mc_eip = 672899630, mc_cs = 31, mc_eflags = 775, 
|           mc_esp = -1077948420, mc_ss = 47, mc_len = 640, mc_fpformat = 65538, 
|           mc_ownedfp = 131074, mc_spare1 = {-1050295680}, mc_fpstate = {
|             2101887, 66912256, 134785089, 31, 135619620, 47, 8064, 65535, 
|             0 <repeats 25 times>, -822083584, 16380, 0, -1564784640, 
|             -1376234408, 16384, 0, 0, 1087476736, 0 <repeats 86 times>}, 
|           mc_spare2 = {-1041346560, -1041346560, 582, 0, -866100040, 
|             -1067356287, 0, -1041346560}}, uc_link = 0x0, uc_stack = {
|           ss_sp = 0x0, ss_size = 0, ss_flags = 0}, uc_flags = 0, __spare__ = {
|           0, 0, 0, 0}}, status = 6}}, frame = 0x811a4e0, iter = 0xbfbfe760, 
|   tag = 0, retval = 4, scope = 0x8165d2c, dst = 0, prev = 0xbfbfe790, 
|   blkid = 0}

setcontext の中身を調べてみると、これもシステムコールで実装
されています。

| (gdb) disassemble 
| Dump of assembler code for function setcontext:
| 0x281df7f4 <setcontext+0>:      mov    $0x1a6,%eax
| 0x281df7f9 <setcontext+5>:      int    $0x80
| 0x281df7fb <setcontext+7>:      jb     0x281df7e0 <swapcontext+12>
| 0x281df7fd <setcontext+9>:      ret    
| 0x281df7fe <setcontext+10>:     nop    
| 0x281df7ff <setcontext+11>:     nop    
| 0x281df800 <setcontext+12>:     push   %ebx
| 0x281df801 <setcontext+13>:     call   0x281df806 <setcontext+18>
| 0x281df806 <setcontext+18>:     pop    %ebx
| 0x281df807 <setcontext+19>:     add    $0x7a306,%ebx
| 0x281df80d <setcontext+25>:     jmp    0x281afe5c <_init+4284>
| 0x281df812 <setcontext+30>:     mov    %esi,%esi
| End of assembler dump.

システムコールの直前まで進めます。

| (gdb) si
| 0x281df7f9 in setcontext () from /lib/libc.so.5
| 1: x/i $pc  0x281df7f9 <setcontext+5>:  int    $0x80

システムコールを実行すると、getcontext 内のシステムコール直
後に制御が移ります。

| (gdb) 
| 0x281ba22e in getcontext () from /lib/libc.so.5
| 1: x/i $pc  0x281ba22e <getcontext+10>: jb     0x281ba235 <getcontext+17>

この時点での register の内容を調べると、eflags が奇数なので
システムコールが失敗していることになります。

| (gdb) info registers 
| eax            0x0      0
| ecx            0x805ff7d        134610813
| edx            0x0      0
| ebx            0x8119cf4        135372020
| esp            0xbfbfcffc       0xbfbfcffc
| ebp            0xbfbfe738       0xbfbfe738
| esi            0x811100c        135335948
| edi            0xbfbfe760       -1077942432
| eip            0x281ba22e       0x281ba22e
| eflags         0x207    519
| cs             0x1f     31
| ss             0x2f     47
| ds             0x2f     47
| es             0x2f     47
| fs             0x2f     47
| gs             0x97     151

進めると jb で分岐し、先ほどとは違うところにいきます。

| (gdb) si
| 0x281ba235 in getcontext () from /lib/libc.so.5
| 1: x/i $pc  0x281ba235 <getcontext+17>: push   %ebx
| (gdb) 
| 0x281ba236 in getcontext () from /lib/libc.so.5
| 1: x/i $pc  0x281ba236 <getcontext+18>: call   0x281ba23b <getcontext+23>
| (gdb) 
| 0x281ba23b in getcontext () from /lib/libc.so.5
| 1: x/i $pc  0x281ba23b <getcontext+23>: pop    %ebx
| (gdb) 
| 0x281ba23c in getcontext () from /lib/libc.so.5
| 1: x/i $pc  0x281ba23c <getcontext+24>: add    $0x9f8d1,%ebx
| (gdb) 
| 0x281ba242 in getcontext () from /lib/libc.so.5
| 1: x/i $pc  0x281ba242 <getcontext+30>: jmp    0x281afe5c <_init+4284>
| (gdb) 
| 0x281afe5c in ?? () from /lib/libc.so.5
| 1: x/i $pc  0x281afe5c <_init+4284>:    jmp    *0x434(%ebx)
| (gdb) 
| 0x281ed990 in .cerror () from /lib/libc.so.5
| 1: x/i $pc  0x281ed990 <.cerror>:       push   %eax
| (gdb) 
| 0x281ed991 in .cerror () from /lib/libc.so.5
| 1: x/i $pc  0x281ed991 <.cerror+1>:     call   0x281b0b5c <_init+7612>
| (gdb) 
| 0x281b0b5c in ?? () from /lib/libc.so.5
| 1: x/i $pc  0x281b0b5c <_init+7612>:    jmp    *0x774(%ebx)
| (gdb) 
| 0x2815ae14 in ?? () from /usr/lib/libpthread.so.1
| 1: x/i $pc  0x2815ae14: push   %ebp
| (gdb) 
| 0x2815ae15 in ?? () from /usr/lib/libpthread.so.1
| 1: x/i $pc  0x2815ae15: mov    %esp,%ebp
| (gdb) 
| 0x2815ae17 in ?? () from /usr/lib/libpthread.so.1
| 1: x/i $pc  0x2815ae17: push   %ebx
| (gdb) bt
| #0  0x2815ae17 in ?? () from /usr/lib/libpthread.so.1
| #1  0xbfbfe738 in ?? ()
| #2  0x281ed996 in .cerror () from /lib/libc.so.5
| #3  0x08119cf4 in __JCR_LIST__ ()
| #4  0x080606f8 in rb_eval (self=0, n=0x0) at eval.c:3133
| Previous frame inner to this frame (corrupt stack?)

というわけで、setcontext から getcontext に移った時に carry
flag が 1 になっているのが非常に怪しいです。

p $eflags = 518 などとしてデバッガから無理矢理 0 にして動か
すと、意図どおりに動くようです。

というわけで、carry flag が 1 になっているのがなぜか、という
話なんですが、さて?
-- 
[田中 哲][たなか あきら][Tanaka Akira]

In This Thread