PATCH: for Serious v3 shared library bug

Loren James Rittle rittle@latour.rsch.comm.mot.com
Tue Jan 2 14:54:00 GMT 2001


In article <u9u27khewu.fsf@casey.cambridge.redhat.com>,
Jason Merrill <jason@redhat.com> writes:

> I discovered recently that linking against shared v3, even if I don't use
> any of the C++ library bits, suppresses the usual flush of the standard
> files on exit().  Testcase:

>   #include <stdio.h>
>   int main () { printf ("Hi\n"); }

> Compile that, link against shared v3 and run it with

>   ./a.out|cat

FYI: I too have seen this problem (in my case, on *-*-freebsd).  I
believe that it is also related to the failure seen in the
27_io/filebuf_members.cc test case.  Your marking it as serious caused
me to finally investigate a proper fix.

Using truss, I see that the program is attempting to flush the buffer
but the stdout file descriptor has already been closed.  Note the
order:

$ truss a.out|cat
[...]
close(1)                                         = 0 (0x0)
close(0)                                         = 0 (0x0)
close(2)                                         = 0 (0x0)
write(1,0x804f000,3)                             ERR#9 'Bad file descriptor'
exit(0x0)                                       process exit, rval = 0

$ truss a.out
[...]
Hi
write(1,0x804ec00,3)                             = 3 (0x3)
sigprocmask(0x1,0x2805b700,0xbfbfe814)           = 0 (0x0)
sigprocmask(0x3,0x2805b710,0x0)                  = 0 (0x0)
close(1)                                         = 0 (0x0)
close(0)                                         = 0 (0x0)
close(2)                                         = 0 (0x0)
exit(0x0)                                       process exit, rval = 0

The reason this doesn't happen with the static link is that less
global constructors run than in the shared case.  For example, cout is
never setup in the static link situation for your test case (since it
is never referenced?) thus the related destructor never runs.  I offer
no opinion as to whether it is right or wrong for those libstdc++
constructors to only run unconditionally for the shared link.

The problem is that fdopen() is used in c_io_stdio.cc to implement
this (non-standard) entry point:

  template<typename _CharT>
    __basic_file<_CharT>*
    __basic_file<_CharT>::sys_open(int __fd, ios_base::openmode __mode)

According to POSIX, as conveyed in my BSD man pages, when fclose() is
called on the value returned from fdopen(__fd, ...), __fd is also closed.

Whereas in the implementation of that entry point in c_io_libio.cc,
the raw file descriptor is plainly copied and a libio flag,
_IO_DELETE_DONT_CLOSE, is set.

I include a patch below that fixes the testsuite test case along with
your test case on any system where it is possible with standard POSIX
calls.  libstdc++-v3 rebuilt and checked on i386-*-freebsd4.2.  Some
will not like this approach since it uses additional file descriptors
to handle the problem.  However, I see no other portable way to handle
this issue and I know of nothing in the C++ standard the precludes
this implementation (but note that cin, cout and cerr are effectively
bound to file descriptors 3, 4 and 5 on a typical UNIX system).

If it is deemed undesirable to have six descriptors allocated instead
of three in the typical case, then something could be done.  I think
it would be possible to write portable special cases for file
descriptors 0, 1 and 2 since their mapping to stdin, stdout and stderr
respectively is already assumed elsewhere in the libstdc++-v3
implementation (in particular, see ios_base::Init::Init() in
src/ios.cc).  In this case, the dup()/fdopen() path would be replaced
by code that sets _M_cfile to stdin whenever __fd == 0, to stdout
whenever __fd == 1 and to stderr whenever __fd == 2.  In addition,
__basic_file<_CharT>::close() could be modified to avoid the fclose()
in those same special cases.

Regards,
Loren

2001-01-02  Loren J. Rittle  <ljrittle@acm.org>

	* config/c_io_stdio.cc (__basic_file<_CharT>::sys_open): On
	systems that support it, call dup() before fdopen().

Index: config/c_io_stdio.cc
===================================================================
RCS file: /cvs/gcc/egcs/libstdc++-v3/config/c_io_stdio.cc,v
retrieving revision 1.2
diff -c -r1.2 c_io_stdio.cc
*** c_io_stdio.cc	2000/11/22 06:37:34	1.2
--- c_io_stdio.cc	2001/01/02 21:48:13
***************
*** 32,37 ****
--- 32,40 ----
  //
  
  #include <bits/basic_file.h>
+ #if _GLIBCPP_HAVE_UNISTD_H
+ #include <unistd.h>
+ #endif
  
  namespace std {
  
***************
*** 96,101 ****
--- 99,109 ----
  
        if (!this->is_open())
  	{
+ #if _GLIBCPP_HAVE_UNISTD_H
+ 	  __fd = dup (__fd);
+ 	  if (__fd == -1)
+ 	    return __ret;
+ #endif
  	  if ((_M_cfile = fdopen(__fd, __c_mode)))
  	    {
  	      _M_fileno = __fd;


More information about the Libstdc++ mailing list