diff mbox

[v3] libio: use PTR_MANGLE/PTR_DEMANGLE for FILE vtables

Message ID 20160524165854.GA32022@www.outflux.net
State New
Headers show

Commit Message

Kees Cook May 24, 2016, 4:58 p.m. UTC
Hi!

This is a continuation of an earlier discussion[1] about hardening the
FILE vtable with PTR_MANGLE, which previously ended with the request that
a new configure variable be used to control it[2]. This series now carries
the new "--enable-libio-compatibility" configure flag to retain libio
compat with pre-roughly-2007 binaries using libstdc++.so.5 and earlier.

Running the glibc test suite shows no regressions for me.

(FWIW, this patch has seen a lot of use, as it has been in Chrome OS
for over 3 years now.)

-Kees

[1] https://sourceware.org/ml/libc-alpha/2015-10/msg00009.html
[2] https://sourceware.org/ml/libc-alpha/2015-10/msg00057.html

v3:
- adjust whitespace, rename mangle/demangle inlines (vapier).
v2:
- added "--enable-libio-compatibility" (fw).
v1:
- URL [1] above
---
2016-05-23  Kees Cook <keescook@chromium.org>

	* configure.ac (AC_ARG_ENABLE(libio-compatibility)): New configure
	flag.
	* config.h.in: add USE_COMPAT_LIBIO
	* configure: Regenerate.
	* manual/install.texi (--disable-libio-compatibility): Document new
	flag.
	* INSTALL: Regenerate.
	* libio/libioP.h: Create inline helpers to run the PTR_MANGLE and
	PTR_DEMANGLE routines on vtable pointers when USE_COMPAT_LIBIO
	is not set. Create _IO_JUMPS_SET and _IO_WIDE_JUMPS_SET macros
	to use the new inline helpers. Update the _IO_JUMPS_FUNC and
	_IO_WIDE_JUMPS_FUNC to use the new inline helpers.
	* debug/obprintf_chk.c: Replace direct vtable assignment with macro.
	* debug/vasprintf_chk.c: Likewise.
	* debug/vdprintf_chk.c: Likewise.
	* debug/vsnprintf_chk.c: Likewise.
	* debug/vsprintf_chk.c: Likewise.
	* libio/fileops.c: Likewise.
	* libio/freopen.c: Likewise.
	* libio/freopen64.c: Likewise.
	* libio/genops.c: Likewise.
	* libio/iofdopen.c: Likewise.
	* libio/iofopen.c: Likewise.
	* libio/iofopncook.c: Likewise.
	* libio/iofwide.c: Likewise.
	* libio/iopopen.c: Likewise.
	* libio/iovdprintf.c: Likewise.
	* libio/iovsprintf.c: Likewise.
	* libio/iovsscanf.c: Likewise.
	* libio/memstream.c: Likewise.
	* libio/obprintf.c: Likewise.
	* libio/oldiofdopen.c: Likewise.
	* libio/oldiofopen.c: Likewise.
	* libio/oldiopopen.c: Likewise.
	* libio/vasprintf.c: Likewise.
	* libio/vsnprintf.c: Likewise.
	* stdio-common/isoc99_vsscanf.c: Likewise.
	* stdio-common/vfprintf.c: Likewise.
	* stdlib/strfmon_l.c: Likewise.
	* misc/init-misc.c: Mangle predefined stdio FILE vtables at startup.
---
 INSTALL                       |  5 ++++
 config.h.in                   |  3 ++
 configure                     | 15 ++++++++++
 configure.ac                  | 11 +++++++
 debug/obprintf_chk.c          |  2 +-
 debug/vasprintf_chk.c         |  2 +-
 debug/vdprintf_chk.c          |  2 +-
 debug/vsnprintf_chk.c         |  2 +-
 debug/vsprintf_chk.c          |  2 +-
 libio/fileops.c               | 28 +++++++++---------
 libio/freopen.c               |  6 ++--
 libio/freopen64.c             |  4 +--
 libio/genops.c                |  2 +-
 libio/iofdopen.c              |  4 +--
 libio/iofopen.c               |  8 +++---
 libio/iofopncook.c            |  4 +--
 libio/iofwide.c               |  2 +-
 libio/iopopen.c               |  2 +-
 libio/iovdprintf.c            |  2 +-
 libio/iovsprintf.c            |  2 +-
 libio/iovsscanf.c             |  2 +-
 libio/libioP.h                | 67 +++++++++++++++++++++++++++++++------------
 libio/memstream.c             |  2 +-
 libio/obprintf.c              |  2 +-
 libio/oldiofdopen.c           |  2 +-
 libio/oldiofopen.c            |  2 +-
 libio/oldiopopen.c            |  2 +-
 libio/vasprintf.c             |  2 +-
 libio/vsnprintf.c             |  2 +-
 manual/install.texi           |  5 ++++
 misc/init-misc.c              | 15 ++++++++++
 stdio-common/isoc99_vsscanf.c |  2 +-
 stdio-common/vfprintf.c       |  2 +-
 stdlib/strfmon_l.c            |  2 +-
 34 files changed, 150 insertions(+), 67 deletions(-)

Comments

Florian Weimer May 25, 2016, 8:29 a.m. UTC | #1
On 05/24/2016 06:58 PM, Kees Cook wrote:
> +	      [libio_compatibility=no])

I really don't think we want to do this by default, at least not for 
i386.  Existing policy is to maintain the ABI, even doing so is not cheap.

I think we need a follow-up patch which removes the symbols which are 
now incompatible from the ABI, so that programs which are broken by this 
configuration receive an explicit error message.

Florian
Kees Cook May 25, 2016, 5:13 p.m. UTC | #2
On Wed, May 25, 2016 at 1:29 AM, Florian Weimer <fweimer@redhat.com> wrote:
> On 05/24/2016 06:58 PM, Kees Cook wrote:
>>
>> +             [libio_compatibility=no])
>
>
> I really don't think we want to do this by default, at least not for i386.
> Existing policy is to maintain the ABI, even doing so is not cheap.

Okay, so send a v3 that inverts this to --disable-libio-compatibility

> I think we need a follow-up patch which removes the symbols which are now
> incompatible from the ABI, so that programs which are broken by this
> configuration receive an explicit error message.

I have no idea how/where to do this. :) Hints?

-Kees
Mike Frysinger May 25, 2016, 5:52 p.m. UTC | #3
On 25 May 2016 10:29, Florian Weimer wrote:
> I think we need a follow-up patch which removes the symbols which are 
> now incompatible from the ABI, so that programs which are broken by this 
> configuration receive an explicit error message.

you mean all of the _IO_* symbols ?  my familiarity here is like 0,
but are there any reasons to keep exporting those for new ABIs ?
-mike
Florian Weimer May 25, 2016, 6:25 p.m. UTC | #4
On 05/25/2016 07:52 PM, Mike Frysinger wrote:
> On 25 May 2016 10:29, Florian Weimer wrote:
>> I think we need a follow-up patch which removes the symbols which are
>> now incompatible from the ABI, so that programs which are broken by this
>> configuration receive an explicit error message.
>
> you mean all of the _IO_* symbols ?

There are exceptions which are part of the API.  At least _IO_putc and 
_IO_getc are, and _IO_stdin_used is interposed from the application. 
(We can maybe get rid of the latter because it does not work anyway 
because nothing ensures that the program is compiled against a 
consistent set of libio definitions.)

> my familiarity here is like 0,

Sadly, I don't know if we can pick anyone's brain who is more familiar 
with these details.

> but are there any reasons to keep exporting those for new ABIs ?

For most symbols, no, there is no reason to keep them for new ABIs.

My understanding of symbol versioning is still limited, and I have no 
clear picture of the minimal libio ABI we can export, otherwise I'd have 
sent some patches long ago.

A first step would involve cutting down the contents of the installed 
<libio.h> header file to the absolute minimum needed by <stdio.h>.  But 
even that is quite a bit of work.

Florian
Florian Weimer May 30, 2016, 10:10 a.m. UTC | #5
On 05/25/2016 07:13 PM, Kees Cook wrote:

>> I think we need a follow-up patch which removes the symbols which are now
>> incompatible from the ABI, so that programs which are broken by this
>> configuration receive an explicit error message.
>
> I have no idea how/where to do this. :) Hints?

You can use the C preprocessor in Versions files, see elf/Versions.

The challenge is to figure out the bits which currently *are* part of 
the ABI.

Thanks,
Florian
diff mbox

Patch

diff --git a/INSTALL b/INSTALL
index 31e256d..8b9e610 100644
--- a/INSTALL
+++ b/INSTALL
@@ -116,6 +116,11 @@  will be used, and CFLAGS sets optimization options for the compiler.
      program linked statically with the NSS libraries cannot be
      dynamically reconfigured to use a different name database.
 
+'--enable-libio-compatibility'
+     Compile libio without PTR_MANGLE of the FILE vtable, in order to
+     remain compatibile with pre-libstdc++.so.6 binaries that may be
+     sharing the vtable (i.e. roughly pre-2007 binaries).
+
 '--without-tls'
      By default the C library is built with support for thread-local
      storage if the used tools support it.  By using '--without-tls'
diff --git a/config.h.in b/config.h.in
index 0147ba3..9a7894b 100644
--- a/config.h.in
+++ b/config.h.in
@@ -161,6 +161,9 @@ 
 /* Define if the dynamic linker should consult an ld.so.cache file.  */
 #undef USE_LDCONFIG
 
+/* Define if we should remain compatibile with pre-libstdc++.so.6 libio. */
+#undef USE_COMPAT_LIBIO
+
 /* Define to 1 if STT_GNU_IFUNC support actually works.  */
 #define HAVE_IFUNC 0
 
diff --git a/configure b/configure
index 8fe5937..24e4d7a 100755
--- a/configure
+++ b/configure
@@ -761,6 +761,7 @@  enable_add_ons
 enable_hidden_plt
 enable_bind_now
 enable_static_nss
+enable_libio_compatibility
 enable_force_install
 enable_maintainer_mode
 enable_kernel
@@ -1422,6 +1423,8 @@  Optional Features:
   --disable-hidden-plt    do not hide internal function calls to avoid PLT
   --enable-bind-now       disable lazy relocations in DSOs
   --enable-static-nss     build static NSS modules [default=no]
+  --enable-libio-compatibility
+                          remain compatible with pre-libstdc++.so.6 libio
   --disable-force-install don't force installation of files from this package,
                           even if they are older than the installed files
   --enable-maintainer-mode
@@ -3430,6 +3433,18 @@  if test x"$static_nss" = xyes || test x"$shared" = xno; then
 
 fi
 
+# Check whether --enable-libio-compatibility was given.
+if test "${enable_libio_compatibility+set}" = set; then :
+  enableval=$enable_libio_compatibility; libio_compatibility=$enableval
+else
+  libio_compatibility=no
+fi
+
+if test "$libio_compatibility" = yes; then
+  $as_echo "#define USE_COMPAT_LIBIO 1" >>confdefs.h
+
+fi
+
 # Check whether --enable-force-install was given.
 if test "${enable_force_install+set}" = set; then :
   enableval=$enable_force_install; force_install=$enableval
diff --git a/configure.ac b/configure.ac
index 3c766b7..347a9ec 100644
--- a/configure.ac
+++ b/configure.ac
@@ -245,6 +245,17 @@  if test x"$static_nss" = xyes || test x"$shared" = xno; then
   AC_DEFINE(DO_STATIC_NSS)
 fi
 
+dnl To enable PTR_MANGLE on the FILE vtable, we break compatibility with
+dnl pre-libstdc++.so.6 libio.
+AC_ARG_ENABLE([libio-compatibility],
+	      AC_HELP_STRING([--enable-libio-compatibility],
+			     [remain compatible with pre-libstdc++.so.6 libio]),
+	      [libio_compatibility=$enableval],
+	      [libio_compatibility=no])
+if test "$libio_compatibility" = yes; then
+  AC_DEFINE(USE_COMPAT_LIBIO)
+fi
+
 AC_ARG_ENABLE([force-install],
 	      AC_HELP_STRING([--disable-force-install],
 			     [don't force installation of files from this package, even if they are older than the installed files]),
diff --git a/debug/obprintf_chk.c b/debug/obprintf_chk.c
index 8469b5f..77ed19f 100644
--- a/debug/obprintf_chk.c
+++ b/debug/obprintf_chk.c
@@ -54,7 +54,7 @@  __obstack_vprintf_chk (struct obstack *obstack, int flags, const char *format,
 #endif
 
   _IO_no_init (&new_f.ofile.file.file, _IO_USER_LOCK, -1, NULL, NULL);
-  _IO_JUMPS (&new_f.ofile.file) = &_IO_obstack_jumps;
+  _IO_JUMPS_SET (&new_f.ofile.file, &_IO_obstack_jumps);
   room = obstack_room (obstack);
   size = obstack_object_size (obstack) + room;
   if (size == 0)
diff --git a/debug/vasprintf_chk.c b/debug/vasprintf_chk.c
index cb1f74a..db81a47 100644
--- a/debug/vasprintf_chk.c
+++ b/debug/vasprintf_chk.c
@@ -52,7 +52,7 @@  __vasprintf_chk (char **result_ptr, int flags, const char *format,
   sf._sbf._f._lock = NULL;
 #endif
   _IO_no_init (&sf._sbf._f, _IO_USER_LOCK, -1, NULL, NULL);
-  _IO_JUMPS (&sf._sbf) = &_IO_str_jumps;
+  _IO_JUMPS_SET (&sf._sbf, &_IO_str_jumps);
   _IO_str_init_static_internal (&sf, string, init_string_size, string);
   sf._sbf._f._flags &= ~_IO_USER_BUF;
   sf._s._allocate_buffer = (_IO_alloc_type) malloc;
diff --git a/debug/vdprintf_chk.c b/debug/vdprintf_chk.c
index 05d0bcd..1daeb4c 100644
--- a/debug/vdprintf_chk.c
+++ b/debug/vdprintf_chk.c
@@ -38,7 +38,7 @@  __vdprintf_chk (int d, int flags, const char *format, va_list arg)
   tmpfil.file._lock = NULL;
 #endif
   _IO_no_init (&tmpfil.file, _IO_USER_LOCK, 0, &wd, &_IO_wfile_jumps);
-  _IO_JUMPS (&tmpfil) = &_IO_file_jumps;
+  _IO_JUMPS_SET (&tmpfil, &_IO_file_jumps);
   _IO_file_init (&tmpfil);
 #if  !_IO_UNIFIED_JUMPTABLES
   tmpfil.vtable = NULL;
diff --git a/debug/vsnprintf_chk.c b/debug/vsnprintf_chk.c
index cc559d2..2a91815 100644
--- a/debug/vsnprintf_chk.c
+++ b/debug/vsnprintf_chk.c
@@ -51,7 +51,7 @@  ___vsnprintf_chk (char *s, size_t maxlen, int flags, size_t slen,
     }
 
   _IO_no_init (&sf.f._sbf._f, _IO_USER_LOCK, -1, NULL, NULL);
-  _IO_JUMPS (&sf.f._sbf) = &_IO_strn_jumps;
+  _IO_JUMPS_SET (&sf.f._sbf, &_IO_strn_jumps);
   s[0] = '\0';
 
   /* For flags > 0 (i.e. __USE_FORTIFY_LEVEL > 1) request that %n
diff --git a/debug/vsprintf_chk.c b/debug/vsprintf_chk.c
index aa1587c..f6605e2 100644
--- a/debug/vsprintf_chk.c
+++ b/debug/vsprintf_chk.c
@@ -71,7 +71,7 @@  ___vsprintf_chk (char *s, int flags, size_t slen, const char *format,
     __chk_fail ();
 
   _IO_no_init (&f._sbf._f, _IO_USER_LOCK, -1, NULL, NULL);
-  _IO_JUMPS (&f._sbf) = &_IO_str_chk_jumps;
+  _IO_JUMPS_SET (&f._sbf, &_IO_str_chk_jumps);
   s[0] = '\0';
   _IO_str_init_static_internal (&f, s, slen - 1, s);
 
diff --git a/libio/fileops.c b/libio/fileops.c
index 8e83b1c..bdf30e6 100644
--- a/libio/fileops.c
+++ b/libio/fileops.c
@@ -414,7 +414,7 @@  _IO_new_file_fopen (_IO_FILE *fp, const char *filename, const char *mode,
 	    &result->_wide_data->_IO_state;
 
 	  /* From now on use the wide character callback functions.  */
-	  _IO_JUMPS_FILE_plus (fp) = fp->_wide_data->_wide_vtable;
+	  _IO_JUMPS_FILE_plus_SET (fp, _IO_WIDE_JUMPS_FUNC (fp));
 
 	  /* Set the mode now.  */
 	  result->_mode = 1;
@@ -466,8 +466,8 @@  _IO_file_setbuf_mmap (_IO_FILE *fp, char *p, _IO_ssize_t len)
   _IO_FILE *result;
 
   /* Change the function table.  */
-  _IO_JUMPS_FILE_plus (fp) = &_IO_file_jumps;
-  fp->_wide_data->_wide_vtable = &_IO_wfile_jumps;
+  _IO_JUMPS_FILE_plus_SET (fp, &_IO_file_jumps);
+  _IO_WIDE_JUMPS_SET (fp, &_IO_wfile_jumps);
 
   /* And perform the normal operation.  */
   result = _IO_new_file_setbuf (fp, p, len);
@@ -475,8 +475,8 @@  _IO_file_setbuf_mmap (_IO_FILE *fp, char *p, _IO_ssize_t len)
   /* If the call failed, restore to using mmap.  */
   if (result == NULL)
     {
-      _IO_JUMPS_FILE_plus (fp) = &_IO_file_jumps_mmap;
-      fp->_wide_data->_wide_vtable = &_IO_wfile_jumps_mmap;
+      _IO_JUMPS_FILE_plus_SET (fp, &_IO_file_jumps_mmap);
+      _IO_WIDE_JUMPS_SET (fp, &_IO_wfile_jumps_mmap);
     }
 
   return result;
@@ -703,10 +703,10 @@  mmap_remap_check (_IO_FILE *fp)
       fp->_IO_buf_base = fp->_IO_buf_end = NULL;
       _IO_setg (fp, NULL, NULL, NULL);
       if (fp->_mode <= 0)
-	_IO_JUMPS_FILE_plus (fp) = &_IO_file_jumps;
+	_IO_JUMPS_FILE_plus_SET (fp, &_IO_file_jumps);
       else
-	_IO_JUMPS_FILE_plus (fp) = &_IO_wfile_jumps;
-      fp->_wide_data->_wide_vtable = &_IO_wfile_jumps;
+	_IO_JUMPS_FILE_plus_SET (fp, &_IO_wfile_jumps);
+      _IO_WIDE_JUMPS_SET (fp, &_IO_wfile_jumps);
 
       return 1;
     }
@@ -773,10 +773,10 @@  decide_maybe_mmap (_IO_FILE *fp)
 	      fp->_offset = st.st_size;
 
 	      if (fp->_mode <= 0)
-		_IO_JUMPS_FILE_plus (fp) = &_IO_file_jumps_mmap;
+		_IO_JUMPS_FILE_plus_SET (fp, &_IO_file_jumps_mmap);
 	      else
-		_IO_JUMPS_FILE_plus (fp) = &_IO_wfile_jumps_mmap;
-	      fp->_wide_data->_wide_vtable = &_IO_wfile_jumps_mmap;
+		_IO_JUMPS_FILE_plus_SET (fp, &_IO_wfile_jumps_mmap);
+	      _IO_WIDE_JUMPS_SET (fp, &_IO_wfile_jumps_mmap);
 
 	      return;
 	    }
@@ -786,10 +786,10 @@  decide_maybe_mmap (_IO_FILE *fp)
   /* We couldn't use mmap, so revert to the vanilla file operations.  */
 
   if (fp->_mode <= 0)
-    _IO_JUMPS_FILE_plus (fp) = &_IO_file_jumps;
+    _IO_JUMPS_FILE_plus_SET (fp, &_IO_file_jumps);
   else
-    _IO_JUMPS_FILE_plus (fp) = &_IO_wfile_jumps;
-  fp->_wide_data->_wide_vtable = &_IO_wfile_jumps;
+    _IO_JUMPS_FILE_plus_SET (fp, &_IO_wfile_jumps);
+  _IO_WIDE_JUMPS_SET (fp, &_IO_wfile_jumps);
 }
 
 int
diff --git a/libio/freopen.c b/libio/freopen.c
index 8a2a417..466cce6 100644
--- a/libio/freopen.c
+++ b/libio/freopen.c
@@ -56,16 +56,16 @@  freopen (const char *filename, const char *mode, FILE *fp)
 	 to the old libio may be passed into shared C library and wind
 	 up here. */
       _IO_old_file_close_it (fp);
-      _IO_JUMPS_FILE_plus (fp) = &_IO_old_file_jumps;
+      _IO_JUMPS_FILE_plus_SET (fp, &_IO_old_file_jumps);
       result = _IO_old_file_fopen (fp, gfilename, mode);
     }
   else
 #endif
     {
       _IO_file_close_it (fp);
-      _IO_JUMPS_FILE_plus (fp) = &_IO_file_jumps;
+      _IO_JUMPS_FILE_plus_SET (fp, &_IO_file_jumps);
       if (_IO_vtable_offset (fp) == 0 && fp->_wide_data != NULL)
-	fp->_wide_data->_wide_vtable = &_IO_wfile_jumps;
+	_IO_WIDE_JUMPS_SET (fp, &_IO_wfile_jumps);
       result = _IO_file_fopen (fp, gfilename, mode, 1);
       if (result != NULL)
 	result = __fopen_maybe_mmap (result);
diff --git a/libio/freopen64.c b/libio/freopen64.c
index ba85c3e..7eccb3e 100644
--- a/libio/freopen64.c
+++ b/libio/freopen64.c
@@ -47,9 +47,9 @@  freopen64 (const char *filename, const char *mode, FILE *fp)
 			   ? fd_to_filename (fd) : filename);
   fp->_flags2 |= _IO_FLAGS2_NOCLOSE;
   _IO_file_close_it (fp);
-  _IO_JUMPS_FILE_plus (fp) = &_IO_file_jumps;
+  _IO_JUMPS_FILE_plus_SET (fp, &_IO_file_jumps);
   if (_IO_vtable_offset (fp) == 0 && fp->_wide_data != NULL)
-    fp->_wide_data->_wide_vtable = &_IO_wfile_jumps;
+    _IO_WIDE_JUMPS_SET (fp, &_IO_wfile_jumps);
   result = _IO_file_fopen (fp, gfilename, mode, 0);
   fp->_flags2 &= ~_IO_FLAGS2_NOCLOSE;
   if (result != NULL)
diff --git a/libio/genops.c b/libio/genops.c
index 5803cbf..8542f78 100644
--- a/libio/genops.c
+++ b/libio/genops.c
@@ -615,7 +615,7 @@  _IO_no_init (_IO_FILE *fp, int flags, int orientation,
       fp->_wide_data->_IO_backup_base = NULL;
       fp->_wide_data->_IO_save_end = NULL;
 
-      fp->_wide_data->_wide_vtable = jmp;
+      _IO_WIDE_JUMPS_SET (fp, jmp);
     }
   else
     /* Cause predictable crash when a wide function is called on a byte
diff --git a/libio/iofdopen.c b/libio/iofdopen.c
index e00f337..8633055 100644
--- a/libio/iofdopen.c
+++ b/libio/iofdopen.c
@@ -148,11 +148,11 @@  _IO_new_fdopen (int fd, const char *mode)
 	       ? &_IO_wfile_jumps_maybe_mmap :
 #endif
 	       &_IO_wfile_jumps);
-  _IO_JUMPS (&new_f->fp) =
+  _IO_JUMPS_SET (&new_f->fp,
 #ifdef _G_HAVE_MMAP
     (use_mmap && (read_write & _IO_NO_WRITES)) ? &_IO_file_jumps_maybe_mmap :
 #endif
-      &_IO_file_jumps;
+      &_IO_file_jumps);
   _IO_file_init (&new_f->fp);
 #if  !_IO_UNIFIED_JUMPTABLES
   new_f->fp.vtable = NULL;
diff --git a/libio/iofopen.c b/libio/iofopen.c
index 13e3910..6195fec 100644
--- a/libio/iofopen.c
+++ b/libio/iofopen.c
@@ -46,10 +46,10 @@  __fopen_maybe_mmap (_IO_FILE *fp)
 	 vanilla file operations and reset the jump table accordingly.  */
 
       if (fp->_mode <= 0)
-	_IO_JUMPS_FILE_plus (fp) = &_IO_file_jumps_maybe_mmap;
+	_IO_JUMPS_FILE_plus_SET (fp, &_IO_file_jumps_maybe_mmap);
       else
-	_IO_JUMPS_FILE_plus (fp) = &_IO_wfile_jumps_maybe_mmap;
-      fp->_wide_data->_wide_vtable = &_IO_wfile_jumps_maybe_mmap;
+	_IO_JUMPS_FILE_plus_SET (fp, &_IO_wfile_jumps_maybe_mmap);
+      _IO_WIDE_JUMPS_SET (fp, &_IO_wfile_jumps_maybe_mmap);
     }
 #endif
   return fp;
@@ -78,7 +78,7 @@  __fopen_internal (const char *filename, const char *mode, int is32)
 #else
   _IO_no_init (&new_f->fp.file, 1, 0, NULL, NULL);
 #endif
-  _IO_JUMPS (&new_f->fp) = &_IO_file_jumps;
+  _IO_JUMPS_SET (&new_f->fp, &_IO_file_jumps);
   _IO_file_init (&new_f->fp);
 #if  !_IO_UNIFIED_JUMPTABLES
   new_f->fp.vtable = NULL;
diff --git a/libio/iofopncook.c b/libio/iofopncook.c
index 9eda7c1..c745e0c 100644
--- a/libio/iofopncook.c
+++ b/libio/iofopncook.c
@@ -131,7 +131,7 @@  _IO_cookie_init (struct _IO_cookie_file *cfile, int read_write,
 		 void *cookie, _IO_cookie_io_functions_t io_functions)
 {
   _IO_init (&cfile->__fp.file, 0);
-  _IO_JUMPS (&cfile->__fp) = &_IO_cookie_jumps;
+  _IO_JUMPS_SET (&cfile->__fp, &_IO_cookie_jumps);
 
   cfile->__cookie = cookie;
   cfile->__io_functions = io_functions;
@@ -249,7 +249,7 @@  _IO_old_fopencookie (void *cookie, const char *mode,
 
   ret = _IO_fopencookie (cookie, mode, io_functions);
   if (ret != NULL)
-    _IO_JUMPS_FILE_plus (ret) = &_IO_old_cookie_jumps;
+    _IO_JUMPS_FILE_plus_SET (ret, &_IO_old_cookie_jumps);
 
   return ret;
 }
diff --git a/libio/iofwide.c b/libio/iofwide.c
index bc82e8b..9137d77 100644
--- a/libio/iofwide.c
+++ b/libio/iofwide.c
@@ -182,7 +182,7 @@  _IO_fwide (_IO_FILE *fp, int mode)
 #endif
 
       /* From now on use the wide character callback functions.  */
-      _IO_JUMPS_FILE_plus (fp) = fp->_wide_data->_wide_vtable;
+      _IO_JUMPS_FILE_plus_SET (fp, _IO_WIDE_JUMPS_FUNC (fp));
     }
 
   /* Set the mode now.  */
diff --git a/libio/iopopen.c b/libio/iopopen.c
index 9ddde23..6029c71 100644
--- a/libio/iopopen.c
+++ b/libio/iopopen.c
@@ -288,7 +288,7 @@  _IO_new_popen (const char *command, const char *mode)
 #endif
   fp = &new_f->fpx.file.file;
   _IO_init (fp, 0);
-  _IO_JUMPS (&new_f->fpx.file) = &_IO_proc_jumps;
+  _IO_JUMPS_SET (&new_f->fpx.file, &_IO_proc_jumps);
   _IO_new_file_init (&new_f->fpx.file);
 #if  !_IO_UNIFIED_JUMPTABLES
   new_f->fpx.file.vtable = NULL;
diff --git a/libio/iovdprintf.c b/libio/iovdprintf.c
index 8ca55fc..f3dcb9a 100644
--- a/libio/iovdprintf.c
+++ b/libio/iovdprintf.c
@@ -38,7 +38,7 @@  _IO_vdprintf (int d, const char *format, _IO_va_list arg)
   tmpfil.file._lock = NULL;
 #endif
   _IO_no_init (&tmpfil.file, _IO_USER_LOCK, 0, &wd, &_IO_wfile_jumps);
-  _IO_JUMPS (&tmpfil) = &_IO_file_jumps;
+  _IO_JUMPS_SET (&tmpfil, &_IO_file_jumps);
   _IO_file_init (&tmpfil);
 #if  !_IO_UNIFIED_JUMPTABLES
   tmpfil.vtable = NULL;
diff --git a/libio/iovsprintf.c b/libio/iovsprintf.c
index 712d178..6baa376 100644
--- a/libio/iovsprintf.c
+++ b/libio/iovsprintf.c
@@ -37,7 +37,7 @@  __IO_vsprintf (char *string, const char *format, _IO_va_list args)
   sf._sbf._f._lock = NULL;
 #endif
   _IO_no_init (&sf._sbf._f, _IO_USER_LOCK, -1, NULL, NULL);
-  _IO_JUMPS (&sf._sbf) = &_IO_str_jumps;
+  _IO_JUMPS_SET (&sf._sbf, &_IO_str_jumps);
   _IO_str_init_static_internal (&sf, string, -1, string);
   ret = _IO_vfprintf (&sf._sbf._f, format, args);
   _IO_putc_unlocked ('\0', &sf._sbf._f);
diff --git a/libio/iovsscanf.c b/libio/iovsscanf.c
index 18d9aaa..07db19a 100644
--- a/libio/iovsscanf.c
+++ b/libio/iovsscanf.c
@@ -36,7 +36,7 @@  _IO_vsscanf (const char *string, const char *format, _IO_va_list args)
   sf._sbf._f._lock = NULL;
 #endif
   _IO_no_init (&sf._sbf._f, _IO_USER_LOCK, -1, NULL, NULL);
-  _IO_JUMPS (&sf._sbf) = &_IO_str_jumps;
+  _IO_JUMPS_SET (&sf._sbf, &_IO_str_jumps);
   _IO_str_init_static_internal (&sf, (char*)string, 0, NULL);
   ret = _IO_vfscanf (&sf._sbf._f, format, args, NULL);
   return ret;
diff --git a/libio/libioP.h b/libio/libioP.h
index 8706af2..3efdde8 100644
--- a/libio/libioP.h
+++ b/libio/libioP.h
@@ -70,17 +70,16 @@  extern "C" {
 
 /* THE JUMPTABLE FUNCTIONS.
 
- * The _IO_FILE type is used to implement the FILE type in GNU libc,
- * as well as the streambuf class in GNU iostreams for C++.
- * These are all the same, just used differently.
- * An _IO_FILE (or FILE) object is allows followed by a pointer to
- * a jump table (of pointers to functions).  The pointer is accessed
- * with the _IO_JUMPS macro.  The jump table has an eccentric format,
- * so as to be compatible with the layout of a C++ virtual function table.
- * (as implemented by g++).  When a pointer to a streambuf object is
- * coerced to an (_IO_FILE*), then _IO_JUMPS on the result just
- * happens to point to the virtual function table of the streambuf.
- * Thus the _IO_JUMPS function table used for C stdio/libio does
+ * The _IO_FILE type is used to implement the FILE type in GNU libc, as well
+ * as the streambuf class in GNU iostreams for C++. These are all the same,
+ * just used differently. An _IO_FILE (or FILE) object is always followed by
+ * a pointer to a jump table (of pointers to functions). The pointer is
+ * accessed with the _IO_JUMPS_SET and _IO_JUMPS_FUNC macros. The jump table
+ * has an eccentric format, so as to be compatible with the layout of a C++
+ * virtual function table (as implemented by g++). When a pointer to a
+ * streambuf object is coerced to an (_IO_FILE*), then _IO_JUMPS on the
+ * result just happens to point to the virtual function table of the
+ * streambuf. Thus the _IO_JUMPS function table used for C stdio/libio does
  * double duty as the virtual function table for C++ streambuf.
  *
  * The entries in the _IO_JUMPS function table (and hence also the
@@ -106,6 +105,17 @@  extern "C" {
 # define _IO_JUMPS_OFFSET 0
 #endif
 
+static inline void
+_IO_mangle_vtable (const struct _IO_jump_t **vtable,
+		   const struct _IO_jump_t *table)
+{
+  struct _IO_jump_t *ptr = (struct _IO_jump_t *)table;
+#ifndef USE_COMPAT_LIBIO
+  PTR_MANGLE (ptr);
+#endif
+  *vtable = ptr;
+}
+
 /* Type of MEMBER in struct type TYPE.  */
 #define _IO_MEMBER_TYPE(TYPE, MEMBER) __typeof__ (((TYPE){}).MEMBER)
 
@@ -115,24 +125,43 @@  extern "C" {
   (*(_IO_MEMBER_TYPE (TYPE, MEMBER) *)(((char *) (THIS)) \
 				       + offsetof(TYPE, MEMBER)))
 
-#define _IO_JUMPS(THIS) (THIS)->vtable
-#define _IO_JUMPS_FILE_plus(THIS) \
+#define _IO_JUMPS_RAW(THIS) (THIS)->vtable
+#define _IO_JUMPS_SET(THIS, TABLE) \
+  _IO_mangle_vtable (&_IO_JUMPS_RAW (THIS), (TABLE))
+
+#define _IO_JUMPS_FILE_plus_RAW(THIS) \
   _IO_CAST_FIELD_ACCESS ((THIS), struct _IO_FILE_plus, vtable)
-#define _IO_WIDE_JUMPS(THIS) \
+#define _IO_JUMPS_FILE_plus_SET(THIS, TABLE) \
+  _IO_mangle_vtable (&_IO_JUMPS_FILE_plus_RAW (THIS), (TABLE))
+
+#define _IO_WIDE_JUMPS_RAW(THIS) \
   _IO_CAST_FIELD_ACCESS ((THIS), struct _IO_FILE, _wide_data)->_wide_vtable
+#define _IO_WIDE_JUMPS_SET(THIS, TABLE) \
+  _IO_mangle_vtable (&_IO_WIDE_JUMPS_RAW (THIS), (TABLE))
+
 #define _IO_CHECK_WIDE(THIS) \
   (_IO_CAST_FIELD_ACCESS ((THIS), struct _IO_FILE, _wide_data) != NULL)
 
+static inline const struct _IO_jump_t *
+_IO_demangle_vtable (const struct _IO_jump_t *vtable)
+{
+  struct _IO_jump_t *ptr = (struct _IO_jump_t *)vtable;
+#ifndef USE_COMPAT_LIBIO
+  PTR_DEMANGLE (ptr);
+#endif
+  return (const struct _IO_jump_t *)ptr;
+}
+
 #if _IO_JUMPS_OFFSET
-# define _IO_JUMPS_FUNC(THIS) \
- (*(struct _IO_jump_t **) ((void *) &_IO_JUMPS_FILE_plus (THIS) \
-			   + (THIS)->_vtable_offset))
+# define _IO_JUMPS_FUNC(THIS) _IO_demangle_vtable (\
+ (*(struct _IO_jump_t **) ((void *) &_IO_JUMPS_FILE_plus_RAW (THIS) \
+              + (THIS)->_vtable_offset)))
 # define _IO_vtable_offset(THIS) (THIS)->_vtable_offset
 #else
-# define _IO_JUMPS_FUNC(THIS) _IO_JUMPS_FILE_plus (THIS)
+# define _IO_JUMPS_FUNC(THIS) _IO_demangle_vtable (_IO_JUMPS_FILE_plus_RAW (THIS))
 # define _IO_vtable_offset(THIS) 0
 #endif
-#define _IO_WIDE_JUMPS_FUNC(THIS) _IO_WIDE_JUMPS(THIS)
+#define _IO_WIDE_JUMPS_FUNC(THIS) _IO_demangle_vtable (_IO_WIDE_JUMPS_RAW (THIS))
 #define JUMP_FIELD(TYPE, NAME) TYPE NAME
 #define JUMP0(FUNC, THIS) (_IO_JUMPS_FUNC(THIS)->FUNC) (THIS)
 #define JUMP1(FUNC, THIS, X1) (_IO_JUMPS_FUNC(THIS)->FUNC) (THIS, X1)
diff --git a/libio/memstream.c b/libio/memstream.c
index 7fa5245..6ad4355 100644
--- a/libio/memstream.c
+++ b/libio/memstream.c
@@ -87,7 +87,7 @@  __open_memstream (char **bufloc, _IO_size_t *sizeloc)
       return NULL;
     }
   _IO_init (&new_f->fp._sf._sbf._f, 0);
-  _IO_JUMPS_FILE_plus (&new_f->fp._sf._sbf) = &_IO_mem_jumps;
+  _IO_JUMPS_FILE_plus_SET (&new_f->fp._sf._sbf, &_IO_mem_jumps);
   _IO_str_init_static_internal (&new_f->fp._sf, buf, _IO_BUFSIZ, buf);
   new_f->fp._sf._sbf._f._flags &= ~_IO_USER_BUF;
   new_f->fp._sf._s._allocate_buffer = (_IO_alloc_type) malloc;
diff --git a/libio/obprintf.c b/libio/obprintf.c
index aa17b46..1452b8d 100644
--- a/libio/obprintf.c
+++ b/libio/obprintf.c
@@ -132,7 +132,7 @@  _IO_obstack_vprintf (struct obstack *obstack, const char *format, va_list args)
 #endif
 
   _IO_no_init (&new_f.ofile.file.file, _IO_USER_LOCK, -1, NULL, NULL);
-  _IO_JUMPS (&new_f.ofile.file) = &_IO_obstack_jumps;
+  _IO_JUMPS_SET (&new_f.ofile.file, &_IO_obstack_jumps);
   room = obstack_room (obstack);
   size = obstack_object_size (obstack) + room;
   if (size == 0)
diff --git a/libio/oldiofdopen.c b/libio/oldiofdopen.c
index 33406ff..b99e39f 100644
--- a/libio/oldiofdopen.c
+++ b/libio/oldiofdopen.c
@@ -111,7 +111,7 @@  _IO_old_fdopen (int fd, const char *mode)
   new_f->fp.file._file._lock = &new_f->lock;
 #endif
   _IO_old_init (&new_f->fp.file._file, 0);
-  _IO_JUMPS_FILE_plus (&new_f->fp) = &_IO_old_file_jumps;
+  _IO_JUMPS_FILE_plus_SET (&new_f->fp, &_IO_old_file_jumps);
   _IO_old_file_init ((struct _IO_FILE_plus *) &new_f->fp);
 #if  !_IO_UNIFIED_JUMPTABLES
   new_f->fp.vtable = NULL;
diff --git a/libio/oldiofopen.c b/libio/oldiofopen.c
index cc7c342..89cb848 100644
--- a/libio/oldiofopen.c
+++ b/libio/oldiofopen.c
@@ -50,7 +50,7 @@  _IO_old_fopen (const char *filename, const char *mode)
   new_f->fp.file._file._lock = &new_f->lock;
 #endif
   _IO_old_init (&new_f->fp.file._file, 0);
-  _IO_JUMPS_FILE_plus (&new_f->fp) = &_IO_old_file_jumps;
+  _IO_JUMPS_FILE_plus_SET (&new_f->fp, &_IO_old_file_jumps);
   _IO_old_file_init ((struct _IO_FILE_plus *) &new_f->fp);
 #if  !_IO_UNIFIED_JUMPTABLES
   new_f->fp.vtable = NULL;
diff --git a/libio/oldiopopen.c b/libio/oldiopopen.c
index ea75b4f..dec2b3c 100644
--- a/libio/oldiopopen.c
+++ b/libio/oldiopopen.c
@@ -210,7 +210,7 @@  _IO_old_popen (const char *command, const char *mode)
 #endif
   fp = &new_f->fpx.file.file._file;
   _IO_old_init (fp, 0);
-  _IO_JUMPS_FILE_plus (&new_f->fpx.file) = &_IO_old_proc_jumps;
+  _IO_JUMPS_FILE_plus_SET (&new_f->fpx.file, &_IO_old_proc_jumps);
   _IO_old_file_init ((struct _IO_FILE_plus *) &new_f->fpx.file);
 #if  !_IO_UNIFIED_JUMPTABLES
   new_f->fpx.file.vtable = NULL;
diff --git a/libio/vasprintf.c b/libio/vasprintf.c
index 7460f1e..146bd36 100644
--- a/libio/vasprintf.c
+++ b/libio/vasprintf.c
@@ -51,7 +51,7 @@  _IO_vasprintf (char **result_ptr, const char *format, _IO_va_list args)
   sf._sbf._f._lock = NULL;
 #endif
   _IO_no_init (&sf._sbf._f, _IO_USER_LOCK, -1, NULL, NULL);
-  _IO_JUMPS (&sf._sbf) = &_IO_str_jumps;
+  _IO_JUMPS_SET (&sf._sbf, &_IO_str_jumps);
   _IO_str_init_static_internal (&sf, string, init_string_size, string);
   sf._sbf._f._flags &= ~_IO_USER_BUF;
   sf._s._allocate_buffer = (_IO_alloc_type) malloc;
diff --git a/libio/vsnprintf.c b/libio/vsnprintf.c
index f1063a1..75a15a9 100644
--- a/libio/vsnprintf.c
+++ b/libio/vsnprintf.c
@@ -108,7 +108,7 @@  _IO_vsnprintf (char *string, _IO_size_t maxlen, const char *format,
     }
 
   _IO_no_init (&sf.f._sbf._f, _IO_USER_LOCK, -1, NULL, NULL);
-  _IO_JUMPS (&sf.f._sbf) = &_IO_strn_jumps;
+  _IO_JUMPS_SET (&sf.f._sbf, &_IO_strn_jumps);
   string[0] = '\0';
   _IO_str_init_static_internal (&sf.f, string, maxlen - 1, string);
   ret = _IO_vfprintf (&sf.f._sbf._f, format, args);
diff --git a/manual/install.texi b/manual/install.texi
index 95021b4..c150ee0 100644
--- a/manual/install.texi
+++ b/manual/install.texi
@@ -146,6 +146,11 @@  This is not recommended because it defeats the purpose of NSS; a program
 linked statically with the NSS libraries cannot be dynamically
 reconfigured to use a different name database.
 
+@item --enable-libio-compatibility
+Compile libio without PTR_MANGLE of the FILE vtable, in order to remain
+compatibile with pre-@file{libstdc++.so.6} binaries that may be sharing the
+vtable (i.e. roughly pre-2007 binaries).
+
 @item --without-tls
 By default the C library is built with support for thread-local storage
 if the used tools support it.  By using @samp{--without-tls} this can be
diff --git a/misc/init-misc.c b/misc/init-misc.c
index a9bf1da..7491500 100644
--- a/misc/init-misc.c
+++ b/misc/init-misc.c
@@ -16,7 +16,11 @@ 
    License along with the GNU C Library; if not, see
    <http://www.gnu.org/licenses/>.  */
 
+#include "libioP.h"
+
 #include <string.h>
+#include <stdio.h>
+#include <sysdep.h>
 #include <libc-internal.h>
 
 char *__progname_full = (char *) "";
@@ -37,4 +41,15 @@  __init_misc (int argc, char **argv, char **envp)
 	__progname = p + 1;
       __progname_full = argv[0];
     }
+
+#ifndef USE_COMPAT_LIBIO
+  PTR_MANGLE (_IO_JUMPS_FILE_plus_RAW (stdin));
+  PTR_MANGLE (_IO_WIDE_JUMPS_RAW (stdin));
+
+  PTR_MANGLE (_IO_JUMPS_FILE_plus_RAW (stdout));
+  PTR_MANGLE (_IO_WIDE_JUMPS_RAW (stdout));
+
+  PTR_MANGLE (_IO_JUMPS_FILE_plus_RAW (stderr));
+  PTR_MANGLE (_IO_WIDE_JUMPS_RAW (stderr));
+#endif
 }
diff --git a/stdio-common/isoc99_vsscanf.c b/stdio-common/isoc99_vsscanf.c
index 720f122..edbcf72 100644
--- a/stdio-common/isoc99_vsscanf.c
+++ b/stdio-common/isoc99_vsscanf.c
@@ -37,7 +37,7 @@  __isoc99_vsscanf (const char *string, const char *format, _IO_va_list args)
   sf._sbf._f._lock = NULL;
 #endif
   _IO_no_init (&sf._sbf._f, _IO_USER_LOCK, -1, NULL, NULL);
-  _IO_JUMPS (&sf._sbf) = &_IO_str_jumps;
+  _IO_JUMPS_SET (&sf._sbf, &_IO_str_jumps);
   _IO_str_init_static_internal (&sf, (char*)string, 0, NULL);
   sf._sbf._f._flags2 |= _IO_FLAGS2_SCANF_STD;
   ret = _IO_vfscanf (&sf._sbf._f, format, args, NULL);
diff --git a/stdio-common/vfprintf.c b/stdio-common/vfprintf.c
index f24020a..392b48d 100644
--- a/stdio-common/vfprintf.c
+++ b/stdio-common/vfprintf.c
@@ -2318,7 +2318,7 @@  buffered_vfprintf (_IO_FILE *s, const CHAR_T *format,
   hp->_lock = NULL;
 #endif
   hp->_flags2 = s->_flags2;
-  _IO_JUMPS (&helper._f) = (struct _IO_jump_t *) &_IO_helper_jumps;
+  _IO_JUMPS_SET (&helper._f, (struct _IO_jump_t *) &_IO_helper_jumps);
 
   /* Now print to helper instead.  */
 #ifndef COMPILE_WPRINTF
diff --git a/stdlib/strfmon_l.c b/stdlib/strfmon_l.c
index 5851a5b..5efaab0 100644
--- a/stdlib/strfmon_l.c
+++ b/stdlib/strfmon_l.c
@@ -513,7 +513,7 @@  __vstrfmon_l (char *s, size_t maxsize, __locale_t loc, const char *format,
       f._sbf._f._lock = NULL;
 #endif
       _IO_init (&f._sbf._f, 0);
-      _IO_JUMPS (&f._sbf) = &_IO_str_jumps;
+      _IO_JUMPS_SET (&f._sbf, &_IO_str_jumps);
       _IO_str_init_static_internal (&f, dest, (s + maxsize) - dest, dest);
       /* We clear the last available byte so we can find out whether
 	 the numeric representation is too long.  */