Bug 49230 - please provide workaround for setjmp/longjmp in mingw32
Summary: please provide workaround for setjmp/longjmp in mingw32
Status: RESOLVED INVALID
Alias: None
Product: gcc
Classification: Unclassified
Component: rtl-optimization (show other bugs)
Version: 4.7.0
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2011-05-30 11:50 UTC by gee
Modified: 2011-10-20 13:22 UTC (History)
2 users (show)

See Also:
Host: i686-pc-cygwin
Target: i686-pc-mingw32
Build: i686-pc-cygwin
Known to work: 4.6.0
Known to fail: 4.7.0
Last reconfirmed:


Attachments
testcase (209 bytes, text/plain)
2011-06-29 17:08 UTC, gee
Details
generated asm with i686-pc-mingw32-gcc testsetjmp.c -S -s (474 bytes, text/plain)
2011-06-29 18:03 UTC, gee
Details
preprocessed source and generated code (420.82 KB, application/octet-stream)
2011-06-29 19:37 UTC, gee
Details

Note You need to log in before you can comment on or make changes to this bug.
Description gee 2011-05-30 11:50:54 UTC
in mingw32, there is setjmp/longjmp which its implementation is specific to msvc.
and, in latest gcc trunk ebp register doesn't have to have its caller's frame address.(
unfortunately, this could cause mingw32 application which use setjmp/longjmp to SIGSEGV. because in msvcrt, __NLG_Notify dereferences ebp+8 which is *(((void*)jmp_buf[0])+2) which msvcrt assumes it's correct address(maybe first parameter of caller function), and which it assumes is wrong for now.
Comment 1 Kai Tietz 2011-06-29 15:16:49 UTC
Hmm, I can't confirm this. Do you have a testcase for this?
As far as I know has msvcrt's setjmp function an hidden argument, which should be for 32-bit NULL.  This might be here the real issue.
Comment 2 gee 2011-06-29 17:08:41 UTC
Created attachment 24635 [details]
testcase

i hope it would be helpful
GNU gdb (GDB) 7.3.50.20110610-cvs
Copyright (C) 2011 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 "i686-pc-cygwin".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...

warning: the current range check setting does not match the language.


warning: the current type check setting does not match the language.

Whether backtraces should continue past the entry point of a program is off.
Reading symbols from /tmp/a...done.
(gdb) r
Starting program: /tmp/a
[New Thread 9616.0x1628]

Program received signal SIGSEGV, Segmentation fault.
0x77b66502 in msvcrt!_abnormal_termination ()
   from /cygdrive/c/WINDOWS/system32/msvcrt.dll
(gdb) bt
#0  0x77b66502 in msvcrt!_abnormal_termination ()
   from /cygdrive/c/WINDOWS/system32/msvcrt.dll
#1  0x77b6ac67 in strerror () from /cygdrive/c/WINDOWS/system32/msvcrt.dll
Backtrace stopped: previous frame inner to this frame (corrupt stack?)
(gdb) disassemble
Dump of assembler code for function msvcrt!_abnormal_termination:
   0x77b664cf <+0>:     xor    %eax,%eax
   0x77b664d1 <+2>:     mov    %fs:0x0,%ecx
   0x77b664d8 <+9>:     cmpl   $0x77b66424,0x4(%ecx)
   0x77b664df <+16>:    jne    0x77b664f1 <msvcrt!_abnormal_termination+34>
   0x77b664e1 <+18>:    mov    0xc(%ecx),%edx
   0x77b664e4 <+21>:    mov    0xc(%edx),%edx
   0x77b664e7 <+24>:    cmp    %edx,0x8(%ecx)
   0x77b664ea <+27>:    jne    0x77b664f1 <msvcrt!_abnormal_termination+34>
   0x77b664ec <+29>:    mov    $0x1,%eax
   0x77b664f1 <+34>:    ret
   0x77b664f2 <+35>:    push   %ebx
   0x77b664f3 <+36>:    push   %ecx
   0x77b664f4 <+37>:    mov    $0x77b919b0,%ebx
   0x77b664f9 <+42>:    jmp    0x77b66505 <msvcrt!_abnormal_termination+54>
   0x77b664fb <+44>:    push   %ebx
   0x77b664fc <+45>:    push   %ecx
   0x77b664fd <+46>:    mov    $0x77b919b0,%ebx
=> 0x77b66502 <+51>:    mov    0x8(%ebp),%ecx
   0x77b66505 <+54>:    mov    %ecx,0x8(%ebx)
   0x77b66508 <+57>:    mov    %eax,0x4(%ebx)
   0x77b6650b <+60>:    mov    %ebp,0xc(%ebx)
   0x77b6650e <+63>:    push   %ebp
---Type <return> to continue, or q <return> to quit---q
Quit
Comment 3 Kai Tietz 2011-06-29 17:30:41 UTC
Well, this test-file helps pretty much to see your problem.
As you didn't mentioned, which options you are specifying to gcc on complilation, I assumed that you were using -fomit-frame-pointer and -O1. But well, as described later, it doesn't really matters much here.

As register ebp is used by gcc as internal frame-register. Even if you are specifying -fomit-frame-pointer as option, the main function will still have a frame-pointer setup. Also ebp-register is a callee-saved register, which means its use as local-register variable is in general nothing wise to do.

I compiled program with 4.6.0 gcc using mingw-w64 as runtime, and I get a crash when main-function exits.  This is to be expected, as epilogue for main is

 movl %ebp, %esp
 pop  %ebp
 ret

but output looks as expected:

$ ./tst.exe
start
foobar 10
foobar 9
foobar 8
foobar 7
foobar 6
foobar 5
foobar 4
foobar 3
foobar 2
foobar 1
foobar 0
bar

So I mark this bug as invalid. If for you the code crashes by using mingw.org, well then file a report to them.  This is for sure no gcc issue.

Regards,
Kai
Comment 4 gee 2011-06-29 18:03:24 UTC
Created attachment 24637 [details]
generated asm with i686-pc-mingw32-gcc testsetjmp.c -S -s

> I compiled program with 4.6.0 gcc using mingw-w64 as runtime, and I get a crash
yes this testcase crashes at epilogue of main if gcc doesn't do anything
but in latest gcc trunk, it doesn't work as expected. 
sorry. i forgot to say gcc version
$ gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/libexec/gcc/i686-pc-cygwin/4.7.0/lto-wrapper.exe
Target: i686-pc-cygwin
Configured with: ./configure --config-cache --prefix=/usr --disable-win32-registry --enable-threads=win32 --enable-languages=c,c++,lto --with-win32-nlsapi=unicode --enable-tls --disable-bootstrap --enable-shared --disable-sjlj-exceptions --enable-gomp
Thread model: win32
gcc version 4.7.0 20110622 (experimental) (GCC)
and i attached asm code latest gcc trunk generated
i used i686-pc-mingw32-gcc testsetjmp.c -S -s to generate .s file.
 . please ld using attached asm file.
and i expect you and i get same result.
D:\cygwin\tmp>i686-pc-mingw32-gcc testsetjmp.s

D:\cygwin\tmp>a
start
foobar 10
foobar 9
foobar 8
foobar 7
foobar 6
foobar 5
foobar 4
foobar 3
foobar 2
foobar 1
foobar 0
(jit debugger message dialog shows)
Comment 5 gee 2011-06-29 18:07:14 UTC
and expected bar didn't show up
as it sigsegvs in msvcrt!nlg_notify
Comment 6 gee 2011-06-29 18:08:34 UTC
> $ gcc -v
i'm sorry to attach invalid gcc -v. this is proper one.
$ i686-pc-mingw32-gcc -v
Using built-in specs.
COLLECT_GCC=i686-pc-mingw32-gcc
COLLECT_LTO_WRAPPER=/usr/libexec/gcc/i686-pc-mingw32/4.7.0/lto-wrapper.exe
Target: i686-pc-mingw32
Configured with: ./configure --config-cache --prefix=/usr --enable-win32-registry --enable-threads=win32 --enable-languages=c,c++,lto --with-win32-nlsapi=unicode --enable-tls --disable-bootstrap --target=i686-pc-mingw32 --enable-shared --enable-interpreter --disable-sjlj-exceptions --enable-gomp
Thread model: win32
gcc version 4.7.0 20110622 (experimental) (GCC)
Comment 7 gee 2011-06-29 19:35:20 UTC
(In reply to comment #3)
> As register ebp is used by gcc as internal frame-register. Even if you are
> specifying -fomit-frame-pointer as option, the main function will still have a
> frame-pointer setup. Also ebp-register is a callee-saved register, which means
> its use as local-register variable is in general nothing wise to do.

maybe the following could be counterexample.
this code is from gcc 2011110622.
ebp may be not internal frame pointer.
so in this case setjmp can store invalid ebp.
nlg_notify dereferences ebp,causing sigsegv
not only it don't use framepointer, but also it doesn't restore framepointer

LFE104:
	.p2align 4,,15
	.globl	_rb_ensure
	.def	_rb_ensure;	.scl	2;	.type	32;	.endef
_rb_ensure:
LFB105:
	.loc 2 727 0 is_stmt 1
	.cfi_startproc
LVL835:
	pushl	%ebx
LCFI228:
	.cfi_def_cfa_offset 8
	.cfi_offset 3, -8
	subl	$120, %esp
LCFI229:
	.cfi_def_cfa_offset 128
LBB566:
	.loc 2 731 0
	movl	_ruby_current_thread, %eax
LBE566:
	.loc 2 729 0
	movl	$4, 32(%esp)
LVL836:
LBB567:
	.loc 2 731 0
	movl	$0, 100(%esp)
	movl	%eax, %edx
	movl	%eax, 28(%esp)
LVL837:
	movl	152(%eax), %eax
	movl	%eax, 108(%esp)
	leal	36(%esp), %eax
	movl	%eax, 152(%edx)
	.loc 2 732 0
	movl	%eax, (%esp)
	call	__setjmp
LVL838:
	testl	%eax, %eax
	movl	%eax, %ebx
LVL839:
	je	L733
	.loc 2 735 0
	movl	108(%esp), %eax
LVL840:
	movl	28(%esp), %edx
	movl	%eax, 152(%edx)
LBE567:
	.loc 2 738 0
	movl	140(%esp), %eax
	movl	%eax, (%esp)
	call	*136(%esp)
LVL841:
	.loc 2 740 0
	movl	_ruby_current_thread, %eax
	movl	%ebx, 4(%esp)
	movl	152(%eax), %eax
	movl	%eax, (%esp)
	call	_longjmp
LVL842:
	.p2align 4,,10
L733:
LBB568:
	.loc 2 733 0
	movl	132(%esp), %eax
	movl	%eax, (%esp)
	call	*128(%esp)
LVL843:
	.loc 2 735 0
	movl	28(%esp), %edx
	.loc 2 733 0
	movl	%eax, 32(%esp)
	.loc 2 735 0
	movl	108(%esp), %eax
	movl	%eax, 152(%edx)
LBE568:
	.loc 2 738 0
	movl	140(%esp), %eax
	movl	%eax, (%esp)
	call	*136(%esp)
LVL844:
	.loc 2 741 0
	movl	32(%esp), %eax
	.loc 2 742 0
	addl	$120, %esp
LCFI230:
	.cfi_def_cfa_offset 8
	popl	%ebx
LCFI231:
	.cfi_def_cfa_offset 4
	.cfi_restore 3
LVL845:
	ret
	.cfi_endproc
LFE105:
	.p2align 4,,15
	.globl	_rb_frame_this_func
	.def	_rb_frame_this_func;	.scl	2;	.type	32;	.endef
_rb_frame_this_func:
LFB108:
	.loc 2 791 0
	.cfi_startproc
	.loc 2 792 0
	movl	_ruby_current_thread, %eax
	movl	16(%eax), %eax
	jmp	_frame_func_id
LVL846:
	.cfi_endproc
LFE108:
Comment 8 gee 2011-06-29 19:37:35 UTC
Created attachment 24640 [details]
preprocessed source and generated code

verbose log

i686-pc-mingw32-gcc -O4 -mfpmath=sse -march=native -mtune=native -mthreads -mstackrealign -g3 -Wextra -Wno-unused-parameter -Wno-parentheses -Wno-long-long -Wno-missing-field-initializers -Werror=pointer-arith -Werror=write-strings -Werror=declaration-after-statement -Werror=implicit-function-declaration -include ruby/config.h -include ruby/missing.h -fvisibility=hidden -DRUBY_EXPORT   -I. -I.ext/include/i386-mingw32 -I./include -I. -I/usr/include/w32api -o eval.o -c eval.c -save-temps -v
Using built-in specs.
COLLECT_GCC=i686-pc-mingw32-gcc
COLLECT_LTO_WRAPPER=/usr/libexec/gcc/i686-pc-mingw32/4.7.0/lto-wrapper.exe
Target: i686-pc-mingw32
Configured with: ./configure --config-cache --prefix=/usr --enable-win32-registry --enable-threads=win32 --enable-languages=c,c++,lto --with-win32-nlsapi=unicode --enable-tls --disable-bootstrap --target=i686-pc-mingw32 --enable-shared --enable-interpreter --disable-sjlj-exceptions --enable-gomp
Thread model: win32
gcc version 4.7.0 20110622 (experimental) (GCC)
COLLECT_GCC_OPTIONS='-O4' '-mfpmath=sse' '-march=native' '-mtune=native' '-mthreads' '-mstackrealign' '-g3' '-Wextra' '-Wno-unused-parameter' '-Wno-parentheses' '-Wno-long-long' '-Wno-missing-field-initializers' '-Werror=pointer-arith' '-Werror=write-strings' '-Werror=declaration-after-statement' '-Werror=implicit-function-declaration' '-include' 'ruby/config.h' '-include' 'ruby/missing.h' '-fvisibility=hidden' '-D' 'RUBY_EXPORT' '-I' '.' '-I' '.ext/include/i386-mingw32' '-I' './include' '-I' '.' '-I' '/usr/include/w32api' '-o' 'eval.o' '-c' '-save-temps' '-v'
 /usr/libexec/gcc/i686-pc-mingw32/4.7.0/cc1.exe -E -quiet -v -I . -I .ext/include/i386-mingw32 -I ./include -I . -I /usr/include/w32api -dD -D_MT -D RUBY_EXPORT -include ruby/config.h -include ruby/missing.h eval.c -march=core2 -mcx16 -msahf -mno-movbe -mno-aes -mno-pclmul -mno-popcnt -mno-abm -mno-lwp -mno-fma -mno-fma4 -mno-xop -mno-bmi -mno-tbm -mno-avx -mno-sse4.2 -msse4.1 --param l1-cache-size=32 --param l1-cache-line-size=64 --param l2-cache-size=2048 -mtune=core2 -mfpmath=sse -mthreads -mstackrealign -Wextra -Wno-unused-parameter -Wno-parentheses -Wno-long-long -Wno-missing-field-initializers -Werror=pointer-arith -Werror=write-strings -Werror=declaration-after-statement -Werror=implicit-function-declaration -fvisibility=hidden -g3 -fworking-directory -O4 -fpch-preprocess -o eval.i
ignoring nonexistent directory "/usr/lib/gcc/i686-pc-mingw32/4.7.0/../../../../i686-pc-mingw32/sys-include"
ignoring duplicate directory "."
#include "..." search starts here:
#include <...> search starts here:
 .
 .ext/include/i386-mingw32
 ./include
 /usr/include/w32api
 /usr/lib/gcc/i686-pc-mingw32/4.7.0/include
 /usr/lib/gcc/i686-pc-mingw32/4.7.0/include-fixed
 /usr/lib/gcc/i686-pc-mingw32/4.7.0/../../../../i686-pc-mingw32/include
End of search list.
COLLECT_GCC_OPTIONS='-O4' '-mfpmath=sse' '-march=native' '-mtune=native' '-mthreads' '-mstackrealign' '-g3' '-Wextra' '-Wno-unused-parameter' '-Wno-parentheses' '-Wno-long-long' '-Wno-missing-field-initializers' '-Werror=pointer-arith' '-Werror=write-strings' '-Werror=declaration-after-statement' '-Werror=implicit-function-declaration' '-include' 'ruby/config.h' '-include' 'ruby/missing.h' '-fvisibility=hidden' '-D' 'RUBY_EXPORT' '-I' '.' '-I' '.ext/include/i386-mingw32' '-I' './include' '-I' '.' '-I' '/usr/include/w32api' '-o' 'eval.o' '-c' '-save-temps' '-v'
 /usr/libexec/gcc/i686-pc-mingw32/4.7.0/cc1.exe -fpreprocessed eval.i -march=core2 -mcx16 -msahf -mno-movbe -mno-aes -mno-pclmul -mno-popcnt -mno-abm -mno-lwp -mno-fma -mno-fma4 -mno-xop -mno-bmi -mno-tbm -mno-avx -mno-sse4.2 -msse4.1 --param l1-cache-size=32 --param l1-cache-line-size=64 --param l2-cache-size=2048 -mtune=core2 -quiet -dumpbase eval.c -mfpmath=sse -mthreads -mstackrealign -auxbase-strip eval.o -g3 -O4 -Wextra -Wno-unused-parameter -Wno-parentheses -Wno-long-long -Wno-missing-field-initializers -Werror=pointer-arith -Werror=write-strings -Werror=declaration-after-statement -Werror=implicit-function-declaration -version -fvisibility=hidden -o eval.s
GNU C (GCC) version 4.7.0 20110622 (experimental) (i686-pc-mingw32)
        compiled by GNU C version 4.7.0 20110622 (experimental), GMP version 5.0.0, MPFR version 2.4.2, MPC version 0.8.1
GGC heuristics: --param ggc-min-expand=30 --param ggc-min-heapsize=4096
GNU C (GCC) version 4.7.0 20110622 (experimental) (i686-pc-mingw32)
        compiled by GNU C version 4.7.0 20110622 (experimental), GMP version 5.0.0, MPFR version 2.4.2, MPC version 0.8.1
GGC heuristics: --param ggc-min-expand=30 --param ggc-min-heapsize=4096
Compiler executable checksum: b98a9904da950a3b4c2a13878d133f7f
COLLECT_GCC_OPTIONS='-O4' '-mfpmath=sse' '-march=native' '-mtune=native' '-mthreads' '-mstackrealign' '-g3' '-Wextra' '-Wno-unused-parameter' '-Wno-parentheses' '-Wno-long-long' '-Wno-missing-field-initializers' '-Werror=pointer-arith' '-Werror=write-strings' '-Werror=declaration-after-statement' '-Werror=implicit-function-declaration' '-include' 'ruby/config.h' '-include' 'ruby/missing.h' '-fvisibility=hidden' '-D' 'RUBY_EXPORT' '-I' '.' '-I' '.ext/include/i386-mingw32' '-I' './include' '-I' '.' '-I' '/usr/include/w32api' '-o' 'eval.o' '-c' '-save-temps' '-v'
 /usr/lib/gcc/i686-pc-mingw32/4.7.0/../../../../i686-pc-mingw32/bin/as.exe -o eval.o eval.s
COMPILER_PATH=/usr/libexec/gcc/i686-pc-mingw32/4.7.0/:/usr/libexec/gcc/i686-pc-mingw32/4.7.0/:/usr/libexec/gcc/i686-pc-mingw32/:/usr/lib/gcc/i686-pc-mingw32/4.7.0/:/usr/lib/gcc/i686-pc-mingw32/:/usr/lib/gcc/i686-pc-mingw32/4.7.0/../../../../i686-pc-mingw32/bin/
LIBRARY_PATH=/usr/lib/gcc/i686-pc-mingw32/4.7.0/:/usr/lib/gcc/i686-pc-mingw32/4.7.0/../../../../i686-pc-mingw32/lib/
COLLECT_GCC_OPTIONS='-O4' '-mfpmath=sse' '-march=native' '-mtune=native' '-mthreads' '-mstackrealign' '-g3' '-Wextra' '-Wno-unused-parameter' '-Wno-parentheses' '-Wno-long-long' '-Wno-missing-field-initializers' '-Werror=pointer-arith' '-Werror=write-strings' '-Werror=declaration-after-statement' '-Werror=implicit-function-declaration' '-include' 'ruby/config.h' '-include' 'ruby/missing.h' '-fvisibility=hidden' '-D' 'RUBY_EXPORT' '-I' '.' '-I' '.ext/include/i386-mingw32' '-I' './include' '-I' '.' '-I' '/usr/include/w32api' '-o' 'eval.o' '-c' '-save-temps' '-v'
Comment 9 gee 2011-06-29 19:50:34 UTC
this is invalid if ebp is used as framepointer

5381            long len = RARRAY_LEN(tmp);
   0x62d7ae8b <+155>:   mov    (%eax),%ebp
   0x62d7ae8d <+157>:   test   $0x2000,%ebp
---Type <return> to continue, or q <return> to quit---
   0x62d7ae93 <+163>:   jne    0x62d7af40 <rb_io_s_popen+336>
   0x62d7ae99 <+169>:   mov    0x8(%eax),%ebp
   0x62d7af40 <+336>:   shr    $0xf,%ebp
   0x62d7af43 <+339>:   and    $0x3,%ebp
   0x62d7af46 <+342>:   jmp    0x62d7ae9c <rb_io_s_popen+172>
   0x62d7af4b <+347>:   nop
   0x62d7af4c <+348>:   lea    0x0(%esi,%eiz,1),%esi
ebp is used. isn't it supposed as frame pointer?
additionally, long len = RARRAY_LEN(tmp); is expanded to 
long len = ((((struct RBasic*)(tmp))->flags & (((VALUE)1)<<(12+1))) ? \
     (long)((((struct RBasic*)(tmp))->flags >> (12+3)) & \
	 (((((VALUE)1)<<(12+4))|(((VALUE)1)<<(12+3))) >> (12+3))) : \
     ((struct RArray*)(tmp))->as.heap.len);

gcc is doing wrong just like my testcase i posted.
> which means its use as local-register variable is in general nothing wise to do.
like you mentioned.
could it be fixed?

and debug session

Starting program: /tmp/ruby/ruby -I./lib -I. -I.ext/common -I.ext/i386-mingw32 D:/cygwin/tmp/ruby/test/ruby/test_process.rb
[New Thread 284.0x1400]
[New Thread 284.0x1ac8]
[New Thread 284.0x2904]
<internal:gem_prelude>:1:in `require': cannot load such file -- rubygems.rb (LoadError)
        from <internal:gem_prelude>:1:in `<compiled>'
Run options:

# Running tests:

.<internal:gem_prelude>:1:in `require': cannot load such file -- rubygems.rb (LoadError)
        from <internal:gem_prelude>:1:in `<compiled>'
<internal:gem_prelude>:1:in `require': cannot load such file -- rubygems.rb (LoadError)
        from <internal:gem_prelude>:1:in `<compiled>'
F<internal:gem_prelude>:1:in `require': cannot load such file -- rubygems.rb (LoadError)
        from <internal:gem_prelude>:1:in `<compiled>'
F
Breakpoint 5, 0x77b671c8 in setjmp ()
   from /cygdrive/c/WINDOWS/system32/msvcrt.dll
(gdb) <internal:gem_prelude>:1:in `require': cannot load such file -- rubygems.rb (LoadError)
        from <internal:gem_prelude>:1:in `<compiled>'

(gdb) i b
Num     Type           Disp Enb Address    What
5       breakpoint     keep y   0x77b671c8 <setjmp>
        stop only if $ebp<0x10
        breakpoint already hit 1 time
(gdb) i r
eax            0x23eb64 0x23eb64
ecx            0x7c969f8c       0x7c969f8c
edx            0x359a8  0x359a8
ebx            0x148fc20        0x148fc20
esp            0x23eb3c 0x23eb3c
ebp            0x3      0x3
esi            0x148fe18        0x148fe18
edi            0x23ec1c 0x23ec1c
eip            0x77b671c8       0x77b671c8 <setjmp>
eflags         0x202    [ IF ]
cs             0x1b     0x1b
ss             0x23     0x23
ds             0x23     0x23
es             0x23     0x23
fs             0x3b     0x3b
gs             0x0      0x0
(gdb) bt
#0  0x77b671c8 in setjmp () from /cygdrive/c/WINDOWS/system32/msvcrt.dll
#1  0x62d4b37b in rb_ensure (b_proc=0x62e6f310 <rb_yield>, data1=0x148fc20,
    e_proc=0x62d6aac0 <io_close>, data2=0x148fc20) at eval.c:732
#2  0x62d7af2d in rb_io_s_popen (argc=<optimized out>, argv=0xf801f8,
    klass=0x1039428) at io.c:5408
#3  0x62e5f3cd in call_cfunc (func=0x62d7adf0 <rb_io_s_popen>,
    recv=<optimized out>, len=0xffffffff, argc=0x2, argv=0xf801f8)
    at vm_insnhelper.c:317
#4  0x62e6e0f5 in vm_call_cfunc (me=0x1093de0, blockptr=0xfffae0,
    recv=0x1039428, num=0x2, reg_cfp=0xfffacc, th=0x359a8)
    at vm_insnhelper.c:404
#5  vm_call_method (th=0x359a8, cfp=0xfffacc, num=<optimized out>,
    blockptr=0xfffae0, flag=<optimized out>, id=<optimized out>, me=0x1093de0,
    recv=0x1039428) at vm_insnhelper.c:526
#6  0x62e6482d in vm_exec_core (th=0x359a8, initial=<optimized out>)
    at insns.def:1012
#7  0x62e68548 in vm_exec (th=0x359a8) at vm.c:1180
#8  0x62e69208 in invoke_block_from_c (th=0x359a8, block=<optimized out>,
    self=0x1493370, argc=0x1, argv=0x23ef90, blockptr=0x0, cref=0x0)
    at vm.c:591
#9  0x62e6f352 in vm_yield (argv=0x23ef90, argc=0x1, th=<optimized out>)
    at vm.c:621
#10 rb_yield_0 (argv=0x23ef90, argc=0x1) at vm_eval.c:740
---Type <return> to continue, or q <return> to quit---
#11 rb_yield (val=0x1490100) at vm_eval.c:750
#12 0x62d4b3d1 in rb_ensure (b_proc=0x62d36d00 <chdir_yield>, data1=0x23f024,
    e_proc=0x62d36a50 <chdir_restore>, data2=0x23f024) at eval.c:733
#13 0x62d36f8b in dir_s_chdir (argc=0x1, argv=0xf801c8, obj=0x1037f70)
    at dir.c:862
#14 0x62e5f3cd in call_cfunc (func=0x62d36e60 <dir_s_chdir>,
    recv=<optimized out>, len=0xffffffff, argc=0x1, argv=0xf801c8)
    at vm_insnhelper.c:317
#15 0x62e6e0f5 in vm_call_cfunc (me=0x10a58f8, blockptr=0xfffb90,
    recv=0x1037f70, num=0x1, reg_cfp=0xfffb7c, th=0x359a8)
    at vm_insnhelper.c:404
#16 vm_call_method (th=0x359a8, cfp=0xfffb7c, num=<optimized out>,
    blockptr=0xfffb90, flag=<optimized out>, id=<optimized out>, me=0x10a58f8,
    recv=0x1037f70) at vm_insnhelper.c:526
#17 0x62e6482d in vm_exec_core (th=0x359a8, initial=<optimized out>)
    at insns.def:1012
#18 0x62e68548 in vm_exec (th=0x359a8) at vm.c:1180
#19 0x62e69208 in invoke_block_from_c (th=0x359a8, block=<optimized out>,
    self=0x1520a60, argc=0x1, argv=0x23f380, blockptr=0x0, cref=0x0)
    at vm.c:591
#20 0x62e6f352 in vm_yield (argv=0x23f380, argc=0x1, th=<optimized out>)
    at vm.c:621
#21 rb_yield_0 (argv=0x23f380, argc=0x1) at vm_eval.c:740
---Type <return> to continue, or q <return> to quit---
#22 rb_yield (val=0x14ea628) at vm_eval.c:750
#23 0x62d0bb7c in rb_ary_collect (ary=0x14c36a0) at array.c:2220
#24 0x62e6e0f5 in vm_call_cfunc (me=0x10807b8, blockptr=0xfffd1c,
    recv=0x14c36a0, num=0x0, reg_cfp=0xfffd08, th=0x359a8)
    at vm_insnhelper.c:404
#25 vm_call_method (th=0x359a8, cfp=0xfffd08, num=<optimized out>,
    blockptr=0xfffd1c, flag=<optimized out>, id=<optimized out>, me=0x10807b8,
    recv=0x14c36a0) at vm_insnhelper.c:526
#26 0x62e6482d in vm_exec_core (th=0x359a8, initial=<optimized out>)
    at insns.def:1012
#27 0x62e68548 in vm_exec (th=0x359a8) at vm.c:1180
#28 0x62e69208 in invoke_block_from_c (th=0x359a8, block=<optimized out>,
    self=0x1520a60, argc=0x1, argv=0x23f690, blockptr=0x0, cref=0x0)
    at vm.c:591
#29 0x62e6f352 in vm_yield (argv=0x23f690, argc=0x1, th=<optimized out>)
    at vm.c:621
#30 rb_yield_0 (argv=0x23f690, argc=0x1) at vm_eval.c:740
#31 rb_yield (val=0x1521138) at vm_eval.c:750
#32 0x62d060cc in rb_ary_each (array=0x1509498) at array.c:1478
#33 0x62e6e0f5 in vm_call_cfunc (me=0x107fa90, blockptr=0xfffdcc,
    recv=0x1509498, num=0x0, reg_cfp=0xfffdb8, th=0x359a8)
    at vm_insnhelper.c:404
#34 vm_call_method (th=0x359a8, cfp=0xfffdb8, num=<optimized out>,
---Type <return> to continue, or q <return> to quit---
    blockptr=0xfffdcc, flag=<optimized out>, id=<optimized out>, me=0x107fa90,
    recv=0x1509498) at vm_insnhelper.c:526
#35 0x62e6482d in vm_exec_core (th=0x359a8, initial=<optimized out>)
    at insns.def:1012
#36 0x62e68548 in vm_exec (th=0x359a8) at vm.c:1180
#37 0x62e69208 in invoke_block_from_c (th=0x359a8, block=<optimized out>,
    self=0x1520a60, argc=0x1, argv=0x23f9a0, blockptr=0x0, cref=0x0)
    at vm.c:591
#38 0x62e6f352 in vm_yield (argv=0x23f9a0, argc=0x1, th=<optimized out>)
    at vm.c:621
#39 rb_yield_0 (argv=0x23f9a0, argc=0x1) at vm_eval.c:740
#40 rb_yield (val=0x1509720) at vm_eval.c:750
#41 0x62d060cc in rb_ary_each (array=0x1509528) at array.c:1478
#42 0x62e6e0f5 in vm_call_cfunc (me=0x107fa90, blockptr=0xfffed4,
    recv=0x1509528, num=0x0, reg_cfp=0xfffec0, th=0x359a8)
    at vm_insnhelper.c:404
#43 vm_call_method (th=0x359a8, cfp=0xfffec0, num=<optimized out>,
    blockptr=0xfffed4, flag=<optimized out>, id=<optimized out>, me=0x107fa90,
    recv=0x1509528) at vm_insnhelper.c:526
#44 0x62e6482d in vm_exec_core (th=0x359a8, initial=<optimized out>)
    at insns.def:1012
#45 0x62e68548 in vm_exec (th=0x359a8) at vm.c:1180
#46 0x62e69208 in invoke_block_from_c (th=0x359a8, block=<optimized out>,
---Type <return> to continue, or q <return> to quit---
    self=0x1458de0, argc=0x0, argv=0x1520af8, blockptr=0x0, cref=0x0)
    at vm.c:591
#47 0x62e6937d in rb_vm_invoke_proc (th=0x359a8, proc=0x1600848,
    self=0x1458de0, argc=0x0, argv=0x1520af8, blockptr=0x0) at vm.c:637
#48 0x62d50338 in rb_proc_call (self=0x1458888, args=0x1520af0) at proc.c:580
#49 0x62d48758 in rb_call_end_proc (data=0x1458888) at eval_jump.c:13
#50 0x62d4a55a in rb_exec_end_proc () at eval_jump.c:126
#51 0x62d4a5de in ruby_finalize_0 () at eval.c:92
#52 0x62d4a8cd in ruby_cleanup (ex=0x0) at eval.c:133
#53 0x62d4ac61 in ruby_run_node (n=0x102ca20) at eval.c:241
#54 0x00401e7f in main (argc=0x6, argv=0x357b0) at main.c:38
#55 0x004010fd in __mingw_CRTStartup () at ../../.././winsup/mingw/crt1.c:244
#56 0x00000408 in ?? ()
#57 0x7ffd9000 in ?? ()
#58 0x00000000 in ?? ()


(gdb) up
#1  0x62d4b37b in rb_ensure (b_proc=0x62e6f310 <rb_yield>, data1=0x148fc20,
    e_proc=0x62d6aac0 <io_close>, data2=0x148fc20) at eval.c:732
732         if ((state = EXEC_TAG()) == 0) {
(gdb)
#2  0x62d7af2d in rb_io_s_popen (argc=<optimized out>, argv=0xf801f8,
    klass=0x1039428) at io.c:5408
5408            return rb_ensure(rb_yield, port, io_close, port);
(gdb) disass
Dump of assembler code for function rb_io_s_popen:
   0x62d7adf0 <+0>:     push   %ebp
   0x62d7adf1 <+1>:     push   %edi
   0x62d7adf2 <+2>:     push   %esi
   0x62d7adf3 <+3>:     push   %ebx
   0x62d7adf4 <+4>:     sub    $0x7c,%esp
   0x62d7adf7 <+7>:     lea    0x40(%esp),%eax
   0x62d7adfb <+11>:    movl   $0x62ea0c34,0x8(%esp)
   0x62d7ae03 <+19>:    mov    %eax,0x14(%esp)
   0x62d7ae07 <+23>:    mov    0x94(%esp),%eax
   0x62d7ae0e <+30>:    lea    0x3c(%esp),%edi
   0x62d7ae12 <+34>:    lea    0x38(%esp),%esi
   0x62d7ae16 <+38>:    mov    %edi,0x10(%esp)
   0x62d7ae1a <+42>:    mov    %esi,0xc(%esp)
   0x62d7ae1e <+46>:    lea    0x4c(%esp),%ebx
   0x62d7ae22 <+50>:    mov    %eax,0x4(%esp)
   0x62d7ae26 <+54>:    mov    0x90(%esp),%eax
   0x62d7ae2d <+61>:    mov    %eax,(%esp)
   0x62d7ae30 <+64>:    call   0x62d29510 <rb_scan_args>
   0x62d7ae35 <+69>:    mov    0x40(%esp),%ecx
   0x62d7ae39 <+73>:    lea    0x48(%esp),%eax
   0x62d7ae3d <+77>:    mov    %eax,0x4(%esp)
   0x62d7ae41 <+81>:    lea    0x44(%esp),%eax
---Type <return> to continue, or q <return> to quit---q
Quit
(gdb) disass /m
Dump of assembler code for function rb_io_s_popen:
4214        int accmode = oflags & (O_RDONLY|O_WRONLY|O_RDWR);
   0x62d7ae59 <+105>:   mov    %eax,%edx
   0x62d7ae5b <+107>:   and    $0x3,%edx

4215        if (oflags & O_APPEND) {
   0x62d7ae5e <+110>:   test   $0x8,%al
   0x62d7ae60 <+112>:   je     0x62d7ae6c <rb_io_s_popen+124>

4216            if (accmode == O_WRONLY) {
   0x62d7ae62 <+114>:   cmp    $0x1,%edx
   0x62d7ae65 <+117>:   je     0x62d7ae76 <rb_io_s_popen+134>

4217                return MODE_BINARY("a", "ab");
4218            }
4219            if (accmode == O_RDWR) {
   0x62d7ae67 <+119>:   cmp    $0x2,%edx
   0x62d7ae6a <+122>:   je     0x62d7ae76 <rb_io_s_popen+134>

4220                return MODE_BINARY("a+", "ab+");
4221            }
4222        }
4223        switch (oflags & (O_RDONLY|O_WRONLY|O_RDWR)) {
---Type <return> to continue, or q <return> to quit---
   0x62d7ae6c <+124>:   cmp    $0x2,%edx
   0x62d7ae6f <+127>:   nop
   0x62d7ae70 <+128>:   ja     0x62d7af90 <rb_io_s_popen+416>

4224          case O_RDONLY:
4225            return MODE_BINARY("r", "rb");
4226          case O_WRONLY:
4227            return MODE_BINARY("w", "wb");
4228          case O_RDWR:
4229            return MODE_BINARY("r+", "rb+");
4230        }
4231        rb_raise(rb_eArgError, "invalid access oflags 0x%x", oflags);
   0x62d7af90 <+416>:   mov    %eax,0x8(%esp)
   0x62d7af94 <+420>:   mov    0x62f3e7b0,%eax
   0x62d7af99 <+425>:   movl   $0x62ea0a14,0x4(%esp)
   0x62d7afa1 <+433>:   mov    %eax,(%esp)
   0x62d7afa4 <+436>:   call   0x62d44410 <rb_raise>
   0x62d7afa9 <+441>:   lea    0x0(%esi,%eiz,1),%esi

4232        return NULL;                /* not reached */
4233    }
4234
4235    /*
---Type <return> to continue, or q <return> to quit---
4236     * Convert external/internal encodings to enc/enc2
4237     * NULL => use default encoding
4238     * Qnil => no encoding specified (internal only)
4239     */
4240    static void
4241    rb_io_ext_int_to_encs(rb_encoding *ext, rb_encoding *intern, rb_encoding **enc, rb_encoding **enc2)
4242    {
4243        int default_ext = 0;
4244
4245        if (ext == NULL) {
4246            ext = rb_default_external_encoding();
4247            default_ext = 1;
4248        }
4249        if (intern == NULL && ext != rb_ascii8bit_encoding())
4250            /* If external is ASCII-8BIT, no default transcoding */
4251            intern = rb_default_internal_encoding();
4252        if (intern == NULL || intern == (rb_encoding *)Qnil || intern == ext) {
4253            /* No internal encoding => use external + no transcoding */
4254            *enc = (default_ext && intern != ext) ? NULL : ext;
4255            *enc2 = NULL;
4256        }
---Type <return> to continue, or q <return> to quit---
4257        else {
4258            *enc = intern;
4259            *enc2 = ext;
4260        }
4261    }
4262
4263    static void
4264    parse_mode_enc(const char *estr, rb_encoding **enc_p, rb_encoding **enc2_p, int *fmode_p)
4265    {
4266        const char *p;
4267        char encname[ENCODING_MAXNAMELEN+1];
4268        int idx, idx2;
4269        rb_encoding *ext_enc, *int_enc;
4270
4271        /* parse estr as "enc" or "enc2:enc" or "enc:-" */
4272
4273        p = strrchr(estr, ':');
4274        if (p) {
4275            long len = (p++) - estr;
4276            if (len == 0 || len > ENCODING_MAXNAMELEN)
4277                idx = -1;
4278            else {
---Type <return> to continue, or q <return> to quit---
4279                if (io_encname_bom_p(estr, len)) {
4280                    if (fmode_p) *fmode_p |= FMODE_SETENC_BY_BOM;
4281                    estr += 4;
4282                    len -= 4;
4283                }
4284                memcpy(encname, estr, len);
4285                encname[len] = '\0';
4286                estr = encname;
4287                idx = rb_enc_find_index(encname);
4288            }
4289        }
4290        else {
4291            long len = strlen(estr);
4292            if (io_encname_bom_p(estr, len)) {
4293                if (fmode_p) *fmode_p |= FMODE_SETENC_BY_BOM;
4294                estr += 4;
4295                len -= 4;
4296                memcpy(encname, estr, len);
4297                encname[len] = '\0';
4298                estr = encname;
4299            }
4300            idx = rb_enc_find_index(estr);
4301        }
---Type <return> to continue, or q <return> to quit---
4302
4303        if (idx >= 0)
4304            ext_enc = rb_enc_from_index(idx);
4305        else {
4306            if (idx != -2)
4307                rb_warn("Unsupported encoding %s ignored", estr);
4308            ext_enc = NULL;
4309        }
4310
4311        int_enc = NULL;
4312        if (p) {
4313            if (*p == '-' && *(p+1) == '\0') {
4314                /* Special case - "-" => no transcoding */
4315                int_enc = (rb_encoding *)Qnil;
4316            }
4317            else {
4318                idx2 = rb_enc_find_index(p);
4319                if (idx2 < 0)
4320                    rb_warn("Unsupported encoding %s ignored", p);
4321                else if (idx2 == idx) {
4322                    rb_warn("Ignoring internal encoding %s: it is identical to external encoding %s", p, estr);
4323                    int_enc = (rb_encoding *)Qnil;
---Type <return> to continue, or q <return> to quit---
4324                }
4325                else
4326                    int_enc = rb_enc_from_index(idx2);
4327            }
4328        }
4329
4330        rb_io_ext_int_to_encs(ext_enc, int_enc, enc_p, enc2_p);
4331    }
4332
4333    static void
4334    mode_enc(rb_io_t *fptr, const char *estr)
4335    {
4336        clear_codeconv(fptr);
4337
4338        parse_mode_enc(estr, &fptr->encs.enc, &fptr->encs.enc2, NULL);
4339    }
4340
4341    static void
4342    rb_io_mode_enc(rb_io_t *fptr, const char *modestr)
4343    {
4344        const char *p = strchr(modestr, ':');
4345        if (p) {
4346            mode_enc(fptr, p+1);
---Type <return> to continue, or q <return> to quit---
4347        }
4348    }
4349
4350    int
4351    rb_io_extract_encoding_option(VALUE opt, rb_encoding **enc_p, rb_encoding **enc2_p, int *fmode_p)
4352    {
4353        VALUE encoding=Qnil, extenc=Qundef, intenc=Qundef, tmp;
4354        int extracted = 0;
4355        rb_encoding *extencoding = NULL;
4356        rb_encoding *intencoding = NULL;
4357
4358        if (!NIL_P(opt)) {
4359            VALUE v;
4360            v = rb_hash_lookup2(opt, sym_encoding, Qnil);
4361            if (v != Qnil) encoding = v;
4362            v = rb_hash_lookup2(opt, sym_extenc, Qundef);
4363            if (v != Qnil) extenc = v;
4364            v = rb_hash_lookup2(opt, sym_intenc, Qundef);
4365            if (v != Qundef) intenc = v;
4366        }
4367        if ((extenc != Qundef || intenc != Qundef) && !NIL_P(encoding)) {
4368            if (!NIL_P(ruby_verbose)) {
---Type <return> to continue, or q <return> to quit---
4369                int idx = rb_to_encoding_index(encoding);
4370                rb_warn("Ignoring encoding parameter '%s': %s_encoding is used",
4371                        idx < 0 ? StringValueCStr(encoding) : rb_enc_name(rb_enc_from_index(idx)),
4372                        extenc == Qundef ? "internal" : "external");
4373            }
4374            encoding = Qnil;
4375        }
4376        if (extenc != Qundef && !NIL_P(extenc)) {
4377            extencoding = rb_to_encoding(extenc);
4378        }
4379        if (intenc != Qundef) {
4380            if (NIL_P(intenc)) {
4381                /* internal_encoding: nil => no transcoding */
4382                intencoding = (rb_encoding *)Qnil;
4383            }
4384            else if (!NIL_P(tmp = rb_check_string_type(intenc))) {
4385                char *p = StringValueCStr(tmp);
4386
4387                if (*p == '-' && *(p+1) == '\0') {
4388                    /* Special case - "-" => no transcoding */
4389                    intencoding = (rb_encoding *)Qnil;
---Type <return> to continue, or q <return> to quit---
4390                }
4391                else {
4392                    intencoding = rb_to_encoding(intenc);
4393                }
4394            }
4395            else {
4396                intencoding = rb_to_encoding(intenc);
4397            }
4398            if (extencoding == intencoding) {
4399                intencoding = (rb_encoding *)Qnil;
4400            }
4401        }
4402        if (!NIL_P(encoding)) {
4403            extracted = 1;
4404            if (!NIL_P(tmp = rb_check_string_type(encoding))) {
4405                parse_mode_enc(StringValueCStr(tmp), enc_p, enc2_p, fmode_p);
4406            }
4407            else {
4408                rb_io_ext_int_to_encs(rb_to_encoding(encoding), NULL, enc_p, enc2_p);
4409            }
4410        }
---Type <return> to continue, or q <return> to quit---
4411        else if (extenc != Qundef || intenc != Qundef) {
4412            extracted = 1;
4413            rb_io_ext_int_to_encs(extencoding, intencoding, enc_p, enc2_p);
4414        }
4415        return extracted;
4416    }
4417
4418    typedef struct rb_io_enc_t convconfig_t;
4419
4420    static void
4421    validate_enc_binmode(int *fmode_p, int ecflags, rb_encoding *enc, rb_encoding *enc2)
4422    {
4423        int fmode = *fmode_p;
4424
4425        if ((fmode & FMODE_READABLE) &&
4426            !enc2 &&
4427            !(fmode & FMODE_BINMODE) &&
4428            !rb_enc_asciicompat(enc ? enc : rb_default_external_encoding()))
4429            rb_raise(rb_eArgError, "ASCII incompatible encoding needs binmode");
4430
---Type <return> to continue, or q <return> to quit---
4431        if (!(fmode & FMODE_BINMODE) &&
4432            (ecflags & ECONV_NEWLINE_DECORATOR_MASK)) {
4433            fmode |= DEFAULT_TEXTMODE;
4434            *fmode_p = fmode;
4435        }
4436    #if !DEFAULT_TEXTMODE
4437        else if (!(ecflags & ECONV_NEWLINE_DECORATOR_MASK)) {
4438            fmode &= ~FMODE_TEXTMODE;
4439            *fmode_p = fmode;
4440        }
4441    #endif
4442    }
4443
4444    static void
4445    extract_binmode(VALUE opthash, int *fmode)
4446    {
4447        if (!NIL_P(opthash)) {
4448            VALUE v;
4449            v = rb_hash_aref(opthash, sym_textmode);
4450            if (!NIL_P(v) && RTEST(v))
4451                *fmode |= FMODE_TEXTMODE;
4452            v = rb_hash_aref(opthash, sym_binmode);
4453            if (!NIL_P(v) && RTEST(v))
---Type <return> to continue, or q <return> to quit---
4454                *fmode |= FMODE_BINMODE;
4455
4456            if ((*fmode & FMODE_BINMODE) && (*fmode & FMODE_TEXTMODE))
4457                rb_raise(rb_eArgError, "both textmode and binmode specified");
4458        }
4459    }
4460
4461    static void
4462    rb_io_extract_modeenc(VALUE *vmode_p, VALUE *vperm_p, VALUE opthash,
4463            int *oflags_p, int *fmode_p, convconfig_t *convconfig_p)
4464    {
4465        VALUE vmode;
4466        int oflags, fmode;
4467        rb_encoding *enc, *enc2;
4468        int ecflags;
4469        VALUE ecopts;
4470        int has_enc = 0, has_vmode = 0;
4471        VALUE intmode;
4472
4473        vmode = *vmode_p;
4474
4475        /* Set to defaults */
---Type <return> to continue, or q <return> to quit---
4476        rb_io_ext_int_to_encs(NULL, NULL, &enc, &enc2);
4477
4478      vmode_handle:
4479        if (NIL_P(vmode)) {
4480            fmode = FMODE_READABLE | DEFAULT_TEXTMODE;
4481            oflags = O_RDONLY;
4482        }
4483        else if (!NIL_P(intmode = rb_check_to_integer(vmode, "to_int"))) {
4484            vmode = intmode;
4485            oflags = NUM2INT(intmode);
4486            fmode = rb_io_oflags_fmode(oflags);
4487        }
4488        else {
4489            const char *p;
4490
4491            SafeStringValue(vmode);
4492            p = StringValueCStr(vmode);
4493            fmode = rb_io_modestr_fmode(p);
4494            oflags = rb_io_fmode_oflags(fmode);
4495            p = strchr(p, ':');
4496            if (p) {
4497                has_enc = 1;
4498                parse_mode_enc(p+1, &enc, &enc2, &fmode);
---Type <return> to continue, or q <return> to quit---
4499            }
4500            else {
4501                rb_encoding *e;
4502
4503                e = (fmode & FMODE_BINMODE) ? rb_ascii8bit_encoding() : NULL;
4504                rb_io_ext_int_to_encs(e, NULL, &enc, &enc2);
4505            }
4506        }
4507
4508        if (NIL_P(opthash)) {
4509            ecflags = (fmode & FMODE_READABLE) ?
4510                MODE_BTMODE(ECONV_DEFAULT_NEWLINE_DECORATOR,
4511                            0, ECONV_UNIVERSAL_NEWLINE_DECORATOR) : 0;
4512            ecopts = Qnil;
4513        }
4514        else {
4515            VALUE v;
4516            extract_binmode(opthash, &fmode);
4517    #ifdef O_BINARY
4518            if (fmode & FMODE_BINMODE)
4519                oflags |= O_BINARY;
4520    #endif
---Type <return> to continue, or q <return> to quit---
4521            if (!has_vmode) {
4522                v = rb_hash_aref(opthash, sym_mode);
4523                if (!NIL_P(v)) {
4524                    if (!NIL_P(vmode)) {
4525                        rb_raise(rb_eArgError, "mode specified twice");
4526                    }
4527                    has_vmode = 1;
4528                    vmode = v;
4529                    goto vmode_handle;
4530                }
4531            }
4532            v = rb_hash_aref(opthash, sym_perm);
4533            if (!NIL_P(v)) {
4534                if (vperm_p) {
4535                    if (!NIL_P(*vperm_p)) {
4536                        rb_raise(rb_eArgError, "perm specified twice");
4537                    }
4538                    *vperm_p = v;
4539                }
4540                else {
4541                    /* perm no use, just ignore */
4542                }
4543            }
---Type <return> to continue, or q <return> to quit---
4544            ecflags = (fmode & FMODE_READABLE) ?
4545                MODE_BTMODE(ECONV_DEFAULT_NEWLINE_DECORATOR,
4546                            0, ECONV_UNIVERSAL_NEWLINE_DECORATOR) : 0;
4547            ecflags = rb_econv_prepare_options(opthash, &ecopts, ecflags);
4548
4549            if (rb_io_extract_encoding_option(opthash, &enc, &enc2, &fmode)) {
4550                if (has_enc) {
4551                    rb_raise(rb_eArgError, "encoding specified twice");
4552                }
4553            }
4554        }
4555
4556        validate_enc_binmode(&fmode, ecflags, enc, enc2);
4557
4558        *vmode_p = vmode;
4559
4560        *oflags_p = oflags;
4561        *fmode_p = fmode;
4562        convconfig_p->enc = enc;
4563        convconfig_p->enc2 = enc2;
4564        convconfig_p->ecflags = ecflags;
4565        convconfig_p->ecopts = ecopts;
---Type <return> to continue, or q <return> to quit---
4566    }
4567
4568    struct sysopen_struct {
4569        VALUE fname;
4570        int oflags;
4571        mode_t perm;
4572    };
4573
4574    static VALUE
4575    sysopen_func(void *ptr)
4576    {
4577        const struct sysopen_struct *data = ptr;
4578        const char *fname = RSTRING_PTR(data->fname);
4579        return (VALUE)open(fname, data->oflags, data->perm);
4580    }
4581
4582    static inline int
4583    rb_sysopen_internal(struct sysopen_struct *data)
4584    {
4585        return (int)rb_thread_blocking_region(sysopen_func, data, RUBY_UBF_IO, 0);
4586    }
4587
---Type <return> to continue, or q <return> to quit---
4588    static int
4589    rb_sysopen(VALUE fname, int oflags, mode_t perm)
4590    {
4591        int fd;
4592        struct sysopen_struct data;
4593
4594    #ifdef O_BINARY
4595        oflags |= O_BINARY;
4596    #endif
4597        data.fname = rb_str_encode_ospath(fname);
4598        data.oflags = oflags;
4599        data.perm = perm;
4600
4601        fd = rb_sysopen_internal(&data);
4602        if (fd < 0) {
4603            if (errno == EMFILE || errno == ENFILE) {
4604                rb_gc();
4605                fd = rb_sysopen_internal(&data);
4606            }
4607            if (fd < 0) {
4608                rb_sys_fail(RSTRING_PTR(fname));
4609            }
4610        }
---Type <return> to continue, or q <return> to quit---
4611        UPDATE_MAXFD(fd);
4612        return fd;
4613    }
4614
4615    FILE *
4616    rb_fdopen(int fd, const char *modestr)
4617    {
4618        FILE *file;
4619
4620    #if defined(sun)
4621        errno = 0;
4622    #endif
4623        file = fdopen(fd, modestr);
4624        if (!file) {
4625            if (
4626    #if defined(sun)
4627                errno == 0 ||
4628    #endif
4629                errno == EMFILE || errno == ENFILE) {
4630                rb_gc();
4631    #if defined(sun)
4632                errno = 0;
4633    #endif
---Type <return> to continue, or q <return> to quit---
4634                file = fdopen(fd, modestr);
4635            }
4636            if (!file) {
4637    #ifdef _WIN32
4638                if (errno == 0) errno = EINVAL;
4639    #elif defined(sun)
4640                if (errno == 0) errno = EMFILE;
4641    #endif
4642                rb_sys_fail(0);
4643            }
4644        }
4645
4646        /* xxx: should be _IONBF?  A buffer in FILE may have trouble. */
4647    #ifdef USE_SETVBUF
4648        if (setvbuf(file, NULL, _IOFBF, 0) != 0)
4649            rb_warn("setvbuf() can't be honoured (fd=%d)", fd);
4650    #endif
4651        return file;
4652    }
4653
4654    static void
4655    io_check_tty(rb_io_t *fptr)
4656    {
---Type <return> to continue, or q <return> to quit---
4657        if (isatty(fptr->fd))
4658            fptr->mode |= FMODE_TTY|FMODE_DUPLEX;
4659    }
4660
4661    static VALUE rb_io_internal_encoding(VALUE);
4662    static void io_encoding_set(rb_io_t *, VALUE, VALUE, VALUE);
4663
4664    static int
4665    io_strip_bom(VALUE io)
4666    {
4667        int b1, b2, b3, b4;
4668        switch (b1 = FIX2INT(rb_io_getbyte(io))) {
4669          case 0xEF:
4670            b2 = FIX2INT(rb_io_getbyte(io));
4671            if (b2 == 0xBB) {
4672                b3 = FIX2INT(rb_io_getbyte(io));
4673                if (b3 == 0xBF) {
4674                    return rb_utf8_encindex();
4675                }
4676                rb_io_ungetbyte(io, INT2FIX(b3));
4677            }
4678            rb_io_ungetbyte(io, INT2FIX(b2));
4679            break;
---Type <return> to continue, or q <return> to quit---
4680
4681          case 0xFE:
4682            b2 = FIX2INT(rb_io_getbyte(io));
4683            if (b2 == 0xFF) {
4684                return rb_enc_find_index("UTF-16BE");
4685            }
4686            rb_io_ungetbyte(io, INT2FIX(b2));
4687            break;
4688
4689          case 0xFF:
4690            b2 = FIX2INT(rb_io_getbyte(io));
4691            if (b2 == 0xFE) {
4692                b3 = FIX2INT(rb_io_getbyte(io));
4693                if (b3 == 0) {
4694                    b4 = FIX2INT(rb_io_getbyte(io));
4695                    if (b4 == 0) {
4696                        return rb_enc_find_index("UTF-32LE");
4697                    }
4698                    rb_io_ungetbyte(io, INT2FIX(b4));
4699                }
4700                else {
4701                    rb_io_ungetbyte(io, INT2FIX(b3));
4702                    return rb_enc_find_index("UTF-16LE");
---Type <return> to continue, or q <return> to quit---
4703                }
4704                rb_io_ungetbyte(io, INT2FIX(b3));
4705            }
4706            rb_io_ungetbyte(io, INT2FIX(b2));
4707            break;
4708
4709          case 0:
4710            b2 = FIX2INT(rb_io_getbyte(io));
4711            if (b2 == 0) {
4712                b3 = FIX2INT(rb_io_getbyte(io));
4713                if (b3 == 0xFE) {
4714                    b4 = FIX2INT(rb_io_getbyte(io));
4715                    if (b4 == 0xFF) {
4716                        return rb_enc_find_index("UTF-32BE");
4717                    }
4718                    rb_io_ungetbyte(io, INT2FIX(b4));
4719                }
4720                rb_io_ungetbyte(io, INT2FIX(b3));
4721            }
4722            rb_io_ungetbyte(io, INT2FIX(b2));
4723            break;
4724        }
4725        rb_io_ungetbyte(io, INT2FIX(b1));
---Type <return> to continue, or q <return> to quit---
4726        return 0;
4727    }
4728
4729    static void
4730    io_set_encoding_by_bom(VALUE io)
4731    {
4732        int idx = io_strip_bom(io);
4733
4734        if (idx) {
4735            rb_io_t *fptr;
4736            GetOpenFile(io, fptr);
4737            io_encoding_set(fptr, rb_enc_from_encoding(rb_enc_from_index(idx)),
4738                    rb_io_internal_encoding(io), Qnil);
4739        }
4740    }
4741
4742    static VALUE
4743    rb_file_open_generic(VALUE io, VALUE filename, int oflags, int fmode, convconfig_t *convconfig, mode_t perm)
4744    {
4745        rb_io_t *fptr;
4746        convconfig_t cc;
---Type <return> to continue, or q <return> to quit---
4747        if (!convconfig) {
4748            /* Set to default encodings */
4749            rb_io_ext_int_to_encs(NULL, NULL, &cc.enc, &cc.enc2);
4750            cc.ecflags = 0;
4751            cc.ecopts = Qnil;
4752            convconfig = &cc;
4753        }
4754        validate_enc_binmode(&fmode, convconfig->ecflags,
4755                             convconfig->enc, convconfig->enc2);
4756
4757        MakeOpenFile(io, fptr);
4758        fptr->mode = fmode;
4759        fptr->encs = *convconfig;
4760        fptr->pathv = rb_str_new_frozen(filename);
4761        fptr->fd = rb_sysopen(fptr->pathv, oflags, perm);
4762        io_check_tty(fptr);
4763        if (fmode & FMODE_SETENC_BY_BOM) io_set_encoding_by_bom(io);
4764
4765        return io;
4766    }
4767
4768    static VALUE
4769    rb_file_open_internal(VALUE io, VALUE filename, const char *modestr)
---Type <return> to continue, or q <return> to quit---
4770    {
4771        int fmode = rb_io_modestr_fmode(modestr);
4772        const char *p = strchr(modestr, ':');
4773        convconfig_t convconfig;
4774
4775        if (p) {
4776            parse_mode_enc(p+1, &convconfig.enc, &convconfig.enc2, &fmode);
4777        }
4778        else {
4779            rb_encoding *e;
4780            /* Set to default encodings */
4781
4782            e = (fmode & FMODE_BINMODE) ? rb_ascii8bit_encoding() : NULL;
4783            rb_io_ext_int_to_encs(e, NULL, &convconfig.enc, &convconfig.enc2);
4784            convconfig.ecflags = 0;
4785            convconfig.ecopts = Qnil;
4786        }
4787
4788        return rb_file_open_generic(io, filename,
4789                rb_io_fmode_oflags(fmode),
4790                fmode,
4791                &convconfig,
---Type <return> to continue, or q <return> to quit---
4792                0666);
4793    }
4794
4795    VALUE
4796    rb_file_open_str(VALUE fname, const char *modestr)
4797    {
4798        FilePathValue(fname);
4799        return rb_file_open_internal(io_alloc(rb_cFile), fname, modestr);
4800    }
4801
4802    VALUE
4803    rb_file_open(const char *fname, const char *modestr)
4804    {
4805        return rb_file_open_internal(io_alloc(rb_cFile), rb_str_new_cstr(fname), modestr);
4806    }
4807
4808    #if defined(__CYGWIN__) || !defined(HAVE_FORK)
4809    static struct pipe_list {
4810        rb_io_t *fptr;
4811        struct pipe_list *next;
4812    } *pipe_list;
4813
---Type <return> to continue, or q <return> to quit---
4814    static void
4815    pipe_add_fptr(rb_io_t *fptr)
4816    {
4817        struct pipe_list *list;
4818
4819        list = ALLOC(struct pipe_list);
4820        list->fptr = fptr;
4821        list->next = pipe_list;
4822        pipe_list = list;
4823    }
4824
4825    static void
4826    pipe_del_fptr(rb_io_t *fptr)
4827    {
4828        struct pipe_list *list = pipe_list;
4829        struct pipe_list *tmp;
4830
4831        if (list->fptr == fptr) {
4832            pipe_list = list->next;
4833            free(list);
4834            return;
4835        }
4836
---Type <return> to continue, or q <return> to quit---
4837        while (list->next) {
4838            if (list->next->fptr == fptr) {
4839                tmp = list->next;
4840                list->next = list->next->next;
4841                free(tmp);
4842                return;
4843            }
4844            list = list->next;
4845        }
4846    }
4847
4848    static void
4849    pipe_atexit(void)
4850    {
4851        struct pipe_list *list = pipe_list;
4852        struct pipe_list *tmp;
4853
4854        while (list) {
4855            tmp = list->next;
4856            rb_io_fptr_finalize(list->fptr);
4857            list = tmp;
4858        }
4859    }
---Type <return> to continue, or q <return> to quit---
4860
4861    static void
4862    pipe_finalize(rb_io_t *fptr, int noraise)
4863    {
4864    #if !defined(HAVE_FORK) && !defined(_WIN32)
4865        int status = 0;
4866        if (fptr->stdio_file) {
4867            status = pclose(fptr->stdio_file);
4868        }
4869        fptr->fd = -1;
4870        fptr->stdio_file = 0;
4871        rb_last_status_set(status, fptr->pid);
4872    #else
4873        fptr_finalize(fptr, noraise);
4874    #endif
4875        pipe_del_fptr(fptr);
4876    }
4877    #endif
4878
4879    void
4880    rb_io_synchronized(rb_io_t *fptr)
4881    {
4882        rb_io_check_initialized(fptr);
---Type <return> to continue, or q <return> to quit---
4883        fptr->mode |= FMODE_SYNC;
4884    }
4885
4886    void
4887    rb_io_unbuffered(rb_io_t *fptr)
4888    {
4889        rb_io_synchronized(fptr);
4890    }
4891
4892    int
4893    rb_pipe(int *pipes)
4894    {
4895        int ret;
4896        ret = pipe(pipes);
4897        if (ret == -1) {
4898            if (errno == EMFILE || errno == ENFILE) {
4899                rb_gc();
4900                ret = pipe(pipes);
4901            }
4902        }
4903        if (ret == 0) {
4904            UPDATE_MAXFD(pipes[0]);
4905            UPDATE_MAXFD(pipes[1]);
---Type <return> to continue, or q <return> to quit---
4906        }
4907        return ret;
4908    }
4909
4910    #ifdef HAVE_FORK
4911    struct popen_arg {
4912        struct rb_exec_arg *execp;
4913        int modef;
4914        int pair[2];
4915        int write_pair[2];
4916    };
4917
4918    static void
4919    popen_redirect(struct popen_arg *p)
4920    {
4921        if ((p->modef & FMODE_READABLE) && (p->modef & FMODE_WRITABLE)) {
4922            close(p->write_pair[1]);
4923            if (p->write_pair[0] != 0) {
4924                dup2(p->write_pair[0], 0);
4925                close(p->write_pair[0]);
4926            }
4927            close(p->pair[0]);
4928            if (p->pair[1] != 1) {
---Type <return> to continue, or q <return> to quit---
4929                dup2(p->pair[1], 1);
4930                close(p->pair[1]);
4931            }
4932        }
4933        else if (p->modef & FMODE_READABLE) {
4934            close(p->pair[0]);
4935            if (p->pair[1] != 1) {
4936                dup2(p->pair[1], 1);
4937                close(p->pair[1]);
4938            }
4939        }
4940        else {
4941            close(p->pair[1]);
4942            if (p->pair[0] != 0) {
4943                dup2(p->pair[0], 0);
4944                close(p->pair[0]);
4945            }
4946        }
4947    }
4948
4949    void
4950    rb_close_before_exec(int lowfd, int maxhint, VALUE noclose_fds)
4951    {
---Type <return> to continue, or q <return> to quit---
4952        int fd, ret;
4953        int max = max_file_descriptor;
4954        if (max < maxhint)
4955            max = maxhint;
4956        for (fd = lowfd; fd <= max; fd++) {
4957            if (!NIL_P(noclose_fds) &&
4958                RTEST(rb_hash_lookup(noclose_fds, INT2FIX(fd))))
4959                continue;
4960    #ifdef FD_CLOEXEC
4961            ret = fcntl(fd, F_GETFD);
4962            if (ret != -1 && !(ret & FD_CLOEXEC)) {
4963                fcntl(fd, F_SETFD, ret|FD_CLOEXEC);
4964            }
4965    #else
4966            ret = close(fd);
4967    #endif
4968    #define CONTIGUOUS_CLOSED_FDS 20
4969            if (ret != -1) {
4970                if (max < fd + CONTIGUOUS_CLOSED_FDS)
4971                    max = fd + CONTIGUOUS_CLOSED_FDS;
4972            }
4973        }
4974    }
---Type <return> to continue, or q <return> to quit---
4975
4976    static int
4977    popen_exec(void *pp, char *errmsg, size_t errmsg_len)
4978    {
4979        struct popen_arg *p = (struct popen_arg*)pp;
4980
4981        rb_thread_atfork_before_exec();
4982        return rb_exec_err(p->execp, errmsg, errmsg_len);
4983    }
4984    #endif
4985
4986    static VALUE
4987    pipe_open(struct rb_exec_arg *eargp, VALUE prog, const char *modestr, int fmode, convconfig_t *convconfig)
4988    {
4989        rb_pid_t pid = 0;
4990        rb_io_t *fptr;
4991        VALUE port;
4992        rb_io_t *write_fptr;
4993        VALUE write_port;
4994    #if defined(HAVE_FORK)
4995        int status;
4996        struct popen_arg arg;
---Type <return> to continue, or q <return> to quit---
4997        char errmsg[80] = { '\0' };
4998    #elif defined(_WIN32)
4999        volatile VALUE argbuf;
5000        char **args = NULL;
5001        int pair[2], write_pair[2];
5002    #endif
5003    #if !defined(HAVE_FORK)
5004        struct rb_exec_arg sarg;
5005    #endif
5006        FILE *fp = 0;
5007        int fd = -1;
5008        int write_fd = -1;
5009        const char *cmd = 0;
5010        int argc;
5011        VALUE *argv;
5012
5013        if (prog)
5014            cmd = StringValueCStr(prog);
5015
5016        if (!eargp) {
5017            /* fork : IO.popen("-") */
5018            argc = 0;
5019            argv = 0;
---Type <return> to continue, or q <return> to quit---
5020        }
5021        else if (eargp->argc) {
5022            /* no shell : IO.popen([prog, arg0], arg1, ...) */
5023            argc = eargp->argc;
5024            argv = eargp->argv;
5025        }
5026        else {
5027            /* with shell : IO.popen(prog) */
5028            argc = 0;
5029            argv = 0;
5030        }
5031
5032    #if defined(HAVE_FORK)
5033        arg.execp = eargp;
5034        arg.modef = fmode;
5035        arg.pair[0] = arg.pair[1] = -1;
5036        arg.write_pair[0] = arg.write_pair[1] = -1;
5037        switch (fmode & (FMODE_READABLE|FMODE_WRITABLE)) {
5038          case FMODE_READABLE|FMODE_WRITABLE:
5039            if (rb_pipe(arg.write_pair) < 0)
5040                rb_sys_fail(cmd);
5041            if (rb_pipe(arg.pair) < 0) {
5042                int e = errno;
---Type <return> to continue, or q <return> to quit---
5043                close(arg.write_pair[0]);
5044                close(arg.write_pair[1]);
5045                errno = e;
5046                rb_sys_fail(cmd);
5047            }
5048            if (eargp) {
5049                rb_exec_arg_addopt(eargp, INT2FIX(0), INT2FIX(arg.write_pair[0]));
5050                rb_exec_arg_addopt(eargp, INT2FIX(1), INT2FIX(arg.pair[1]));
5051            }
5052            break;
5053          case FMODE_READABLE:
5054            if (rb_pipe(arg.pair) < 0)
5055                rb_sys_fail(cmd);
5056            if (eargp)
5057                rb_exec_arg_addopt(eargp, INT2FIX(1), INT2FIX(arg.pair[1]));
5058            break;
5059          case FMODE_WRITABLE:
5060            if (rb_pipe(arg.pair) < 0)
5061                rb_sys_fail(cmd);
5062            if (eargp)
---Type <return> to continue, or q <return> to quit---
5063                rb_exec_arg_addopt(eargp, INT2FIX(0), INT2FIX(arg.pair[0]));
5064            break;
5065          default:
5066            rb_sys_fail(cmd);
5067        }
5068        if (eargp) {
5069            rb_exec_arg_fixup(arg.execp);
5070            pid = rb_fork_err(&status, popen_exec, &arg, arg.execp->redirect_fds, errmsg, sizeof(errmsg));
5071        }
5072        else {
5073            fflush(stdin);          /* is it really needed? */
5074            pid = rb_fork(&status, 0, 0, Qnil);
5075            if (pid == 0) {         /* child */
5076                rb_thread_atfork();
5077                popen_redirect(&arg);
5078                rb_io_synchronized(RFILE(orig_stdout)->fptr);
5079                rb_io_synchronized(RFILE(orig_stderr)->fptr);
5080                return Qnil;
5081            }
5082        }
5083
---Type <return> to continue, or q <return> to quit---
5084        /* parent */
5085        if (pid == -1) {
5086            int e = errno;
5087            close(arg.pair[0]);
5088            close(arg.pair[1]);
5089            if ((fmode & (FMODE_READABLE|FMODE_WRITABLE)) == (FMODE_READABLE|FMODE_WRITABLE)) {
5090                close(arg.write_pair[0]);
5091                close(arg.write_pair[1]);
5092            }
5093            errno = e;
5094            if (errmsg[0])
5095                rb_sys_fail(errmsg);
5096            rb_sys_fail(cmd);
5097        }
5098        if ((fmode & FMODE_READABLE) && (fmode & FMODE_WRITABLE)) {
5099            close(arg.pair[1]);
5100            fd = arg.pair[0];
5101            close(arg.write_pair[0]);
5102            write_fd = arg.write_pair[1];
5103        }
5104        else if (fmode & FMODE_READABLE) {
5105            close(arg.pair[1]);
---Type <return> to continue, or q <return> to quit---
5106            fd = arg.pair[0];
5107        }
5108        else {
5109            close(arg.pair[0]);
5110            fd = arg.pair[1];
5111        }
5112    #elif defined(_WIN32)
5113        if (argc) {
5114            int i;
5115
5116            if (argc >= (int)(FIXNUM_MAX / sizeof(char *))) {
5117                rb_raise(rb_eArgError, "too many arguments");
5118            }
5119            argbuf = rb_str_tmp_new((argc+1) * sizeof(char *));
5120            args = (void *)RSTRING_PTR(argbuf);
5121            for (i = 0; i < argc; ++i) {
5122                args[i] = StringValueCStr(argv[i]);
5123            }
5124            args[i] = NULL;
5125        }
5126        switch (fmode & (FMODE_READABLE|FMODE_WRITABLE)) {
5127          case FMODE_READABLE|FMODE_WRITABLE:
5128            if (rb_pipe(write_pair) < 0)
---Type <return> to continue, or q <return> to quit---
5129                rb_sys_fail(cmd);
5130            if (rb_pipe(pair) < 0) {
5131                int e = errno;
5132                close(write_pair[0]);
5133                close(write_pair[1]);
5134                errno = e;
5135                rb_sys_fail(cmd);
5136            }
5137            if (eargp) {
5138                rb_exec_arg_addopt(eargp, INT2FIX(0), INT2FIX(write_pair[0]));
5139                rb_exec_arg_addopt(eargp, INT2FIX(1), INT2FIX(pair[1]));
5140            }
5141            break;
5142          case FMODE_READABLE:
5143            if (rb_pipe(pair) < 0)
5144                rb_sys_fail(cmd);
5145            if (eargp)
5146                rb_exec_arg_addopt(eargp, INT2FIX(1), INT2FIX(pair[1]));
5147            break;
5148          case FMODE_WRITABLE:
5149            if (rb_pipe(pair) < 0)
5150                rb_sys_fail(cmd);
---Type <return> to continue, or q <return> to quit---
5151            if (eargp)
5152                rb_exec_arg_addopt(eargp, INT2FIX(0), INT2FIX(pair[0]));
5153            break;
5154          default:
5155            rb_sys_fail(cmd);
5156        }
5157        if (eargp) {
5158            rb_exec_arg_fixup(eargp);
5159            rb_run_exec_options(eargp, &sarg);
5160        }
5161        while ((pid = (args ?
5162                       rb_w32_aspawn(P_NOWAIT, cmd, args) :
5163                       rb_w32_spawn(P_NOWAIT, cmd, 0))) == -1) {
5164            /* exec failed */
5165            switch (errno) {
5166              case EAGAIN:
5167    #if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
5168              case EWOULDBLOCK:
5169    #endif
5170                rb_thread_sleep(1);
5171                break;
5172              default:
5173                {
---Type <return> to continue, or q <return> to quit---
5174                    int e = errno;
5175                    if (eargp)
5176                        rb_run_exec_options(&sarg, NULL);
5177                    close(pair[0]);
5178                    close(pair[1]);
5179                    if ((fmode & (FMODE_READABLE|FMODE_WRITABLE)) == (FMODE_READABLE|FMODE_WRITABLE)) {
5180                        close(write_pair[0]);
5181                        close(write_pair[1]);
5182                    }
5183                    errno = e;
5184                    rb_sys_fail(cmd);
5185                }
5186                break;
5187            }
5188        }
5189
5190        RB_GC_GUARD(argbuf);
5191
5192        if (eargp)
5193            rb_run_exec_options(&sarg, NULL);
5194        if ((fmode & FMODE_READABLE) && (fmode & FMODE_WRITABLE)) {
5195            close(pair[1]);
---Type <return> to continue, or q <return> to quit---
5196            fd = pair[0];
5197            close(write_pair[0]);
5198            write_fd = write_pair[1];
5199        }
5200        else if (fmode & FMODE_READABLE) {
5201            close(pair[1]);
5202            fd = pair[0];
5203        }
5204        else {
5205            close(pair[0]);
5206            fd = pair[1];
5207        }
5208    #else
5209        if (argc) {
5210            prog = rb_ary_join(rb_ary_new4(argc, argv), rb_str_new2(" "));
5211            cmd = StringValueCStr(prog);
5212        }
5213        if (eargp) {
5214            rb_exec_arg_fixup(eargp);
5215            rb_run_exec_options(eargp, &sarg);
5216        }
5217        fp = popen(cmd, modestr);
5218        if (eargp)
---Type <return> to continue, or q <return> to quit---
5219            rb_run_exec_options(&sarg, NULL);
5220        if (!fp) rb_sys_fail(RSTRING_PTR(prog));
5221        fd = fileno(fp);
5222    #endif
5223
5224        port = io_alloc(rb_cIO);
5225        MakeOpenFile(port, fptr);
5226        fptr->fd = fd;
5227        fptr->stdio_file = fp;
5228        fptr->mode = fmode | FMODE_SYNC|FMODE_DUPLEX;
5229        if (convconfig) {
5230            fptr->encs = *convconfig;
5231        }
5232        else if (NEED_NEWLINE_DECORATOR_ON_READ(fptr)) {
5233            fptr->encs.ecflags |= ECONV_UNIVERSAL_NEWLINE_DECORATOR;
5234        }
5235        fptr->pid = pid;
5236
5237        if (0 <= write_fd) {
5238            write_port = io_alloc(rb_cIO);
5239            MakeOpenFile(write_port, write_fptr);
5240            write_fptr->fd = write_fd;
5241            write_fptr->mode = (fmode & ~FMODE_READABLE)| FMODE_SYNC|FMODE_D---Type <return> to continue, or q <return> to quit---
UPLEX;
5242            fptr->mode &= ~FMODE_WRITABLE;
5243            fptr->tied_io_for_writing = write_port;
5244            rb_ivar_set(port, rb_intern("@tied_io_for_writing"), write_port);
5245        }
5246
5247    #if defined (__CYGWIN__) || !defined(HAVE_FORK)
5248        fptr->finalize = pipe_finalize;
5249        pipe_add_fptr(fptr);
5250    #endif
5251        return port;
5252    }
5253
5254    static VALUE
5255    pipe_open_v(int argc, VALUE *argv, const char *modestr, int fmode, convconfig_t *convconfig)
5256    {
5257        VALUE prog;
5258        struct rb_exec_arg earg;
5259        prog = rb_exec_arg_init(argc, argv, FALSE, &earg);
   0x62d7aebf <+207>:   lea    0x5c(%esp),%edi
   0x62d7aec3 <+211>:   movl   $0x0,0x8(%esp)
---Type <return> to continue, or q <return> to quit---
   0x62d7aecb <+219>:   mov    %edi,0xc(%esp)
   0x62d7aecf <+223>:   mov    %eax,0x4(%esp)
   0x62d7aed3 <+227>:   mov    %ebp,(%esp)
   0x62d7aed6 <+230>:   mov    %ecx,0x2c(%esp)
   0x62d7aeda <+234>:   call   0x62dbf910 <rb_exec_arg_init>

5260        return pipe_open(&earg, prog, modestr, fmode, convconfig);
   0x62d7aedf <+239>:   mov    0x2c(%esp),%ecx
   0x62d7aee6 <+246>:   mov    %eax,%edx
   0x62d7aee8 <+248>:   mov    %edi,%eax
   0x62d7aeea <+250>:   call   0x62d79d00 <pipe_open>
   0x62d7aef2 <+258>:   mov    %eax,%ebx

5261    }
5262
5263    static VALUE
5264    pipe_open_s(VALUE prog, const char *modestr, int fmode, convconfig_t *convconfig)
5265    {
5266        const char *cmd = RSTRING_PTR(prog);
5267        int argc = 1;
5268        VALUE *argv = &prog;
5269        struct rb_exec_arg earg;
---Type <return> to continue, or q <return> to quit---
5270
5271        if (RSTRING_LEN(prog) == 1 && cmd[0] == '-') {
5272    #if !defined(HAVE_FORK)
5273            rb_raise(rb_eNotImpError,
5274                     "fork() function is unimplemented on this machine");
5275    #endif
5276            return pipe_open(0, 0, modestr, fmode, convconfig);
5277        }
5278
5279        rb_exec_arg_init(argc, argv, TRUE, &earg);
5280        return pipe_open(&earg, prog, modestr, fmode, convconfig);
5281    }
5282
5283    /*
5284     *  call-seq:
5285     *     IO.popen(cmd, mode="r" [, opt])               -> io
5286     *     IO.popen(cmd, mode="r" [, opt]) {|io| block } -> obj
5287     *
5288     *  Runs the specified command as a subprocess; the subprocess's
5289     *  standard input and output will be connected to the returned
5290     *  <code>IO</code> object.
5291     *
5292     *  The PID of the started process can be obtained by IO#pid method.
---Type <return> to continue, or q <return> to quit---
5293     *
5294     *  _cmd_ is a string or an array as follows.
5295     *
5296     *    cmd:
5297     *      "-"                                      : fork
5298     *      commandline                              : command line string which is passed to a shell
5299     *      [env, cmdname, arg1, ..., opts]          : command name and zero or more arguments (no shell)
5300     *      [env, [cmdname, argv0], arg1, ..., opts] : command name, argv[0] and zero or more arguments (no shell)
5301     *    (env and opts are optional.)
5302     *
5303     *  If _cmd_ is a +String+ ``<code>-</code>'',
5304     *  then a new instance of Ruby is started as the subprocess.
5305     *
5306     *  If <i>cmd</i> is an +Array+ of +String+,
5307     *  then it will be used as the subprocess's +argv+ bypassing a shell.
5308     *  The array can contains a hash at first for environments and
5309     *  a hash at last for options similar to <code>spawn</code>.
5310     *
5311     *  The default mode for the new file object is ``r'',
5312     *  but <i>mode</i> may be set to any of the modes listed in the descrip---Type <return> to continue, or q <return> to quit---
tion for class IO.
5313     *  The last argument <i>opt</i> qualifies <i>mode</i>.
5314     *
5315     *    # set IO encoding
5316     *    IO.popen("nkf -e filename", :external_encoding=>"EUC-JP") {|nkf_io|
5317     *      euc_jp_string = nkf_io.read
5318     *    }
5319     *
5320     *    # merge standard output and standard error using
5321     *    # spawn option.  See the document of Kernel.spawn.
5322     *    IO.popen(["ls", "/", :err=>[:child, :out]]) {|ls_io|
5323     *      ls_result_with_error = ls_io.read
5324     *    }
5325     *
5326     *  Raises exceptions which <code>IO.pipe</code> and
5327     *  <code>Kernel.spawn</code> raise.
5328     *
5329     *  If a block is given, Ruby will run the command as a child connected
5330     *  to Ruby with a pipe. Ruby's end of the pipe will be passed as a
5331     *  parameter to the block.
5332     *  At the end of block, Ruby close the pipe and sets <code>$?</code>.
5333     *  In this case <code>IO.popen</code> returns
---Type <return> to continue, or q <return> to quit---
5334     *  the value of the block.
5335     *
5336     *  If a block is given with a _cmd_ of ``<code>-</code>'',
5337     *  the block will be run in two separate processes: once in the parent,
5338     *  and once in a child. The parent process will be passed the pipe
5339     *  object as a parameter to the block, the child version of the block
5340     *  will be passed <code>nil</code>, and the child's standard in and
5341     *  standard out will be connected to the parent through the pipe. Not
5342     *  available on all platforms.
5343     *
5344     *     f = IO.popen("uname")
5345     *     p f.readlines
5346     *     f.close
5347     *     puts "Parent is #{Process.pid}"
5348     *     IO.popen("date") { |f| puts f.gets }
5349     *     IO.popen("-") {|f| $stderr.puts "#{Process.pid} is here, f is #{f.inspect}"}
5350     *     p $?
5351     *     IO.popen(%w"sed -e s|^|<foo>| -e s&$&;zot;&", "r+") {|f|
5352     *       f.puts "bar"; f.close_write; puts f.gets
5353     *     }
5354     *
---Type <return> to continue, or q <return> to quit---
5355     *  <em>produces:</em>
5356     *
5357     *     ["Linux\n"]
5358     *     Parent is 21346
5359     *     Thu Jan 15 22:41:19 JST 2009
5360     *     21346 is here, f is #<IO:fd 3>
5361     *     21352 is here, f is nil
5362     *     #<Process::Status: pid 21352 exit 0>
5363     *     <foo>bar;zot;
5364     */
5365
5366    static VALUE
5367    rb_io_s_popen(int argc, VALUE *argv, VALUE klass)
5368    {
   0x62d7adf0 <+0>:     push   %ebp
   0x62d7adf1 <+1>:     push   %edi
   0x62d7adf2 <+2>:     push   %esi
   0x62d7adf3 <+3>:     push   %ebx
   0x62d7adf4 <+4>:     sub    $0x7c,%esp

5369        const char *modestr;
5370        VALUE pname, pmode, port, tmp, opt;
5371        int oflags, fmode;
---Type <return> to continue, or q <return> to quit---
5372        convconfig_t convconfig;
5373
5374        argc = rb_scan_args(argc, argv, "11:", &pname, &pmode, &opt);
   0x62d7adf7 <+7>:     lea    0x40(%esp),%eax
   0x62d7adfb <+11>:    movl   $0x62ea0c34,0x8(%esp)
   0x62d7ae03 <+19>:    mov    %eax,0x14(%esp)
   0x62d7ae07 <+23>:    mov    0x94(%esp),%eax
   0x62d7ae0e <+30>:    lea    0x3c(%esp),%edi
   0x62d7ae12 <+34>:    lea    0x38(%esp),%esi
   0x62d7ae16 <+38>:    mov    %edi,0x10(%esp)
   0x62d7ae1a <+42>:    mov    %esi,0xc(%esp)
   0x62d7ae22 <+50>:    mov    %eax,0x4(%esp)
   0x62d7ae26 <+54>:    mov    0x90(%esp),%eax
   0x62d7ae2d <+61>:    mov    %eax,(%esp)
   0x62d7ae30 <+64>:    call   0x62d29510 <rb_scan_args>

5375
5376        rb_io_extract_modeenc(&pmode, 0, opt, &oflags, &fmode, &convconfig);
   0x62d7ae1e <+46>:    lea    0x4c(%esp),%ebx
   0x62d7ae35 <+69>:    mov    0x40(%esp),%ecx
   0x62d7ae39 <+73>:    lea    0x48(%esp),%eax
   0x62d7ae3d <+77>:    mov    %eax,0x4(%esp)
---Type <return> to continue, or q <return> to quit---
   0x62d7ae41 <+81>:    lea    0x44(%esp),%eax
   0x62d7ae45 <+85>:    xor    %edx,%edx
   0x62d7ae47 <+87>:    mov    %eax,(%esp)
   0x62d7ae4a <+90>:    mov    %edi,%eax
   0x62d7ae4c <+92>:    mov    %ebx,0x8(%esp)
   0x62d7ae50 <+96>:    call   0x62d79190 <rb_io_extract_modeenc>

5377        modestr = rb_io_oflags_modestr(oflags);
   0x62d7ae55 <+101>:   mov    0x44(%esp),%eax

5378
5379        tmp = rb_check_array_type(pname);
   0x62d7ae76 <+134>:   mov    0x38(%esp),%eax
   0x62d7ae7a <+138>:   mov    %eax,(%esp)
   0x62d7ae7d <+141>:   call   0x62d0e770 <rb_check_array_type>

5380        if (!NIL_P(tmp)) {
   0x62d7ae82 <+146>:   cmp    $0x4,%eax
   0x62d7ae85 <+149>:   je     0x62d7afb0 <rb_io_s_popen+448>

5381            long len = RARRAY_LEN(tmp);
   0x62d7ae8b <+155>:   mov    (%eax),%ebp
   0x62d7ae8d <+157>:   test   $0x2000,%ebp
---Type <return> to continue, or q <return> to quit---
   0x62d7ae93 <+163>:   jne    0x62d7af40 <rb_io_s_popen+336>
   0x62d7ae99 <+169>:   mov    0x8(%eax),%ebp
   0x62d7af40 <+336>:   shr    $0xf,%ebp
   0x62d7af43 <+339>:   and    $0x3,%ebp
   0x62d7af46 <+342>:   jmp    0x62d7ae9c <rb_io_s_popen+172>
   0x62d7af4b <+347>:   nop
   0x62d7af4c <+348>:   lea    0x0(%esi,%eiz,1),%esi

5382    #if SIZEOF_LONG > SIZEOF_INT
5383            if (len > INT_MAX) {
5384                rb_raise(rb_eArgError, "too many arguments");
5385            }
5386    #endif
5387            tmp = rb_ary_dup(tmp);
   0x62d7ae9c <+172>:   mov    %eax,(%esp)
   0x62d7ae9f <+175>:   call   0x62d117a0 <rb_ary_dup>
   0x62d7aea8 <+184>:   mov    %eax,%esi

5388            RBASIC(tmp)->klass = 0;
   0x62d7aeaa <+186>:   movl   $0x0,0x4(%eax)

5389            port = pipe_open_v((int)len, RARRAY_PTR(tmp), modestr, fmode, &convconfig);
---Type <return> to continue, or q <return> to quit---
   0x62d7aea4 <+180>:   mov    0x48(%esp),%ecx
   0x62d7aeb1 <+193>:   testl  $0x2000,(%esi)
   0x62d7aeb7 <+199>:   lea    0x8(%eax),%eax
   0x62d7aeba <+202>:   jne    0x62d7aebf <rb_io_s_popen+207>
   0x62d7aebc <+204>:   mov    0x10(%esi),%eax
   0x62d7aee3 <+243>:   mov    %ebx,(%esp)

5390            rb_ary_clear(tmp);
   0x62d7aeef <+255>:   mov    %esi,(%esp)
   0x62d7aef4 <+260>:   call   0x62d08da0 <rb_ary_clear>

5391        }
5392        else {
5393            SafeStringValue(pname);
   0x62d7afb0 <+448>:   mov    %esi,(%esp)

   0x62d7afb3 <+451>:   call   0x62e16740 <rb_string_value>
   0x62d7afb8 <+456>:   mov    0x38(%esp),%eax
   0x62d7afbc <+460>:   mov    %eax,(%esp)

   0x62d7afbf <+463>:   call   0x62dfea50 <rb_check_safe_obj>

5394            port = pipe_open_s(pname, modestr, fmode, &convconfig);
   0x62d7afc4 <+468>:   mov    0x48(%esp),%edx
   0x62d7afc8 <+472>:   mov    %ebx,%ecx
---Type <return> to continue, or q <return> to quit---
   0x62d7afca <+474>:   mov    0x38(%esp),%eax
   0x62d7afce <+478>:   call   0x62d7a410 <pipe_open_s>
   0x62d7afd3 <+483>:   mov    %eax,%ebx
   0x62d7afd5 <+485>:   jmp    0x62d7aef9 <rb_io_s_popen+265>
   0x62d7afda <+490>:   lea    0x0(%esi),%esi

5395        }
5396        if (NIL_P(port)) {
   0x62d7aef9 <+265>:   cmp    $0x4,%ebx
   0x62d7aefc <+268>:   je     0x62d7af50 <rb_io_s_popen+352>

5397            /* child */
5398            if (rb_block_given_p()) {
   0x62d7af50 <+352>:   call   0x62d4aea0 <rb_block_given_p>
   0x62d7af55 <+357>:   test   %eax,%eax
   0x62d7af57 <+359>:   je     0x62d7af2f <rb_io_s_popen+319>

5399                rb_yield(Qnil);
   0x62d7af59 <+361>:   movl   $0x4,(%esp)
   0x62d7af60 <+368>:   call   0x62e6f310 <rb_yield>

5400                rb_io_flush(rb_stdout);
   0x62d7af65 <+373>:   mov    0x62f3e850,%eax
---Type <return> to continue, or q <return> to quit---
   0x62d7af6a <+378>:   mov    %eax,(%esp)
   0x62d7af6d <+381>:   call   0x62d70830 <rb_io_flush>

5401                rb_io_flush(rb_stderr);
   0x62d7af72 <+386>:   mov    0x62f3e800,%eax
   0x62d7af77 <+391>:   mov    %eax,(%esp)
   0x62d7af7a <+394>:   call   0x62d70830 <rb_io_flush>

5402                _exit(0);
   0x62d7af7f <+399>:   movl   $0x0,(%esp)
   0x62d7af86 <+406>:   call   0x62e97338 <_exit>
   0x62d7af8b <+411>:   nop
   0x62d7af8c <+412>:   lea    0x0(%esi,%eiz,1),%esi

5403            }
5404            return Qnil;
5405        }
5406        RBASIC(port)->klass = klass;
   0x62d7aefe <+270>:   mov    0x98(%esp),%eax
   0x62d7af05 <+277>:   mov    %eax,0x4(%ebx)

5407        if (rb_block_given_p()) {
   0x62d7af08 <+280>:   call   0x62d4aea0 <rb_block_given_p>
---Type <return> to continue, or q <return> to quit---
   0x62d7af0d <+285>:   test   %eax,%eax
   0x62d7af0f <+287>:   je     0x62d7af2f <rb_io_s_popen+319>

5408            return rb_ensure(rb_yield, port, io_close, port);
   0x62d7af11 <+289>:   mov    %ebx,0xc(%esp)
   0x62d7af15 <+293>:   mov    %ebx,0x4(%esp)
   0x62d7af19 <+297>:   movl   $0x62d6aac0,0x8(%esp)
   0x62d7af21 <+305>:   movl   $0x62e6f310,(%esp)
   0x62d7af28 <+312>:   call   0x62d4b340 <rb_ensure>
=> 0x62d7af2d <+317>:   mov    %eax,%ebx

5409        }
5410        return port;
5411    }
   0x62d7af2f <+319>:   add    $0x7c,%esp
   0x62d7af32 <+322>:   mov    %ebx,%eax
   0x62d7af34 <+324>:   pop    %ebx
   0x62d7af35 <+325>:   pop    %esi
   0x62d7af36 <+326>:   pop    %edi
   0x62d7af37 <+327>:   pop    %ebp
   0x62d7af38 <+328>:   ret
   0x62d7af39 <+329>:   lea    0x0(%esi,%eiz,1),%esi
Comment 10 Kai Tietz 2011-06-29 20:20:21 UTC
Please stop to reopen this bug. It isn't related to gcc at all. I just rebuild a gcc 4.7 (trunk) version for i686-w64-mingw32 and test passes as expected (with crash after trying to leave main function).
This issue is related to mingw.org's header-set and handling of setjmp.  As I mentioned before, this is caused by a missing (undocumented) argument for setjmp. You disassembly of this function shows that msvcrt's setjmp function tries to read this hidden argument and just by accident it is here the place of ebp.
See on msdn the user-comment about setjmp.