Bug 85222 - [7 Regression] ABI breakage of __throw_ios_failure by r244498
Summary: [7 Regression] ABI breakage of __throw_ios_failure by r244498
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: libstdc++ (show other bugs)
Version: 7.3.1
: P2 normal
Target Milestone: 7.4
Assignee: Jonathan Wakely
URL:
Keywords: ABI, patch
Depends on:
Blocks: 66145
  Show dependency treegraph
 
Reported: 2018-04-05 07:38 UTC by Richard Biener
Modified: 2018-08-08 15:40 UTC (History)
3 users (show)

See Also:
Host:
Target:
Build:
Known to work: 6.4.0
Known to fail: 7.1.0, 8.0
Last reconfirmed: 2018-04-09 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Richard Biener 2018-04-05 07:38:54 UTC
A program compiled with a GCC defaulting to the old ABI
(like GCC 4.8) now fails to run with a dual-ABI libstdc++:

#include <iostream>
#include <fstream>
using namespace std;
int main ()
{
  std::ifstream pstats;
  pstats.exceptions(ifstream::failbit | ifstream::badbit | ifstream::eofbit);
  try {
      printf("\n Opening file : /proc/0/stat ");
      pstats.open("/proc/0/stat");
  }
  catch(ifstream::failure e)
    {
      printf("\n!!Caught ifstream exception!!\n");
      if(pstats.is_open()) {
          pstats.close();
      }
    }
  return 0;
}
tmp> g++-4.8 t.C
tmp> ./a.out 

terminate called after throwing an instance of 'std::ios_base::failure[abi:cxx11]'
  what():  basic_ios::clear: iostream error
Aborted (core dumped)
tmp> LD_LIBRARY_PATH=/space/rguenther/install/gcc-6.4/lib64 ./a.out 

 Opening file : /proc/0/stat 
!!Caught ifstream exception!!

This is because of the documented change of throwing the C++11 errors from
the libraries I/O routines in the attempt to fix PR66145.  This in turn
breaks any old programs trying to catch such exceptions thrown by the
library.

__throw_ios_failure, while being exported, isn't called by the testcase
above but just by libstdc++ but the program is rightfully expecting to
be able to catch ifstream::failure.

A workaround is to use a libstdc++ built with --disable-libstdcxx-dual-abi
but that of course makes it incompatible with any programs using the new ABI.
Comment 1 Jonathan Wakely 2018-04-05 07:40:34 UTC
(In reply to Richard Biener from comment #0)
> A workaround is to use a libstdc++ built with --disable-libstdcxx-dual-abi

Or to catch std::exception&
Comment 2 Richard Biener 2018-04-05 07:44:05 UTC
(In reply to Jonathan Wakely from comment #1)
> (In reply to Richard Biener from comment #0)
> > A workaround is to use a libstdc++ built with --disable-libstdcxx-dual-abi
> 
> Or to catch std::exception&

That isn't a workaround for existing binaries.
Comment 3 Richard Biener 2018-04-05 08:02:48 UTC
Do you know of any other exception type affected by the c++11 vs. old ABI issue or does the entire I/O hierarchy only ever throw exactly ios_base::failure?

So a workaround would be to marshal these somehow in the C++ EH personality
routine?  The c++11 variant seems to be a superset feature-wise (apart from
the changed inheritance), so constructing (in-place?!) the c++98 variant
once we hit a filter for c++98 ios_base::failure with an EH object of
type ios_base::failure[c++11] would "work"?
Comment 4 Jonathan Wakely 2018-04-05 08:33:14 UTC
(In reply to Richard Biener from comment #3)
> Do you know of any other exception type affected by the c++11 vs. old ABI
> issue or does the entire I/O hierarchy only ever throw exactly
> ios_base::failure?

That's the only one.

> So a workaround would be to marshal these somehow in the C++ EH personality
> routine?  The c++11 variant seems to be a superset feature-wise (apart from
> the changed inheritance), so constructing (in-place?!) the c++98 variant
> once we hit a filter for c++98 ios_base::failure with an EH object of
> type ios_base::failure[c++11] would "work"?

Until you try to rethrow it and catch it as the new type again.

This approach was considered, and deemed not worth the complexity (and additional performance hit that would affect every 'catch' in every program that has to check if the catch handler is for ios::failure).

It would be possible to make __throw_ios_failure() throw:

struct enhanced_failure : std::ios::failure {
  unsigned char buf[sizeof old ios::failure];
};

This can be caught as the new type, and when there's an attempt to catch it as the old type, construct it in the buffer and catch the object in the buffer. Constructing it lazily can introduce a race condition, so it might be better to always pre-propulate the buffer, just in case anybody wants to catch the old type.
Comment 5 rguenther@suse.de 2018-04-05 08:47:44 UTC
On Thu, 5 Apr 2018, redi at gcc dot gnu.org wrote:

> https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85222
> 
> --- Comment #4 from Jonathan Wakely <redi at gcc dot gnu.org> ---
> (In reply to Richard Biener from comment #3)
> > Do you know of any other exception type affected by the c++11 vs. old ABI
> > issue or does the entire I/O hierarchy only ever throw exactly
> > ios_base::failure?
> 
> That's the only one.
> 
> > So a workaround would be to marshal these somehow in the C++ EH personality
> > routine?  The c++11 variant seems to be a superset feature-wise (apart from
> > the changed inheritance), so constructing (in-place?!) the c++98 variant
> > once we hit a filter for c++98 ios_base::failure with an EH object of
> > type ios_base::failure[c++11] would "work"?
> 
> Until you try to rethrow it and catch it as the new type again.

Ok, but that would be an even more weird case of an intermediate
old-ABI object sitting inbetween the c++11 throwing libstdc++ and
a c++11 object.

> This approach was considered, and deemed not worth the complexity (and
> additional performance hit that would affect every 'catch' in every program
> that has to check if the catch handler is for ios::failure).

True.  But given we have broken things fixing it either requires
breaking the ABI for c++11 objects compiled with gcc 7 or leaving
things broken for objects compiled with gcc 3.4 to gcc 6.  Or
doing a hack like that.

My main concern is that the dual-ABI story breaks down in the current
situation where there isn't a single libstdc++ that makes both
situations work (catch c++11 ios_base::failure and c++98 
ios_base::failure).  So there's no way you can deploy the dual-ABI
libstdc++ on an existing system without possibly breaking old
programs (or not providing the C++11 ABI at all).  In fact
there isn't a good workaround forward to have both apart from
changing the SONAME for the C++11 ABI library or playing other
tricks via LD_LIBRARY_PATH or so.

> It would be possible to make __throw_ios_failure() throw:
> 
> struct enhanced_failure : std::ios::failure {
>   unsigned char buf[sizeof old ios::failure];
> };
> 
> This can be caught as the new type, and when there's an attempt to catch it as
> the old type, construct it in the buffer and catch the object in the buffer.
> Constructing it lazily can introduce a race condition, so it might be better to
> always pre-propulate the buffer, just in case anybody wants to catch the old
> type.

So the old std::ios::failure doesn't fit in the new one?  As said
above I consider the re-throwing and catching as c++11 a situation
that shouldn't be required for fixing the "legacy" binary case.
Comment 6 Jonathan Wakely 2018-04-05 09:11:06 UTC
(In reply to rguenther@suse.de from comment #5)
> On Thu, 5 Apr 2018, redi at gcc dot gnu.org wrote:
> 
> > https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85222
> > 
> > --- Comment #4 from Jonathan Wakely <redi at gcc dot gnu.org> ---
> > (In reply to Richard Biener from comment #3)
> > > Do you know of any other exception type affected by the c++11 vs. old ABI
> > > issue or does the entire I/O hierarchy only ever throw exactly
> > > ios_base::failure?
> > 
> > That's the only one.
> > 
> > > So a workaround would be to marshal these somehow in the C++ EH personality
> > > routine?  The c++11 variant seems to be a superset feature-wise (apart from
> > > the changed inheritance), so constructing (in-place?!) the c++98 variant
> > > once we hit a filter for c++98 ios_base::failure with an EH object of
> > > type ios_base::failure[c++11] would "work"?
> > 
> > Until you try to rethrow it and catch it as the new type again.
> 
> Ok, but that would be an even more weird case of an intermediate
> old-ABI object sitting inbetween the c++11 throwing libstdc++ and
> a c++11 object.

Which can happen with a legacy shared library trying to catch the old ios::failure, log something, and then re-throw it. If that shared library is used by a program where the main() function is using the new ABI and main() tries to catch ios::failure at the top-level, it won't be able to if the exception was replaced by another type.

> So the old std::ios::failure doesn't fit in the new one?

It fits, but overwriting it in place can create data races and invalidate existing references to the original object that was overwritten.

>  As said
> above I consider the re-throwing and catching as c++11 a situation
> that shouldn't be required for fixing the "legacy" binary case.

It's not required to fix the simple case of a legacy binary using a new libstdc++.so but it breaks other cases.
Comment 7 rguenther@suse.de 2018-04-05 09:27:33 UTC
On Thu, 5 Apr 2018, redi at gcc dot gnu.org wrote:

> https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85222
> 
> --- Comment #6 from Jonathan Wakely <redi at gcc dot gnu.org> ---
> (In reply to rguenther@suse.de from comment #5)
> > On Thu, 5 Apr 2018, redi at gcc dot gnu.org wrote:
> > 
> > > https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85222
> > > 
> > > --- Comment #4 from Jonathan Wakely <redi at gcc dot gnu.org> ---
> > > (In reply to Richard Biener from comment #3)
> > > > Do you know of any other exception type affected by the c++11 vs. old ABI
> > > > issue or does the entire I/O hierarchy only ever throw exactly
> > > > ios_base::failure?
> > > 
> > > That's the only one.
> > > 
> > > > So a workaround would be to marshal these somehow in the C++ EH personality
> > > > routine?  The c++11 variant seems to be a superset feature-wise (apart from
> > > > the changed inheritance), so constructing (in-place?!) the c++98 variant
> > > > once we hit a filter for c++98 ios_base::failure with an EH object of
> > > > type ios_base::failure[c++11] would "work"?
> > > 
> > > Until you try to rethrow it and catch it as the new type again.
> > 
> > Ok, but that would be an even more weird case of an intermediate
> > old-ABI object sitting inbetween the c++11 throwing libstdc++ and
> > a c++11 object.
> 
> Which can happen with a legacy shared library trying to catch the old
> ios::failure, log something, and then re-throw it. If that shared library is
> used by a program where the main() function is using the new ABI and main()
> tries to catch ios::failure at the top-level, it won't be able to if the
> exception was replaced by another type.
> 
> > So the old std::ios::failure doesn't fit in the new one?
> 
> It fits, but overwriting it in place can create data races and invalidate
> existing references to the original object that was overwritten.
> 
> >  As said
> > above I consider the re-throwing and catching as c++11 a situation
> > that shouldn't be required for fixing the "legacy" binary case.
> 
> It's not required to fix the simple case of a legacy binary using a new
> libstdc++.so but it breaks other cases.

None that are not broken right now?

Anyway, what would happen if __throw_ios_failure would throw a

struct ios_failure : ios_base::failure, ios_base::failure[c++11]

class (with both sub-classes properly initialized)?

If I do

  catch (ios_base::failure[c++11] &e)
   {
     throw e;
   }

does it re-throw the original exception type?  Can I catch
that ios_failure class at all this way?
Comment 8 Jonathan Wakely 2018-04-05 09:55:19 UTC
(In reply to rguenther@suse.de from comment #7)
> > It's not required to fix the simple case of a legacy binary using a new
> > libstdc++.so but it breaks other cases.
> 
> None that are not broken right now?

Well with the case of a legacy shared lib and a new main(), currently the shared lib won't catch the exception but main will. It's arguably more broken if main doesn't catch it and terminates with an unhandled exception.
 
> Anyway, what would happen if __throw_ios_failure would throw a
> 
> struct ios_failure : ios_base::failure, ios_base::failure[c++11]
> 
> class (with both sub-classes properly initialized)?

Bug 66145 comment 23.

> 
> If I do
> 
>   catch (ios_base::failure[c++11] &e)

You can't use the ABI tag in the name like this.

>    {
>      throw e;
>    }
> 
> does it re-throw the original exception type?

No, "throw e;" throws a new exception object. If you just do "throw;" it re-throws the original exception.

>  Can I catch
> that ios_failure class at all this way?

I'm not sure what "this way" refers to.
Comment 9 Richard Biener 2018-04-05 11:04:47 UTC
(In reply to Jonathan Wakely from comment #8)
> (In reply to rguenther@suse.de from comment #7)
> > > It's not required to fix the simple case of a legacy binary using a new
> > > libstdc++.so but it breaks other cases.
> > 
> > None that are not broken right now?
> 
> Well with the case of a legacy shared lib and a new main(), currently the
> shared lib won't catch the exception but main will. It's arguably more
> broken if main doesn't catch it and terminates with an unhandled exception.
>  
> > Anyway, what would happen if __throw_ios_failure would throw a
> > 
> > struct ios_failure : ios_base::failure, ios_base::failure[c++11]
> > 
> > class (with both sub-classes properly initialized)?
> 
> Bug 66145 comment 23.

Bah.  Stupid C++ for not providing a way to disambiguate ;)

Looks like it also fails silently - just not catch anything.

> > 
> > If I do
> > 
> >   catch (ios_base::failure[c++11] &e)
> 
> You can't use the ABI tag in the name like this.

Of course, it was just pseudo-code.  Apart from the above issue which
makes it a non-starter it works though.

Downstream comments in the bug already argue you're breaking the ABI :/

Now it might be (no idea!) simpler to "fix" this in the unwinder when
an object with the above multi-inheritance is thrown (just to fix the
std::exception case).  Either by being able to modify the actual
implementation of the inheritance meta-data (if such exists) to avoid
the ambiguity and just re-direct to either base or by detecting the
situation and doing that manually.

Thus, provide an "extension" that makes the following valid:

struct base {};
struct err1 : base { };
struct err2 : base { };
struct err : err1, err2 /*<somehow-specify-'base'-is-from-err1>*/ { };

void f()
{
  err e;
  base &b = e;
}
Comment 10 Jonathan Wakely 2018-04-05 11:39:51 UTC
Seems simpler to just define:

struct __dual_ios_failure {
  __dual_ios_failure(std::string s, error_code e) : new_(s, e), old_(s) { }
  ios::failure[abi:cxx11] new_;
  ios::failure old_;
};

and make __throw_ios_failure() throw one of that type, and make the EH runtime do the necessary adjustments to make this work:

__dual_ios_failure * p1;

try {
  try {
    try {
      throw __dual_ios_failure("", {});
    } catch (__dual_ios_failure& e1) {
      p1 = &e1;
      throw;
    }
  } catch (ios::failure[abi:cxx11]& e2) {
    assert( &e2 == &p1->new_ );
    throw;
  }
} catch (ios::failure& e3) {
  assert( &e3 == &p1->old_ );
}

i.e. if the catch handler is one of the ios::failure types and the actual thrown exception is __dual_ios_failure then catch the member instead of the object itself. The "throw;" would re-throw the original object of type __dual_abi_failure, so the next handler would be able to perform the same checks and adjustments.

This would only require magic in the EH catch routines, not a new way to declare base classes.
Comment 11 rguenther@suse.de 2018-04-05 13:54:15 UTC
On Thu, 5 Apr 2018, redi at gcc dot gnu.org wrote:

> https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85222
> 
> --- Comment #10 from Jonathan Wakely <redi at gcc dot gnu.org> ---
> Seems simpler to just define:
> 
> struct __dual_ios_failure {
>   __dual_ios_failure(std::string s, error_code e) : new_(s, e), old_(s) { }
>   ios::failure[abi:cxx11] new_;
>   ios::failure old_;
> };
> 
> and make __throw_ios_failure() throw one of that type, and make the EH runtime
> do the necessary adjustments to make this work:
> 
> __dual_ios_failure * p1;
> 
> try {
>   try {
>     try {
>       throw __dual_ios_failure("", {});
>     } catch (__dual_ios_failure& e1) {
>       p1 = &e1;
>       throw;
>     }
>   } catch (ios::failure[abi:cxx11]& e2) {
>     assert( &e2 == &p1->new_ );
>     throw;
>   }
> } catch (ios::failure& e3) {
>   assert( &e3 == &p1->old_ );
> }
> 
> i.e. if the catch handler is one of the ios::failure types and the actual
> thrown exception is __dual_ios_failure then catch the member instead of the
> object itself. The "throw;" would re-throw the original object of type
> __dual_abi_failure, so the next handler would be able to perform the same
> checks and adjustments.
> 
> This would only require magic in the EH catch routines, not a new way to
> declare base classes.

True.  Given the issue of declaring the __dual_ios_failure type - as you
said, you can't write it that way - it's probably going to be a
builtin type?  In which case "massaging" the typeinfo data to make
the code in the EH catchers less special might be easier.

Not sure, I'm not at all familiar with these areas of GCC internals.

Whatever it takes, it would be nice to fix this in a way not
breaking pre-GCC7 nor GCC7-and-later code...
Comment 12 rguenther@suse.de 2018-04-05 14:31:21 UTC
On Thu, 5 Apr 2018, rguenther at suse dot de wrote:

> https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85222
> 
> --- Comment #11 from rguenther at suse dot de <rguenther at suse dot de> ---
> On Thu, 5 Apr 2018, redi at gcc dot gnu.org wrote:
> 
> > https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85222
> > 
> > --- Comment #10 from Jonathan Wakely <redi at gcc dot gnu.org> ---
> > Seems simpler to just define:
> > 
> > struct __dual_ios_failure {
> >   __dual_ios_failure(std::string s, error_code e) : new_(s, e), old_(s) { }
> >   ios::failure[abi:cxx11] new_;
> >   ios::failure old_;
> > };
> > 
> > and make __throw_ios_failure() throw one of that type, and make the EH runtime
> > do the necessary adjustments to make this work:
> > 
> > __dual_ios_failure * p1;
> > 
> > try {
> >   try {
> >     try {
> >       throw __dual_ios_failure("", {});
> >     } catch (__dual_ios_failure& e1) {
> >       p1 = &e1;
> >       throw;
> >     }
> >   } catch (ios::failure[abi:cxx11]& e2) {
> >     assert( &e2 == &p1->new_ );
> >     throw;
> >   }
> > } catch (ios::failure& e3) {
> >   assert( &e3 == &p1->old_ );
> > }
> > 
> > i.e. if the catch handler is one of the ios::failure types and the actual
> > thrown exception is __dual_ios_failure then catch the member instead of the
> > object itself. The "throw;" would re-throw the original object of type
> > __dual_abi_failure, so the next handler would be able to perform the same
> > checks and adjustments.
> > 
> > This would only require magic in the EH catch routines, not a new way to
> > declare base classes.
> 
> True.  Given the issue of declaring the __dual_ios_failure type - as you
> said, you can't write it that way - it's probably going to be a
> builtin type?  In which case "massaging" the typeinfo data to make
> the code in the EH catchers less special might be easier.
> 
> Not sure, I'm not at all familiar with these areas of GCC internals.
> 
> Whatever it takes, it would be nice to fix this in a way not
> breaking pre-GCC7 nor GCC7-and-later code...

So you'd have a new internal composite type which you'd associate
with a typeinfo refering to some custom 
__cxxabiv1::__mab_class_type_info you'd then
implement the special filtering on?  (mab aka multiple ambiguous
bases)

The internal type could the still look like it was just
multiple-inherited from the two exception types?

That would leave the rest of the EH personality alone and thus
not affect runtime of other exception type propagation.
Comment 13 Richard Biener 2018-04-06 07:10:08 UTC
Just to mention the testcase fails in the same way if you build and run it with GCC 7 and libstdc++ from GCC 7 when that was configured with the gcc-4 compatible ABI by default.  Just in case that wasn't obvious...

Dropping the

// Determines the version of ios_base::failure thrown by __throw_ios_failure.
// If !_GLIBCXX_USE_DUAL_ABI this will get undefined automatically.
#define _GLIBCXX_USE_CXX11_ABI 1

from src/c++11/ios.cc probably makes that more "consistent", matching the
default ABI.  The PR66145 changed that from an unconditional #define to
0 to an unconditional #define to 1.  While, as the comment now says, a build
without the dual-abi will use the old behavior switching the ABI default
does not influence the behavior.

I realize PR66145 is exactly about "fixing" the case of the user not using
the default ABI but now we have still exactly the same situation when the user
does

#define _GLIBCXX_USE_CXX11_ABI 0

with a compiler defaulted to the new ABI.

So IMHO the original change was totally pointless... :/

I'm probably going to locally patch it to be consistent with the default ABI
choice - that at least keeps programs working that do not explicitely choose
the ABI via #define _GLIBCXX_USE_CXX11_ABI and is consistent with the system
integrators choice of the default ABI to keep compatibility to older GCC
versions.
Comment 14 Jakub Jelinek 2018-04-06 07:25:03 UTC
For the in-place transformation in libsupc++ if something catches the old failure and a new failure is thrown, is it possible?  The other way around, if we would be throwing the new failure and catching the old one, doesn't seem to be possible, the cxx11 failure is bigger (32 bytes compared to 16 bytes for the old one).  Could we just move it to a temporary, in-place construct the old one and destruct the temporary?  What if the code then rethrows it and is caught again in code that expects the new failure?
Comment 15 Jonathan Wakely 2018-04-06 08:35:56 UTC
(In reply to Richard Biener from comment #13)
> I'm probably going to locally patch it to be consistent with the default ABI
> choice - that at least keeps programs working that do not explicitely choose
> the ABI via #define _GLIBCXX_USE_CXX11_ABI and is consistent with the system
> integrators choice of the default ABI to keep compatibility to older GCC
> versions.

That makes sense. Maybe we should do that on trunk too (it doesn't affect the default configuration where the new ABI is the default).
Comment 16 Jonathan Wakely 2018-04-06 08:38:46 UTC
(In reply to Jakub Jelinek from comment #14)
> For the in-place transformation in libsupc++ if something catches the old
> failure and a new failure is thrown, is it possible?  The other way around,
> if we would be throwing the new failure and catching the old one, doesn't
> seem to be possible, the cxx11 failure is bigger (32 bytes compared to 16
> bytes for the old one).

I must be misunderstanding the scenario you're describing, because to me it seems like we could use 32 bytes to store a 16 byte object, but not the other way around.

Anyway, I think in-place transformations are not an option. Code could have pointers to the data in the original exception. If we destroy it and create a new object in the same location we invalidate those pointers.
Comment 17 rguenther@suse.de 2018-04-06 08:49:52 UTC
On Fri, 6 Apr 2018, redi at gcc dot gnu.org wrote:

> https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85222
> 
> --- Comment #16 from Jonathan Wakely <redi at gcc dot gnu.org> ---
> (In reply to Jakub Jelinek from comment #14)
> > For the in-place transformation in libsupc++ if something catches the old
> > failure and a new failure is thrown, is it possible?  The other way around,
> > if we would be throwing the new failure and catching the old one, doesn't
> > seem to be possible, the cxx11 failure is bigger (32 bytes compared to 16
> > bytes for the old one).
> 
> I must be misunderstanding the scenario you're describing, because to me it
> seems like we could use 32 bytes to store a 16 byte object, but not the other
> way around.
> 
> Anyway, I think in-place transformations are not an option. Code could have
> pointers to the data in the original exception. If we destroy it and create a
> new object in the same location we invalidate those pointers.

Yeah, I think throwing a composite object of both representation is
the easiest way here (apart from the difficulty to build that in
the first place).
Comment 18 rguenther@suse.de 2018-04-06 08:50:50 UTC
On Fri, 6 Apr 2018, redi at gcc dot gnu.org wrote:

> https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85222
> 
> --- Comment #15 from Jonathan Wakely <redi at gcc dot gnu.org> ---
> (In reply to Richard Biener from comment #13)
> > I'm probably going to locally patch it to be consistent with the default ABI
> > choice - that at least keeps programs working that do not explicitely choose
> > the ABI via #define _GLIBCXX_USE_CXX11_ABI and is consistent with the system
> > integrators choice of the default ABI to keep compatibility to older GCC
> > versions.
> 
> That makes sense. Maybe we should do that on trunk too (it doesn't affect the
> default configuration where the new ABI is the default).

FYI:

Index: libstdc++-v3/src/c++11/ios.cc
===================================================================
--- libstdc++-v3/src/c++11/ios.cc       (revision 258812)
+++ libstdc++-v3/src/c++11/ios.cc       (working copy)
@@ -26,9 +26,8 @@
 // ISO C++ 14882: 27.4  Iostreams base classes
 //
 
-// Determines the version of ios_base::failure thrown by 
__throw_ios_failure.
-// If !_GLIBCXX_USE_DUAL_ABI this will get undefined automatically.
-#define _GLIBCXX_USE_CXX11_ABI 1
+// The ABI version of ios_base::failure thrown by __throw_ios_failure
+// is determined by the default ABI version choosed at configure time
 
 #include <ios>
 #include <limits>
Comment 19 Richard Biener 2018-04-06 12:56:57 UTC
Luckily both failure classes are immutable (via access limitations) so the
following issue with the __dual_ios_failure idea doesn't exist:

 catch (old-ABI &e)
   {
     modify e in-place
     throw;
   }
 ...
 catch (new-ABI &e)
   {
     ... expect modified content ...
   }
Comment 20 Jonathan Wakely 2018-04-09 23:37:23 UTC
Patch posted: https://gcc.gnu.org/ml/gcc-patches/2018-04/msg00424.html
Comment 21 Jonathan Wakely 2018-04-10 14:36:41 UTC
Author: redi
Date: Tue Apr 10 14:36:09 2018
New Revision: 259281

URL: https://gcc.gnu.org/viewcvs?rev=259281&root=gcc&view=rev
Log:
PR libstdc++/85222 allow catching iostream errors as gcc4-compatible ios::failure

Define a new exception type derived from std::ios::failure[abi:cxx11]
which also aggregates an object of the gcc4-compatible ios::failure
type. Make __throw_ios_failure throw this new type for iostream errors
that raise exceptions. Provide custom type info for the new type so that
it can be caught by handlers for the gcc4-compatible ios::failure type
as well as handlers for ios::failure[abi:cxx11] and its bases.

	PR libstdc++/85222
	* src/c++11/Makefile.am [ENABLE_DUAL_ABI]: Add special rules for
	cxx11-ios_failure.cc to rewrite type info for __ios_failure.
	* src/c++11/Makefile.in: Regenerate.
	* src/c++11/cxx11-ios_failure.cc (__ios_failure, __iosfail_type_info):
	New types.
	[_GLIBCXX_USE_DUAL_ABI] (__throw_ios_failure): Define here.
	* src/c++11/ios.cc (__throw_ios_failure): Remove definition.
	* src/c++98/ios_failure.cc (__construct_ios_failure)
	(__destroy_ios_failure, is_ios_failure_handler): New functions.
	[!_GLIBCXX_USE_DUAL_ABI] (__throw_ios_failure): Define here.
	* testsuite/27_io/ios_base/failure/dual_abi.cc: New.
	* testsuite/27_io/basic_ios/copyfmt/char/1.cc: Revert changes to
	handler types, to always catch std::ios_base::failure.
	* testsuite/27_io/basic_ios/exceptions/char/1.cc: Likewise.
	* testsuite/27_io/basic_istream/extractors_arithmetic/char/
	exceptions_failbit.cc: Likewise.
	* testsuite/27_io/basic_istream/extractors_arithmetic/wchar_t/
	exceptions_failbit.cc: Likewise.
	* testsuite/27_io/basic_istream/extractors_other/char/
	exceptions_null.cc: Likewise.
	* testsuite/27_io/basic_istream/extractors_other/wchar_t/
	exceptions_null.cc: Likewise.
	* testsuite/27_io/basic_istream/sentry/char/12297.cc: Likewise.
	* testsuite/27_io/basic_istream/sentry/wchar_t/12297.cc: Likewise.
	* testsuite/27_io/basic_ostream/inserters_other/char/
	exceptions_null.cc: Likewise.
	* testsuite/27_io/basic_ostream/inserters_other/wchar_t/
	exceptions_null.cc: Likewise.
	* testsuite/27_io/ios_base/storage/2.cc: Likewise.

Added:
    trunk/libstdc++-v3/testsuite/27_io/ios_base/failure/dual_abi.cc
Modified:
    trunk/libstdc++-v3/ChangeLog
    trunk/libstdc++-v3/src/c++11/Makefile.am
    trunk/libstdc++-v3/src/c++11/Makefile.in
    trunk/libstdc++-v3/src/c++11/cxx11-ios_failure.cc
    trunk/libstdc++-v3/src/c++11/ios.cc
    trunk/libstdc++-v3/src/c++98/ios_failure.cc
    trunk/libstdc++-v3/testsuite/27_io/basic_ios/copyfmt/char/1.cc
    trunk/libstdc++-v3/testsuite/27_io/basic_ios/exceptions/char/1.cc
    trunk/libstdc++-v3/testsuite/27_io/basic_istream/extractors_arithmetic/char/exceptions_failbit.cc
    trunk/libstdc++-v3/testsuite/27_io/basic_istream/extractors_arithmetic/wchar_t/exceptions_failbit.cc
    trunk/libstdc++-v3/testsuite/27_io/basic_istream/extractors_other/char/exceptions_null.cc
    trunk/libstdc++-v3/testsuite/27_io/basic_istream/extractors_other/wchar_t/exceptions_null.cc
    trunk/libstdc++-v3/testsuite/27_io/basic_istream/sentry/char/12297.cc
    trunk/libstdc++-v3/testsuite/27_io/basic_istream/sentry/wchar_t/12297.cc
    trunk/libstdc++-v3/testsuite/27_io/basic_ostream/inserters_other/char/exceptions_null.cc
    trunk/libstdc++-v3/testsuite/27_io/basic_ostream/inserters_other/wchar_t/exceptions_null.cc
    trunk/libstdc++-v3/testsuite/27_io/ios_base/storage/2.cc
Comment 22 Jonathan Wakely 2018-04-12 19:07:23 UTC
Author: redi
Date: Thu Apr 12 19:06:50 2018
New Revision: 259352

URL: https://gcc.gnu.org/viewcvs?rev=259352&root=gcc&view=rev
Log:
PR libstdc++/85222 allow catching iostream errors as gcc4-compatible ios::failure

Define a new exception type derived from std::ios::failure[abi:cxx11]
which also aggregates an object of the gcc4-compatible ios::failure
type. Make __throw_ios_failure throw this new type for iostream errors
that raise exceptions. Provide custom type info for the new type so that
it can be caught by handlers for the gcc4-compatible ios::failure type
as well as handlers for ios::failure[abi:cxx11] and its bases.

Backport from mainline
2018-04-10  Jonathan Wakely  <jwakely@redhat.com>

	PR libstdc++/85222
	* src/c++11/Makefile.am [ENABLE_DUAL_ABI]: Add special rules for
	cxx11-ios_failure.cc to rewrite type info for __ios_failure.
	* src/c++11/Makefile.in: Regenerate.
	* src/c++11/cxx11-ios_failure.cc (__ios_failure, __iosfail_type_info):
	New types.
	[_GLIBCXX_USE_DUAL_ABI] (__throw_ios_failure): Define here.
	* src/c++11/ios.cc (__throw_ios_failure): Remove definition.
	(_GLIBCXX_USE_CXX11_ABI): Don't define here.
	* src/c++98/ios_failure.cc (__construct_ios_failure)
	(__destroy_ios_failure, is_ios_failure_handler): New functions.
	[!_GLIBCXX_USE_DUAL_ABI] (__throw_ios_failure): Define here.
	* testsuite/27_io/ios_base/failure/dual_abi.cc: New.
	* testsuite/27_io/basic_ios/copyfmt/char/1.cc: Revert changes to
	handler types, to always catch std::ios_base::failure.
	* testsuite/27_io/basic_ios/exceptions/char/1.cc: Likewise.
	* testsuite/27_io/basic_istream/extractors_arithmetic/char/
	exceptions_failbit.cc: Likewise.
	* testsuite/27_io/basic_istream/extractors_arithmetic/wchar_t/
	exceptions_failbit.cc: Likewise.
	* testsuite/27_io/basic_istream/extractors_other/char/
	exceptions_null.cc: Likewise.
	* testsuite/27_io/basic_istream/extractors_other/wchar_t/
	exceptions_null.cc: Likewise.
	* testsuite/27_io/basic_istream/sentry/char/12297.cc: Likewise.
	* testsuite/27_io/basic_istream/sentry/wchar_t/12297.cc: Likewise.
	* testsuite/27_io/basic_ostream/inserters_other/char/
	exceptions_null.cc: Likewise.
	* testsuite/27_io/basic_ostream/inserters_other/wchar_t/
	exceptions_null.cc: Likewise.
	* testsuite/27_io/ios_base/storage/2.cc: Likewise.

Added:
    branches/gcc-7-branch/libstdc++-v3/testsuite/27_io/ios_base/failure/dual_abi.cc
Modified:
    branches/gcc-7-branch/libstdc++-v3/ChangeLog
    branches/gcc-7-branch/libstdc++-v3/src/c++11/Makefile.am
    branches/gcc-7-branch/libstdc++-v3/src/c++11/Makefile.in
    branches/gcc-7-branch/libstdc++-v3/src/c++11/cxx11-ios_failure.cc
    branches/gcc-7-branch/libstdc++-v3/src/c++11/ios.cc
    branches/gcc-7-branch/libstdc++-v3/src/c++98/ios_failure.cc
    branches/gcc-7-branch/libstdc++-v3/testsuite/27_io/basic_ios/copyfmt/char/1.cc
    branches/gcc-7-branch/libstdc++-v3/testsuite/27_io/basic_ios/exceptions/char/1.cc
    branches/gcc-7-branch/libstdc++-v3/testsuite/27_io/basic_istream/extractors_arithmetic/char/exceptions_failbit.cc
    branches/gcc-7-branch/libstdc++-v3/testsuite/27_io/basic_istream/extractors_arithmetic/wchar_t/exceptions_failbit.cc
    branches/gcc-7-branch/libstdc++-v3/testsuite/27_io/basic_istream/extractors_other/char/exceptions_null.cc
    branches/gcc-7-branch/libstdc++-v3/testsuite/27_io/basic_istream/extractors_other/wchar_t/exceptions_null.cc
    branches/gcc-7-branch/libstdc++-v3/testsuite/27_io/basic_istream/sentry/char/12297.cc
    branches/gcc-7-branch/libstdc++-v3/testsuite/27_io/basic_istream/sentry/wchar_t/12297.cc
    branches/gcc-7-branch/libstdc++-v3/testsuite/27_io/basic_ostream/inserters_other/char/exceptions_null.cc
    branches/gcc-7-branch/libstdc++-v3/testsuite/27_io/basic_ostream/inserters_other/wchar_t/exceptions_null.cc
    branches/gcc-7-branch/libstdc++-v3/testsuite/27_io/ios_base/storage/2.cc
Comment 23 Jonathan Wakely 2018-04-12 19:07:34 UTC
Fixed for 7.4 and 8.1
Comment 24 Jonathan Wakely 2018-08-08 15:40:45 UTC
Author: redi
Date: Wed Aug  8 15:40:11 2018
New Revision: 263414

URL: https://gcc.gnu.org/viewcvs?rev=263414&root=gcc&view=rev
Log:
PR libstdc++/66145 allow catching iostream errors as cxx11 ios::failure

Define a new exception type derived from the gcc4-compatible ios::failure
which also aggregates an object of the ios::failure[abi:cxx11] type.
Make __throw_ios_failure throw this new type for iostream errors
that raise exceptions. Provide custom type info for the new type so that
it can be caught by handlers for ios::failure[abi:cxx11] type
as well as handlers for the gcc4-compatible ios::failure and its bases.

Backport from mainline
2018-04-10  Jonathan Wakely  <jwakely@redhat.com>

	PR libstdc++/85222
	* src/c++11/cxx11-ios_failure.cc (__construct_ios_failure)
	(__destroy_ios_failure, is_ios_failure_handler): New functions.
	* src/c++11/ios.cc (__throw_ios_failure): Remove definition.
	(_GLIBCXX_USE_CXX11_ABI): Don't define here.
	* src/c++98/Makefile.am [ENABLE_DUAL_ABI]: Add special rules for
	ios_failure.cc to rewrite type info for __ios_failure.
	* src/c++98/Makefile.in: Regenerate.
	* src/c++98/ios_failure.cc [_GLIBCXX_USE_DUAL_ABI]
	(__iosfailure, __iosfailure_type_info): New types.
	(__throw_ios_failure): Define here.
	* testsuite/27_io/ios_base/failure/dual_abi.cc: New.
	* testsuite/27_io/basic_ios/copyfmt/char/1.cc: Revert changes to
	add -D_GLIBCXX_USE_CXX11_ABI=0 to dg-options.
	* testsuite/27_io/basic_ios/exceptions/char/1.cc: Likewise.
	* testsuite/27_io/basic_istream/extractors_arithmetic/char/
	exceptions_failbit.cc: Likewise.
	* testsuite/27_io/basic_istream/extractors_arithmetic/wchar_t/
	exceptions_failbit.cc: Likewise.
	* testsuite/27_io/basic_istream/extractors_other/char/
	exceptions_null.cc: Likewise.
	* testsuite/27_io/basic_istream/extractors_other/wchar_t/
	exceptions_null.cc: Likewise.
	* testsuite/27_io/basic_istream/sentry/char/12297.cc: Likewise.
	* testsuite/27_io/basic_istream/sentry/wchar_t/12297.cc: Likewise.
	* testsuite/27_io/basic_ostream/inserters_other/char/
	exceptions_null.cc: Likewise.
	* testsuite/27_io/basic_ostream/inserters_other/wchar_t/
	exceptions_null.cc: Likewise.
	* testsuite/27_io/ios_base/storage/2.cc: Likewise.

Added:
    branches/gcc-6-branch/libstdc++-v3/testsuite/27_io/ios_base/failure/dual_abi.cc
Modified:
    branches/gcc-6-branch/libstdc++-v3/ChangeLog
    branches/gcc-6-branch/libstdc++-v3/src/c++11/cxx11-ios_failure.cc
    branches/gcc-6-branch/libstdc++-v3/src/c++11/ios.cc
    branches/gcc-6-branch/libstdc++-v3/src/c++98/Makefile.am
    branches/gcc-6-branch/libstdc++-v3/src/c++98/Makefile.in
    branches/gcc-6-branch/libstdc++-v3/src/c++98/ios_failure.cc
    branches/gcc-6-branch/libstdc++-v3/testsuite/27_io/basic_ios/copyfmt/char/1.cc
    branches/gcc-6-branch/libstdc++-v3/testsuite/27_io/basic_ios/exceptions/char/1.cc
    branches/gcc-6-branch/libstdc++-v3/testsuite/27_io/basic_istream/extractors_arithmetic/char/exceptions_failbit.cc
    branches/gcc-6-branch/libstdc++-v3/testsuite/27_io/basic_istream/extractors_arithmetic/wchar_t/exceptions_failbit.cc
    branches/gcc-6-branch/libstdc++-v3/testsuite/27_io/basic_istream/extractors_other/char/exceptions_null.cc
    branches/gcc-6-branch/libstdc++-v3/testsuite/27_io/basic_istream/extractors_other/wchar_t/exceptions_null.cc
    branches/gcc-6-branch/libstdc++-v3/testsuite/27_io/basic_istream/sentry/char/12297.cc
    branches/gcc-6-branch/libstdc++-v3/testsuite/27_io/basic_istream/sentry/wchar_t/12297.cc
    branches/gcc-6-branch/libstdc++-v3/testsuite/27_io/basic_ostream/inserters_other/char/exceptions_null.cc
    branches/gcc-6-branch/libstdc++-v3/testsuite/27_io/basic_ostream/inserters_other/wchar_t/exceptions_null.cc
    branches/gcc-6-branch/libstdc++-v3/testsuite/27_io/ios_base/storage/2.cc