Alternative libio vtable hardening approach

Message ID b34105f2-adcb-9347-73c0-43079729c418@redhat.com
State Not applicable
Headers

Commit Message

Florian Weimer May 31, 2016, 1:07 p.m. UTC
  I have implemented a completely different approach to vtable hardening.

The basic idea is to put all libc vtables into a single array, and then 
check whether the vtable pointer is within that array.

There is a new flag, IO_accept_foreign_vtables, which is initially false 
and set to true to indicate that vtables may have been defined outside 
of libc.  This allows us to preserve full backwards compatibility.

The attached patch is not completely finished.  We need to disable 
vtable validation on both sides of a static dlopen boundary because the 
vtables are not shared across the boundary.  This is what causes 
dlfcn/tststatic2 to fail.  We also may have to set the 
IO_accept_foreign_vtables flag for additional constructor functions 
(although _IO_file_init may be sufficient, based on what I have seen in 
GCC).

A future optimization would change the virtual function calls to compare 
the function pointer in the vtable against an expected function pointer. 
  Only if that comparison fails, the vtable pointer is validated.  This 
will allow us to inline parts of xsputn into vfprintf, especially if we 
change the various implementations to share more code.

At this point, I'm mainly interested in comments whether the use of the 
flag is acceptable from a security perspective.  I expect that if you 
can set the flag and overwrite vtable pointers, you already have 
substantial control over what the process does.  It is also likely that 
you would able to reset the pointer guard variable, disabling the 
hardening in Kees' patch.

Florian
  

Comments

Carlos O'Donell May 31, 2016, 3:09 p.m. UTC | #1
Florian,

Overall the patch looks great. I like the idea, it's fast, and simple
to review and implement.

We need to store some state regarding the vtable checking and there is
no way to avoid that, either TP+offset or global are some of our cheapest
solutions.

The question I'm asking myself is: Should we be pointer mangling any of
these pointers to make it harder to scan for the table?

> The attached patch is not completely finished.  We need to disable
> vtable validation on both sides of a static dlopen boundary because
> the vtables are not shared across the boundary.  This is what causes
> dlfcn/tststatic2 to fail.

How do you plan to disable the hardening at the static dlopen boundary?
I can see it being simple to disable the checks entirely for static
binaries, but the dlopen'd runtime won't know that and it will continue
to want to check the vtable values for any FILE objects passed to functions
of the loaded dynamic runtime? It seems to me that we're up against the
same problem of shared state across static/dynamic that we have discussed
before?

What we really want is a set of internal initializers that are run during
static dlopen / dlmopen that allow the library to copy key state into the
other namespace.

I don't suggest that this block your forward progress on this issue
thought, I'm just raising the fact that I don't know how to solve this
problem.
 
> A future optimization would change the virtual function calls to
> compare the function pointer in the vtable against an expected
> function pointer.  Only if that comparison fails, the vtable pointer
> is validated.  This will allow us to inline parts of xsputn into
> vfprintf, especially if we change the various implementations to
> share more code.

You suggest this because a pointer comparison is cheaper than an
address range comparison? Could we make the tables read-only and use
another flag to elide all checking?

> At this point, I'm mainly interested in comments whether the use of
> the flag is acceptable from a security perspective.  I expect that if
> you can set the flag and overwrite vtable pointers, you already have
> substantial control over what the process does.  It is also likely
> that you would able to reset the pointer guard variable, disabling
> the hardening in Kees' patch.

Agreed. My only worry is static dlopen.
  
Florian Weimer May 31, 2016, 3:56 p.m. UTC | #2
On 05/31/2016 05:09 PM, Carlos O'Donell wrote:

> The question I'm asking myself is: Should we be pointer mangling any of
> these pointers to make it harder to scan for the table?

I'm not sure what you are trying to address.  The table itself should be 
in RELRO memory, and its address should be at a fixed offset relative to 
the PC at the site of the check, so all that should be pretty safe.

I'm only worried about the flag that turns of the check.

>> The attached patch is not completely finished.  We need to disable
>> vtable validation on both sides of a static dlopen boundary because
>> the vtables are not shared across the boundary.  This is what causes
>> dlfcn/tststatic2 to fail.
>
> How do you plan to disable the hardening at the static dlopen boundary?

Set the flag on both sides once dlopen is called.  I see no way to avoid 
that because we cannot know if any of the functions in the loaded DSOs 
access stdio streams on both sides of the boundary.

We could presumably add a flag that inhibits this for internal DSOs 
which we know do not have such behavior.  But I'm not sure if this is 
worthwhile.

> What we really want is a set of internal initializers that are run during
> static dlopen / dlmopen that allow the library to copy key state into the
> other namespace.

We don't need pass across any actual data, or affect relocation, so it's 
easier.  malloc in libc.so does the following to detect whether it's 
running across a linking boundary:

#ifdef SHARED
   /* In case this libc copy is in a non-default namespace, never use brk.
      Likewise if dlopened from statically linked program.  */
   Dl_info di;
   struct link_map *l;

   if (_dl_open_hook != NULL
       || (_dl_addr (ptmalloc_init, &di, &l, NULL) != 0
           && l->l_ns != LM_ID_BASE))
     __morecore = __failing_morecore;
#endif

I just need to stick this code in some place where it runs early enough 
(before any ELF constructors and the like).  Suggestions welcome. :)

(And we already have some mechanism because it seems that the dlopen 
hooks are sent across.  But I don't have all the details.)

>> A future optimization would change the virtual function calls to
>> compare the function pointer in the vtable against an expected
>> function pointer.  Only if that comparison fails, the vtable pointer
>> is validated.  This will allow us to inline parts of xsputn into
>> vfprintf, especially if we change the various implementations to
>> share more code.
>
> You suggest this because a pointer comparison is cheaper than an
> address range comparison? Could we make the tables read-only and use
> another flag to elide all checking?

The current checking code looks like this:

      a27:       48 8b 85 80 fb ff ff    mov    -0x480(%rbp),%rax
      a2e:       4c 8b a0 d8 00 00 00    mov    0xd8(%rax),%r12
      a35:       49 81 fc 00 00 00 00    cmp    $IO_vtables,%r12
      a3c:       0f 82 0e 03 00 00       jb     d50
      a42:       49 81 fc 00 00 00 00    cmp    $IO_vtables+0xbd0,%r12
      a49:       0f 83 01 03 00 00       jae    d50
      a4f:       49 8b 75 18             mov    0x18(%r13),%rsi
      a53:       49 8b 55 20             mov    0x20(%r13),%rdx
      a57:       48 8b bd 80 fb ff ff    mov    -0x480(%rbp),%rdi
      a5e:       48 29 f2                sub    %rsi,%rdx
      a61:       41 ff 54 24 38          callq  *0x38(%r12)
…
      d50:       e8 00 00 00 00          callq  IO_vtable_check
      d55:       e9 f5 fc ff ff          jmpq   a4f

Instead we could do something like this (the call setup sequence is 
somewhat different here):

      a40:       49 8b 44 24 38          mov    0x38(%r12),%rax
      a45:       48 29 f2                sub    %rsi,%rdx
      a48:       48 3d 00 00 00 00       cmp   $_IO_file_xsputn_fast,%rax
      a4e:       0f 85 f7 11 00 00       jne    1c4b
      a54:       4c 89 ff                mov    %r15,%rdi
      a57:       e8 00 00 00 00          callq  _IO_file_xsputn_fast
…
     1c4b:       49 81 fc 00 00 00 00    cmp    $IO_vtables+0xbd0,%r12
     1c52:       0f 83 4e 04 00 00       jae    20a6
     1c58:       49 81 fc 00 00 00 00    cmp    $IO_vtables,%r12
     1c5f:       0f 82 41 04 00 00       jb     20a6
     1c65:       4c 89 ff                mov    %r15,%rdi
     1c68:       ff d0                   callq  *%rax
     1c6a:       e9 ed ed ff ff          jmpq   a5c
…
     20a6:       48 89 95 78 fb ff ff    mov    %rdx,-0x488(%rbp)
     20ad:       48 89 b5 88 fb ff ff    mov    %rsi,-0x478(%rbp)
     20b4:       e8 00 00 00 00          callq  IO_vtable_check
     20b9:       49 8b 44 24 38          mov    0x38(%r12),%rax
     20be:       48 8b 95 78 fb ff ff    mov    -0x488(%rbp),%rdx
     20c5:       48 8b b5 88 fb ff ff    mov    -0x478(%rbp),%rsi
     20cc:       e9 94 fb ff ff          jmpq   1c65

So devirtualize the call to _IO_file_xsputn_fast, which can then be 
inlined.  If the slow path is only needed for compatibility (after we 
have carefully rewritten xsputn so that we only need one version, which 
seems doable), we could call some slow-path xsputn function, so the


      a4c:       48 8b 83 d8 00 00 00    mov    0xd8(%rbx),%rax
      a53:       48 29 f2                sub    %rsi,%rdx
      a56:       48 81 78 38 00 00 00    cmpq 
$_IO_file_xsputn_fast,0x38(%rax)
      a5e:       0f 85 e6 11 00 00       jne    1c4a
      a64:       e8 00 00 00 00          callq  _IO_file_xsputn_fast
…
     1c4a:       e8 00 00 00 00          callq  _IO_file_xsputn_slow
     1c4f:       e9 15 ee ff ff          jmpq   a69

This expansion has much more reasonable size, but only makes sense if 
the call site is monomorphic in practice.

> Agreed. My only worry is static dlopen.

My worry is that the hardening is insufficient. :)

Florian
  
Kees Cook May 31, 2016, 7:23 p.m. UTC | #3
On Tue, May 31, 2016 at 6:07 AM, Florian Weimer <fweimer@redhat.com> wrote:
> I have implemented a completely different approach to vtable hardening.
>
> The basic idea is to put all libc vtables into a single array, and then
> check whether the vtable pointer is within that array.
>
> There is a new flag, IO_accept_foreign_vtables, which is initially false and
> set to true to indicate that vtables may have been defined outside of libc.
> This allows us to preserve full backwards compatibility.
>
> The attached patch is not completely finished.  We need to disable vtable
> validation on both sides of a static dlopen boundary because the vtables are
> not shared across the boundary.  This is what causes dlfcn/tststatic2 to
> fail.  We also may have to set the IO_accept_foreign_vtables flag for
> additional constructor functions (although _IO_file_init may be sufficient,
> based on what I have seen in GCC).
>
> A future optimization would change the virtual function calls to compare the
> function pointer in the vtable against an expected function pointer.  Only
> if that comparison fails, the vtable pointer is validated.  This will allow
> us to inline parts of xsputn into vfprintf, especially if we change the
> various implementations to share more code.
>
> At this point, I'm mainly interested in comments whether the use of the flag
> is acceptable from a security perspective.  I expect that if you can set the
> flag and overwrite vtable pointers, you already have substantial control
> over what the process does.  It is also likely that you would able to reset
> the pointer guard variable, disabling the hardening in Kees' patch.

Well, this is certainly better than not having it, and the on/off
switch isn't in the FILE structure, so I would think this should be of
a similar protection level. Though, it'd be nice if a process could
opt-out for its entire lifetime. Right now, any call to _IO_file_init
disables the protection.

(Also, is the call to _IO_new_file_init in _IO_new_popen wrong? Should
that be the internal one instead?)

-Kees
  
Mike Frysinger May 31, 2016, 10:11 p.m. UTC | #4
On 31 May 2016 15:07, Florian Weimer wrote:
> At this point, I'm mainly interested in comments whether the use of the 
> flag is acceptable from a security perspective.  I expect that if you 
> can set the flag and overwrite vtable pointers, you already have 
> substantial control over what the process does.  It is also likely that 
> you would able to reset the pointer guard variable, disabling the 
> hardening in Kees' patch.

by that logic, why do we have PTR_MANGLE at all ?

seems like it'd be possible to use to arbitrary points to use as the
false/true values if we really wanted.  i.e. turn the new bool into a
pointer, then for "true", assign it a mangled value of the vtables
array, and for "false", assign it a mangled value of some other sym.
then even if someone could corrupt the variable, they wouldn't be
able to just stamp in "0" or "1".  only one or two points would need
to be updated, so the ugliness would be contained.

> +      JUMP_INIT(finish, _IO_file_finish),

i know the old code got the style wrong, but since you're writing a
whole new file from scratch, might as well fix the style issues too.
-mike
  
Carlos O'Donell June 1, 2016, 12:25 a.m. UTC | #5
On 05/31/2016 11:56 AM, Florian Weimer wrote:
> On 05/31/2016 05:09 PM, Carlos O'Donell wrote:
> 
>> The question I'm asking myself is: Should we be pointer mangling any of
>> these pointers to make it harder to scan for the table?
> 
> I'm not sure what you are trying to address. The table itself should
> be in RELRO memory, and its address should be at a fixed offset
> relative to the PC at the site of the check, so all that should be
> pretty safe.

That's exactly the question I'm asking.

If I understand your reasoning:

(a) The table is in .rodata/.data.rel.ro (verified present tables are in .rodata)

(b) The table is always PC-relative addressable from ROP gadgets
    within the library so finding it is easy.
    - Hiding the pointers with mangling doesn't make this harder.

(c) Mangling degrades performance.

Seems fine to me, I just wanted to be as explicit as possible for our
choices.

> I'm only worried about the flag that turns of the check.

I don't see any way to provide backwards compatibility than to disable
the check with a global.

We could encrypt the value of the global to make it harder to unset?

Mike suggests this too.

>>> The attached patch is not completely finished.  We need to disable
>>> vtable validation on both sides of a static dlopen boundary because
>>> the vtables are not shared across the boundary.  This is what causes
>>> dlfcn/tststatic2 to fail.
>>
>> How do you plan to disable the hardening at the static dlopen boundary?
> 
> Set the flag on both sides once dlopen is called. I see no way to
> avoid that because we cannot know if any of the functions in the
> loaded DSOs access stdio streams on both sides of the boundary.

You answer this below.
 
> We could presumably add a flag that inhibits this for internal DSOs
> which we know do not have such behavior. But I'm not sure if this is
> worthwhile.

Let us stay focused on the hardening aspect for this first change.
 
>> What we really want is a set of internal initializers that are run during
>> static dlopen / dlmopen that allow the library to copy key state into the
>> other namespace.
> 
> We don't need pass across any actual data, or affect relocation, so
> it's easier. malloc in libc.so does the following to detect whether
> it's running across a linking boundary:
> 
> #ifdef SHARED
>   /* In case this libc copy is in a non-default namespace, never use brk.
>      Likewise if dlopened from statically linked program.  */
>   Dl_info di;
>   struct link_map *l;
> 
>   if (_dl_open_hook != NULL
>       || (_dl_addr (ptmalloc_init, &di, &l, NULL) != 0
>           && l->l_ns != LM_ID_BASE))
>     __morecore = __failing_morecore;
> #endif

Ah, OK, that answers my question. You lookup your own link map and then
determine you're not in the default namespace and reset the variables.
That's a sufficient solution. Thanks.
 
> I just need to stick this code in some place where it runs early
> enough (before any ELF constructors and the like). Suggestions
> welcome. :)

That sounds good. Thanks.

> (And we already have some mechanism because it seems that the dlopen
> hooks are sent across. But I don't have all the details.)

Interesting.

Again, my thoughts were along the lines of some kind of callback
registration process where the old structure pointers are registered
along with a callback. Then in the new namespace the callback runs
with access to the new structure address (dynamic lookup) and does
some kind of custom copy to reinitialize the dynamic namespace.

That's a lot more work than what you propose. I do not suggest you
follow my suggestion. Stick with what works and solve the security
issue first.

>>> A future optimization would change the virtual function calls to
>>> compare the function pointer in the vtable against an expected
>>> function pointer.  Only if that comparison fails, the vtable pointer
>>> is validated.  This will allow us to inline parts of xsputn into
>>> vfprintf, especially if we change the various implementations to
>>> share more code.
>>
>> You suggest this because a pointer comparison is cheaper than an
>> address range comparison? Could we make the tables read-only and use
>> another flag to elide all checking?

Note that this was before I checked to verify the tables were in .rodata
or .data.rel.ro. The present tables are in .rodata.

> The current checking code looks like this:
> 
>      a27:       48 8b 85 80 fb ff ff    mov    -0x480(%rbp),%rax
>      a2e:       4c 8b a0 d8 00 00 00    mov    0xd8(%rax),%r12
>      a35:       49 81 fc 00 00 00 00    cmp    $IO_vtables,%r12
>      a3c:       0f 82 0e 03 00 00       jb     d50
>      a42:       49 81 fc 00 00 00 00    cmp    $IO_vtables+0xbd0,%r12
>      a49:       0f 83 01 03 00 00       jae    d50
>      a4f:       49 8b 75 18             mov    0x18(%r13),%rsi
>      a53:       49 8b 55 20             mov    0x20(%r13),%rdx
>      a57:       48 8b bd 80 fb ff ff    mov    -0x480(%rbp),%rdi
>      a5e:       48 29 f2                sub    %rsi,%rdx
>      a61:       41 ff 54 24 38          callq  *0x38(%r12)
> …
>      d50:       e8 00 00 00 00          callq  IO_vtable_check
>      d55:       e9 f5 fc ff ff          jmpq   a4f
> 
> Instead we could do something like this (the call setup sequence is somewhat different here):
> 
>      a40:       49 8b 44 24 38          mov    0x38(%r12),%rax
>      a45:       48 29 f2                sub    %rsi,%rdx
>      a48:       48 3d 00 00 00 00       cmp   $_IO_file_xsputn_fast,%rax
>      a4e:       0f 85 f7 11 00 00       jne    1c4b
>      a54:       4c 89 ff                mov    %r15,%rdi
>      a57:       e8 00 00 00 00          callq  _IO_file_xsputn_fast
> …
>     1c4b:       49 81 fc 00 00 00 00    cmp    $IO_vtables+0xbd0,%r12
>     1c52:       0f 83 4e 04 00 00       jae    20a6
>     1c58:       49 81 fc 00 00 00 00    cmp    $IO_vtables,%r12
>     1c5f:       0f 82 41 04 00 00       jb     20a6
>     1c65:       4c 89 ff                mov    %r15,%rdi
>     1c68:       ff d0                   callq  *%rax
>     1c6a:       e9 ed ed ff ff          jmpq   a5c
> …
>     20a6:       48 89 95 78 fb ff ff    mov    %rdx,-0x488(%rbp)
>     20ad:       48 89 b5 88 fb ff ff    mov    %rsi,-0x478(%rbp)
>     20b4:       e8 00 00 00 00          callq  IO_vtable_check
>     20b9:       49 8b 44 24 38          mov    0x38(%r12),%rax
>     20be:       48 8b 95 78 fb ff ff    mov    -0x488(%rbp),%rdx
>     20c5:       48 8b b5 88 fb ff ff    mov    -0x478(%rbp),%rsi
>     20cc:       e9 94 fb ff ff          jmpq   1c65
> 
> So devirtualize the call to _IO_file_xsputn_fast, which can then be
> inlined. If the slow path is only needed for compatibility (after we
> have carefully rewritten xsputn so that we only need one version,
> which seems doable), we could call some slow-path xsputn function, so
> the

Sounds doable.

> 
>      a4c:       48 8b 83 d8 00 00 00    mov    0xd8(%rbx),%rax
>      a53:       48 29 f2                sub    %rsi,%rdx
>      a56:       48 81 78 38 00 00 00    cmpq $_IO_file_xsputn_fast,0x38(%rax)
>      a5e:       0f 85 e6 11 00 00       jne    1c4a
>      a64:       e8 00 00 00 00          callq  _IO_file_xsputn_fast
> …
>     1c4a:       e8 00 00 00 00          callq  _IO_file_xsputn_slow
>     1c4f:       e9 15 ee ff ff          jmpq   a69
> 
> This expansion has much more reasonable size, but only makes sense if
> the call site is monomorphic in practice.

I don't follow. The slow path can still use the vtables as required by
legacy binaries and the call site there can be polymorphic? Maybe I've
misunderstood the suggestion.

>> Agreed. My only worry is static dlopen.
> 
> My worry is that the hardening is insufficient. :)

Would it worry you if I said "the hardening is _always_ insufficient." ;-)
  
Florian Weimer June 1, 2016, 8:34 a.m. UTC | #6
On 06/01/2016 12:11 AM, Mike Frysinger wrote:
> On 31 May 2016 15:07, Florian Weimer wrote:
>> At this point, I'm mainly interested in comments whether the use of the
>> flag is acceptable from a security perspective.  I expect that if you
>> can set the flag and overwrite vtable pointers, you already have
>> substantial control over what the process does.  It is also likely that
>> you would able to reset the pointer guard variable, disabling the
>> hardening in Kees' patch.
>
> by that logic, why do we have PTR_MANGLE at all ?

I looked at this some more.

I guess it's somewhat difficult to overwrite the pointer_guard cookie in 
the TCB.  Main thread and additional thread initialization are both 
right in the middle of functions, so you need to know the TCB address. 
I assume this is what it makes effective (if it is, I don't know).

The downside is that once the guard value leaks, it loses its 
effectiveness.  The vtable validation approach is not based on a secret 
and thus doesn't have this property.

> seems like it'd be possible to use to arbitrary points to use as the
> false/true values if we really wanted.  i.e. turn the new bool into a
> pointer, then for "true", assign it a mangled value of the vtables
> array, and for "false", assign it a mangled value of some other sym.
> then even if someone could corrupt the variable, they wouldn't be
> able to just stamp in "0" or "1".  only one or two points would need
> to be updated, so the ugliness would be contained.

Good idea.  It's easy to implement, and only the compatibility paths are 
affected.

(There is another caveat, see Kees' about the process opting out.)

>> +      JUMP_INIT(finish, _IO_file_finish),
>
> i know the old code got the style wrong, but since you're writing a
> whole new file from scratch, might as well fix the style issues too.

I'm going to turn this into a designated initializer and remove the 
macro altogether.  Currently, the macro drops the first argument.

Florian
  
Florian Weimer June 1, 2016, 8:56 a.m. UTC | #7
On 05/31/2016 09:23 PM, Kees Cook wrote:
> Well, this is certainly better than not having it, and the on/off
> switch isn't in the FILE structure, so I would think this should be of
> a similar protection level. Though, it'd be nice if a process could
> opt-out for its entire lifetime. Right now, any call to _IO_file_init
> disables the protection.

I don't quite see how to do this.  The machine code sequence to set the 
flag has to be in the process image to enable backwards compatibility 
when needed.  It doesn't really matter if this code is in an IFUNC 
handler or in a library subroutine.  And even if we have some 
precondition check (say, the IFUNC handler checks that we are in ld.so 
and relocation processing is running), execution could start after it.

We could have another flag, this time in read-only memory, which is some 
sort of tunable which can be tweaked by the system administrator. 
(Maybe we could even query the SELinux policy engine to get the flag.) 
The fallback path could check this flag as well.

In general, I want to avoid policy-based solutions because I'm told that 
in too many relevant scenarios, only the “anything goes” policy counts.

Do you think it's import to address this from the start, or can this 
wait until we have a better story for tunables?

> (Also, is the call to _IO_new_file_init in _IO_new_popen wrong? Should
> that be the internal one instead?)

Yes, these parts aren't completely done yet.

I also need to do something about static linking.  The increase in 
executable size appears to be rather moderate, but there are 
linknamespace issues.  I'll try to use weak function symbols for some of 
the methods (particularly the obstack ones), maybe this will help on 
both fronts.

Florian
  
Florian Weimer June 1, 2016, 9:12 a.m. UTC | #8
On 06/01/2016 02:25 AM, Carlos O'Donell wrote:
> On 05/31/2016 11:56 AM, Florian Weimer wrote:
>> On 05/31/2016 05:09 PM, Carlos O'Donell wrote:
>>
>>> The question I'm asking myself is: Should we be pointer mangling any of
>>> these pointers to make it harder to scan for the table?
>>
>> I'm not sure what you are trying to address. The table itself should
>> be in RELRO memory, and its address should be at a fixed offset
>> relative to the PC at the site of the check, so all that should be
>> pretty safe.
>
> That's exactly the question I'm asking.
>
> If I understand your reasoning:
>
> (a) The table is in .rodata/.data.rel.ro (verified present tables are in .rodata)
>
> (b) The table is always PC-relative addressable from ROP gadgets
>     within the library so finding it is easy.
>     - Hiding the pointers with mangling doesn't make this harder.

I don't get the “scanning” and “ROP” parts. :)

> (c) Mangling degrades performance.

Not just that, it makes compatibility so much harder.  (We would have to 
unmangle vtables if needed.)

>> I'm only worried about the flag that turns of the check.
>
> I don't see any way to provide backwards compatibility than to disable
> the check with a global.
>
> We could encrypt the value of the global to make it harder to unset?
>
> Mike suggests this too.

Yes, it's a good idea.

>>> What we really want is a set of internal initializers that are run during
>>> static dlopen / dlmopen that allow the library to copy key state into the
>>> other namespace.
>>
>> We don't need pass across any actual data, or affect relocation, so
>> it's easier. malloc in libc.so does the following to detect whether
>> it's running across a linking boundary:
>>
>> #ifdef SHARED
>>   /* In case this libc copy is in a non-default namespace, never use brk.
>>      Likewise if dlopened from statically linked program.  */
>>   Dl_info di;
>>   struct link_map *l;
>>
>>   if (_dl_open_hook != NULL
>>       || (_dl_addr (ptmalloc_init, &di, &l, NULL) != 0
>>           && l->l_ns != LM_ID_BASE))
>>     __morecore = __failing_morecore;
>> #endif
>
> Ah, OK, that answers my question. You lookup your own link map and then
> determine you're not in the default namespace and reset the variables.
> That's a sufficient solution. Thanks.

I also found this, which seems to cover the same thing:

/* Set nonzero if we have to be prepared for more than one libc being
    used in the process.  Safe assumption if initializer never runs.  */
int __libc_multiple_libcs attribute_hidden = 1;

The malloc code doesn't use it, maybe because it came from out-of-tree 
ptmalloc.

Would be csu/init-first.c the right place to perform the multiple-libc 
check and set the flag?  Does it run for both static and dynamic binaries?

> Again, my thoughts were along the lines of some kind of callback
> registration process where the old structure pointers are registered
> along with a callback.

Based on our experience with the fork handlers, I think explicit calls 
across subsystems are the way to go.  It's the best way to express 
ordering requirements.

>>      a4c:       48 8b 83 d8 00 00 00    mov    0xd8(%rbx),%rax
>>      a53:       48 29 f2                sub    %rsi,%rdx
>>      a56:       48 81 78 38 00 00 00    cmpq $_IO_file_xsputn_fast,0x38(%rax)
>>      a5e:       0f 85 e6 11 00 00       jne    1c4a
>>      a64:       e8 00 00 00 00          callq  _IO_file_xsputn_fast
>> …
>>     1c4a:       e8 00 00 00 00          callq  _IO_file_xsputn_slow
>>     1c4f:       e9 15 ee ff ff          jmpq   a69
>>
>> This expansion has much more reasonable size, but only makes sense if
>> the call site is monomorphic in practice.
>
> I don't follow. The slow path can still use the vtables as required by
> legacy binaries and the call site there can be polymorphic? Maybe I've
> misunderstood the suggestion.

It's just a matter of performance.  The comparison against the expected 
pointer is worthwhile if the in almost all cases, there is a match and 
you hit the fast path.  If it's just 50/50 or something like that (as it 
currently would be because fprintf and sprintf use different xsputn 
implementations), it does not improve performance.  Calling the slow 
path directly would be preferable.

Florian
  
Kees Cook June 1, 2016, 5:08 p.m. UTC | #9
On Wed, Jun 1, 2016 at 1:56 AM, Florian Weimer <fweimer@redhat.com> wrote:
> On 05/31/2016 09:23 PM, Kees Cook wrote:
>>
>> Well, this is certainly better than not having it, and the on/off
>> switch isn't in the FILE structure, so I would think this should be of
>> a similar protection level. Though, it'd be nice if a process could
>> opt-out for its entire lifetime. Right now, any call to _IO_file_init
>> disables the protection.
>
>
> I don't quite see how to do this.  The machine code sequence to set the flag
> has to be in the process image to enable backwards compatibility when
> needed.  It doesn't really matter if this code is in an IFUNC handler or in
> a library subroutine.  And even if we have some precondition check (say, the
> IFUNC handler checks that we are in ld.so and relocation processing is
> running), execution could start after it.
>
> We could have another flag, this time in read-only memory, which is some
> sort of tunable which can be tweaked by the system administrator. (Maybe we
> could even query the SELinux policy engine to get the flag.) The fallback
> path could check this flag as well.
>
> In general, I want to avoid policy-based solutions because I'm told that in
> too many relevant scenarios, only the “anything goes” policy counts.

Right, totally agreed. I guess I'm not clear on the execution order of
some of these things. How early in the process lifetime can glibc know
that it must use the compat logic? Is it early enough that the dynamic
linker can decide and write the result into what-will-be-read-only
memory?

> Do you think it's import to address this from the start, or can this wait
> until we have a better story for tunables?

I think it can be secondary. The value is separate from the FILE
table, so I'm happy with that.

-Kees
  
Szabolcs Nagy June 1, 2016, 6:08 p.m. UTC | #10
On 31/05/16 14:07, Florian Weimer wrote:
> The basic idea is to put all libc vtables into a single array, and then check whether the vtable pointer is
> within that array.
> 

does this mean that static linking anything with
stdio will include all the code referenced from
the vtables.. or was that already the case?
  
Florian Weimer June 1, 2016, 6:16 p.m. UTC | #11
On 06/01/2016 08:08 PM, Szabolcs Nagy wrote:
> On 31/05/16 14:07, Florian Weimer wrote:
>> The basic idea is to put all libc vtables into a single array, and then check whether the vtable pointer is
>> within that array.
>>
>
> does this mean that static linking anything with
> stdio will include all the code referenced from
> the vtables..

Correct.

> or was that already the case?

It was mostly the case already.  There are only small savings to be had, 
it seems.  I will post numbers along with a real patch proposal.

I have to use weak references anyway to avoid linknamespace issues, and 
we can add more if the current approach pulls in too much additional code.

Florian
  
Florian Weimer June 2, 2016, 12:33 p.m. UTC | #12
On 06/01/2016 07:08 PM, Kees Cook wrote:

> Right, totally agreed. I guess I'm not clear on the execution order of
> some of these things. How early in the process lifetime can glibc know
> that it must use the compat logic? Is it early enough that the dynamic
> linker can decide and write the result into what-will-be-read-only
> memory?

glibc knows it when it sees the symbol to the constructor function.  The 
function might still not be called, ever, but it is a very strong indicator.

Unfortunately, due to lazy binding, glibc won't learn this information 
during the initial link (unless BIND_NOW is used).

dlopen may also open a DSO which contain such a symbol, so the reference 
may not even be contained in the initial set of DSOs.  The reference to 
dlopen can also be lazily bound (or hidden behind a call to gethostbyname).

It's quite messy.  For most architectures, arguing against the need for 
backwards compatibility will be the better approach. :)

Florian
  
Pedro Alves June 3, 2016, 9:34 a.m. UTC | #13
On 05/31/2016 02:07 PM, Florian Weimer wrote:
> I have implemented a completely different approach to vtable hardening.
> 
> The basic idea is to put all libc vtables into a single array, and then
> check whether the vtable pointer is within that array.

Instead of a single array, how about instead putting all the vtables in
the same section with __attribute__ section.  Something like:

+ #define __vtable __attribute__ ((section("vtables")))

- const struct _IO_jump_t _IO_file_jumps_mmap =
+ const struct _IO_jump_t __vtable _IO_file_jumps_mmap =
  ...

and then check whether the vtable pointer is within that section,
with __start_vtables, __end_vtables:

static inline const struct _IO_jump_t *
IO_validate_vtable (const struct _IO_jump_t *vtable)
{
  extern char __start_vtables[];
  extern char __end_vtables[];

  if (!__glibc_likely ((long) __start_vtables <= vtable
		       && vtable < (long) __end_vtables))
    IO_vtable_check ();
  return vtable;
}

That'd avoid having to have a central place that knows about all
the vtables.   It'd probably make the patch smaller too, as
side effect.

Thanks,
Pedro Alves
  
Florian Weimer June 3, 2016, 9:44 a.m. UTC | #14
On 06/03/2016 11:34 AM, Pedro Alves wrote:
> On 05/31/2016 02:07 PM, Florian Weimer wrote:
>> I have implemented a completely different approach to vtable hardening.
>>
>> The basic idea is to put all libc vtables into a single array, and then
>> check whether the vtable pointer is within that array.
>
> Instead of a single array, how about instead putting all the vtables in
> the same section with __attribute__ section.  Something like:
>
> + #define __vtable __attribute__ ((section("vtables")))
>
> - const struct _IO_jump_t _IO_file_jumps_mmap =
> + const struct _IO_jump_t __vtable _IO_file_jumps_mmap =
>   ...
>
> and then check whether the vtable pointer is within that section,
> with __start_vtables, __end_vtables:
>
> static inline const struct _IO_jump_t *
> IO_validate_vtable (const struct _IO_jump_t *vtable)
> {
>   extern char __start_vtables[];
>   extern char __end_vtables[];
>
>   if (!__glibc_likely ((long) __start_vtables <= vtable
> 		       && vtable < (long) __end_vtables))
>     IO_vtable_check ();
>   return vtable;
> }
>
> That'd avoid having to have a central place that knows about all
> the vtables.   It'd probably make the patch smaller too, as
> side effect.

This will need an additional substraction in the validation code because 
there is no relocation to express the different between two pointers, 
even though this value is a link-time constant.  The statically sized 
array makes the difference a constant, avoiding this problem.

(GCC currently does not perform this optimization for pointer 
differences, but it's easy enough to do it manually.)

Thanks,
Florian
  
Pedro Alves June 3, 2016, 9:57 a.m. UTC | #15
On 06/03/2016 10:44 AM, Florian Weimer wrote:

> This will need an additional substraction in the validation code because
> there is no relocation to express the different between two pointers,
> even though this value is a link-time constant.  The statically sized
> array makes the difference a constant, avoiding this problem.

Is it a problem in practice?
> 
> (GCC currently does not perform this optimization for pointer
> differences, but it's easy enough to do it manually.)

I believe it would sort out the "static link now pulls
everything" issue mentioned downthread, though.

Thanks,
Pedro Alves
  
Pedro Alves June 3, 2016, 10:08 a.m. UTC | #16
On 06/03/2016 10:34 AM, Pedro Alves wrote:
> and then check whether the vtable pointer is within that section,
> with __start_vtables, __end_vtables:

BTW, s/__end_vtables/__stop_vtables/.

I misremembered the magic section end symbol.

Thanks,
Pedro Alves
  
Florian Weimer June 3, 2016, 11:05 a.m. UTC | #17
On 06/03/2016 11:57 AM, Pedro Alves wrote:
> On 06/03/2016 10:44 AM, Florian Weimer wrote:
>
>> This will need an additional substraction in the validation code because
>> there is no relocation to express the different between two pointers,
>> even though this value is a link-time constant.  The statically sized
>> array makes the difference a constant, avoiding this problem.
>
> Is it a problem in practice?

Probably not.  I looked at the mechanism, and there is a scary comment:

/* Make SYMBOL, which is in the text segment, an element of SET.  */
#define text_set_element(set, symbol)	_elf_set_element(set, symbol)

/* When building a shared library, make the set section writable,
    because it will need to be relocated at run time anyway.  */
# define _elf_set_element(set, symbol) \
   static const void *__elf_set_##set##_element_##symbol##__ \
     __attribute__ ((used, section (#set))) = &(symbol)

But this doesn't seem to be true in practice, the relevant section 
appears to be read-only (also eu-readelf says it's subject to RELRO).

Do you know how this mechanism works?  There's a sed construct in 
Makerules for $(common-objpfx)shlib.lds.  I assume it would need adjusting.

A const array of vtables somehow seems more robust than this (but we 
need to get this right anyway because of the callback pointers in these 
symbol sets).

>> (GCC currently does not perform this optimization for pointer
>> differences, but it's easy enough to do it manually.)
>
> I believe it would sort out the "static link now pulls
> everything" issue mentioned downthread, though.

Yes, I think it would.

I'll give it a try.

Thanks,
Florian
  
Florian Weimer June 3, 2016, 2:18 p.m. UTC | #18
On 06/03/2016 01:05 PM, Florian Weimer wrote:
> On 06/03/2016 11:57 AM, Pedro Alves wrote:
>> On 06/03/2016 10:44 AM, Florian Weimer wrote:
>>
>>> This will need an additional substraction in the validation code because
>>> there is no relocation to express the different between two pointers,
>>> even though this value is a link-time constant.  The statically sized
>>> array makes the difference a constant, avoiding this problem.
>>
>> Is it a problem in practice?
>
> Probably not.  I looked at the mechanism, and there is a scary comment:
>
> /* Make SYMBOL, which is in the text segment, an element of SET.  */
> #define text_set_element(set, symbol)    _elf_set_element(set, symbol)
>
> /* When building a shared library, make the set section writable,
>    because it will need to be relocated at run time anyway.  */
> # define _elf_set_element(set, symbol) \
>   static const void *__elf_set_##set##_element_##symbol##__ \
>     __attribute__ ((used, section (#set))) = &(symbol)
>
> But this doesn't seem to be true in practice, the relevant section
> appears to be read-only (also eu-readelf says it's subject to RELRO).
>
> Do you know how this mechanism works?  There's a sed construct in
> Makerules for $(common-objpfx)shlib.lds.  I assume it would need adjusting.

I've got it working, except for the static dlopen case (which is an 
unrelated problem).

The tricky bit was realizing that ld has a special rule (not reflected 
in the default linker script) that orphan sections receive those 
automatically generated __start_ and __stop_ symbols.  This wasn't clear 
to me at all (although it is documented), and I found it only after 
reading the ld source code.

Thanks,
Florian
  
Pedro Alves June 3, 2016, 2:34 p.m. UTC | #19
On 06/03/2016 03:18 PM, Florian Weimer wrote:

> I've got it working, except for the static dlopen case (which is an
> unrelated problem).

Sounds great!

> The tricky bit was realizing that ld has a special rule (not reflected
> in the default linker script) that orphan sections receive those
> automatically generated __start_ and __stop_ symbols.  This wasn't clear
> to me at all (although it is documented), and I found it only after
> reading the ld source code.
> 

Sorry, I didn't think to call that out explicitly, but I should have.

Thanks,
Pedro Alves
  
Carlos O'Donell June 7, 2016, 5:38 a.m. UTC | #20
On 06/01/2016 05:12 AM, Florian Weimer wrote:
>> (b) The table is always PC-relative addressable from ROP gadgets
>>     within the library so finding it is easy.
>>     - Hiding the pointers with mangling doesn't make this harder.
> 
> I don't get the “scanning” and “ROP” parts. :)

Assume you want to remap the table as read-write.
Assume you want to do so via a ROP gadget.
Assume the gadget can do PC-relative + offset loads.
Assume a gadget that can remap a region of memory as read-only.

An attacker must find the table before they can modify it.
Any changes to the glibc binaries change the offset in a PC+offset
equation being used by the attacker to find the table. The goal being
to remap the table as read-write to modify the pointers. If you can't
find the table your only alternative is to scan known places for the
table and attempt to find the start or the end. You might conduct this
scanning by looking for JUMP_INIT_DUMMY holes at some fixed intervals.
If you could obscure that detection then it makes finding the table
hard.

I think it is probably easier to just shotgun the handful of offsets
if you can use mprotect to change the protections of the table. If you
can already use mprotect, perhaps you have enough control to attack
other things. Thus hiding the table therefore doesn't make a big
difference?

>> (c) Mangling degrades performance.
> 
> Not just that, it makes compatibility so much harder. (We would have
> to unmangle vtables if needed.)

Good point.

>>> I'm only worried about the flag that turns of the check.
>>
>> I don't see any way to provide backwards compatibility than to disable
>> the check with a global.
>>
>> We could encrypt the value of the global to make it harder to unset?
>>
>> Mike suggests this too.
> 
> Yes, it's a good idea.

Awesome.

>>>> What we really want is a set of internal initializers that are run during
>>>> static dlopen / dlmopen that allow the library to copy key state into the
>>>> other namespace.
>>>
>>> We don't need pass across any actual data, or affect relocation, so
>>> it's easier. malloc in libc.so does the following to detect whether
>>> it's running across a linking boundary:
>>>
>>> #ifdef SHARED
>>>   /* In case this libc copy is in a non-default namespace, never use brk.
>>>      Likewise if dlopened from statically linked program.  */
>>>   Dl_info di;
>>>   struct link_map *l;
>>>
>>>   if (_dl_open_hook != NULL
>>>       || (_dl_addr (ptmalloc_init, &di, &l, NULL) != 0
>>>           && l->l_ns != LM_ID_BASE))
>>>     __morecore = __failing_morecore;
>>> #endif
>>
>> Ah, OK, that answers my question. You lookup your own link map and then
>> determine you're not in the default namespace and reset the variables.
>> That's a sufficient solution. Thanks.
> 
> I also found this, which seems to cover the same thing:
> 
> /* Set nonzero if we have to be prepared for more than one libc being
>    used in the process.  Safe assumption if initializer never runs.  */
> int __libc_multiple_libcs attribute_hidden = 1;
> 
> The malloc code doesn't use it, maybe because it came from out-of-tree ptmalloc.
> 
> Would be csu/init-first.c the right place to perform the
> multiple-libc check and set the flag? Does it run for both static and
> dynamic binaries?

No, I would use csu/libc-start.c since static or dynamic you always
call __libc_start_main.

Though I think we might have already shown that this method doesn't
work in some of the cases you want to cover, but I wanted to respond
to the question here with a clear answer.

>> Again, my thoughts were along the lines of some kind of callback
>> registration process where the old structure pointers are registered
>> along with a callback.
> 
> Based on our experience with the fork handlers, I think explicit
> calls across subsystems are the way to go. It's the best way to
> express ordering requirements.

Would this mean that we have to use "init on first use" for all of
the things in the new dynamic namespace? Without the registered
callback we need to call across subsystems right when we need it.

>>>      a4c:       48 8b 83 d8 00 00 00    mov    0xd8(%rbx),%rax
>>>      a53:       48 29 f2                sub    %rsi,%rdx
>>>      a56:       48 81 78 38 00 00 00    cmpq $_IO_file_xsputn_fast,0x38(%rax)
>>>      a5e:       0f 85 e6 11 00 00       jne    1c4a
>>>      a64:       e8 00 00 00 00          callq  _IO_file_xsputn_fast
>>> …
>>>     1c4a:       e8 00 00 00 00          callq  _IO_file_xsputn_slow
>>>     1c4f:       e9 15 ee ff ff          jmpq   a69
>>>
>>> This expansion has much more reasonable size, but only makes sense if
>>> the call site is monomorphic in practice.
>>
>> I don't follow. The slow path can still use the vtables as required by
>> legacy binaries and the call site there can be polymorphic? Maybe I've
>> misunderstood the suggestion.
> 
> It's just a matter of performance. The comparison against the
> expected pointer is worthwhile if the in almost all cases, there is a
> match and you hit the fast path. If it's just 50/50 or something like
> that (as it currently would be because fprintf and sprintf use
> different xsputn implementations), it does not improve performance.
> Calling the slow path directly would be preferable.

OK.
  

Patch

diff --git a/debug/obprintf_chk.c b/debug/obprintf_chk.c
index 8469b5f..5e319ff 100644
--- a/debug/obprintf_chk.c
+++ b/debug/obprintf_chk.c
@@ -35,8 +35,6 @@  struct _IO_obstack_file
   struct obstack *obstack;
 };
 
-extern const struct _IO_jump_t _IO_obstack_jumps attribute_hidden;
-
 int
 __obstack_vprintf_chk (struct obstack *obstack, int flags, const char *format,
 		       va_list args)
@@ -54,7 +52,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 (&new_f.ofile.file) = &IO_vtables[IO_VTABLE_obstack];
   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..f512c17 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 (&sf._sbf) = &IO_vtables[IO_VTABLE_str];
   _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..73797e9 100644
--- a/debug/vdprintf_chk.c
+++ b/debug/vdprintf_chk.c
@@ -37,9 +37,10 @@  __vdprintf_chk (int d, int flags, const char *format, va_list arg)
 #ifdef _IO_MTSAFE_IO
   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_file_init (&tmpfil);
+  _IO_no_init (&tmpfil.file, _IO_USER_LOCK, 0, &wd,
+	       &IO_vtables[IO_VTABLE_wfile]);
+  _IO_JUMPS (&tmpfil) = &IO_vtables[IO_VTABLE_file];
+  _IO_file_init_internal (&tmpfil);
 #if  !_IO_UNIFIED_JUMPTABLES
   tmpfil.vtable = NULL;
 #endif
diff --git a/debug/vsnprintf_chk.c b/debug/vsnprintf_chk.c
index cc559d2..f3cc332 100644
--- a/debug/vsnprintf_chk.c
+++ b/debug/vsnprintf_chk.c
@@ -20,8 +20,6 @@ 
 #include "../libio/libioP.h"
 #include "../libio/strfile.h"
 
-extern const struct _IO_jump_t _IO_strn_jumps attribute_hidden;
-
 /* Write formatted output into S, according to the format
    string FORMAT, writing no more than MAXLEN characters.  */
 /* VARARGS5 */
@@ -51,7 +49,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 (&sf.f._sbf) = &IO_vtables[IO_VTABLE_strn];
   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..e184ebd 100644
--- a/debug/vsprintf_chk.c
+++ b/debug/vsprintf_chk.c
@@ -20,10 +20,7 @@ 
 #include "../libio/libioP.h"
 #include "../libio/strfile.h"
 
-
-static int _IO_str_chk_overflow (_IO_FILE *fp, int c) __THROW;
-
-static int
+int
 _IO_str_chk_overflow (_IO_FILE *fp, int c)
 {
   /* When we come to here this means the user supplied buffer is
@@ -31,32 +28,6 @@  _IO_str_chk_overflow (_IO_FILE *fp, int c)
   __chk_fail ();
 }
 
-
-static const struct _IO_jump_t _IO_str_chk_jumps =
-{
-  JUMP_INIT_DUMMY,
-  JUMP_INIT(finish, _IO_str_finish),
-  JUMP_INIT(overflow, _IO_str_chk_overflow),
-  JUMP_INIT(underflow, _IO_str_underflow),
-  JUMP_INIT(uflow, _IO_default_uflow),
-  JUMP_INIT(pbackfail, _IO_str_pbackfail),
-  JUMP_INIT(xsputn, _IO_default_xsputn),
-  JUMP_INIT(xsgetn, _IO_default_xsgetn),
-  JUMP_INIT(seekoff, _IO_str_seekoff),
-  JUMP_INIT(seekpos, _IO_default_seekpos),
-  JUMP_INIT(setbuf, _IO_default_setbuf),
-  JUMP_INIT(sync, _IO_default_sync),
-  JUMP_INIT(doallocate, _IO_default_doallocate),
-  JUMP_INIT(read, _IO_default_read),
-  JUMP_INIT(write, _IO_default_write),
-  JUMP_INIT(seek, _IO_default_seek),
-  JUMP_INIT(close, _IO_default_close),
-  JUMP_INIT(stat, _IO_default_stat),
-  JUMP_INIT(showmanyc, _IO_default_showmanyc),
-  JUMP_INIT(imbue, _IO_default_imbue)
-};
-
-
 int
 ___vsprintf_chk (char *s, int flags, size_t slen, const char *format,
 		 va_list args)
@@ -71,7 +42,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 (&f._sbf) = &IO_vtables[IO_VTABLE_str_chk];
   s[0] = '\0';
   _IO_str_init_static_internal (&f, s, slen - 1, s);
 
diff --git a/debug/vswprintf_chk.c b/debug/vswprintf_chk.c
index 50bdb78..6bdb16f 100644
--- a/debug/vswprintf_chk.c
+++ b/debug/vswprintf_chk.c
@@ -49,7 +49,8 @@  __vswprintf_chk (wchar_t *s, size_t maxlen, int flags, size_t slen,
        length of zero always makes the function fail.  */
     return -1;
 
-  _IO_no_init (&sf.f._sbf._f, _IO_USER_LOCK, 0, &wd, &_IO_wstrn_jumps);
+  _IO_no_init (&sf.f._sbf._f, _IO_USER_LOCK, 0, &wd,
+	       &IO_vtables[IO_VTABLE_wstrn]);
   _IO_fwide (&sf.f._sbf._f, 1);
   s[0] = L'\0';
 
diff --git a/libio/Makefile b/libio/Makefile
index 4189bc4..12589f2 100644
--- a/libio/Makefile
+++ b/libio/Makefile
@@ -46,7 +46,7 @@  routines	:=							      \
 	__fbufsize __freading __fwriting __freadable __fwritable __flbf	      \
 	__fpurge __fpending __fsetlocking				      \
 									      \
-	libc_fatal fmemopen oldfmemopen
+	libc_fatal fmemopen oldfmemopen vtables
 
 tests = tst_swprintf tst_wprintf tst_swscanf tst_wscanf tst_getwc tst_putwc   \
 	tst_wprintf2 tst-widetext test-fmemopen tst-ext tst-ext2 \
diff --git a/libio/fileops.c b/libio/fileops.c
index 8e83b1c..4ccd3b7 100644
--- a/libio/fileops.c
+++ b/libio/fileops.c
@@ -140,7 +140,7 @@  extern struct __gconv_trans_data __libio_translit attribute_hidden;
 
 
 void
-_IO_new_file_init (struct _IO_FILE_plus *fp)
+_IO_file_init_internal (struct _IO_FILE_plus *fp)
 {
   /* POSIX.1 allows another file handle to be used to change the position
      of our file descriptor.  Hence we actually don't know the actual
@@ -151,7 +151,16 @@  _IO_new_file_init (struct _IO_FILE_plus *fp)
   _IO_link_in (fp);
   fp->file._fileno = -1;
 }
-libc_hidden_ver (_IO_new_file_init, _IO_file_init)
+
+/* Externally accessible constructor for _IO_FILE objects.  */
+void
+_IO_new_file_init (struct _IO_FILE_plus *fp)
+{
+  _IO_file_init_internal (fp);
+  /* The vtable is supplied by the caller, so we have to disable
+     validation.  */
+  IO_accept_foreign_vtables = true;
+}
 
 int
 _IO_new_file_close_it (_IO_FILE *fp)
@@ -466,8 +475,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 (fp) = &IO_vtables[IO_VTABLE_file];
+  fp->_wide_data->_wide_vtable = &IO_vtables[IO_VTABLE_wfile];
 
   /* And perform the normal operation.  */
   result = _IO_new_file_setbuf (fp, p, len);
@@ -475,8 +484,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 (fp) = &IO_vtables[IO_VTABLE_file_mmap];
+      fp->_wide_data->_wide_vtable = &IO_vtables[IO_VTABLE_wfile_mmap];
     }
 
   return result;
@@ -703,10 +712,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 (fp) = &IO_vtables[IO_VTABLE_file];
       else
-	_IO_JUMPS_FILE_plus (fp) = &_IO_wfile_jumps;
-      fp->_wide_data->_wide_vtable = &_IO_wfile_jumps;
+	_IO_JUMPS_FILE_plus (fp) = &IO_vtables[IO_VTABLE_wfile];
+      fp->_wide_data->_wide_vtable = &IO_vtables[IO_VTABLE_wfile];
 
       return 1;
     }
@@ -773,10 +782,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 (fp) = &IO_vtables[IO_VTABLE_file_mmap];
 	      else
-		_IO_JUMPS_FILE_plus (fp) = &_IO_wfile_jumps_mmap;
-	      fp->_wide_data->_wide_vtable = &_IO_wfile_jumps_mmap;
+		_IO_JUMPS_FILE_plus (fp) = &IO_vtables[IO_VTABLE_wfile_mmap];
+	      fp->_wide_data->_wide_vtable = &IO_vtables[IO_VTABLE_wfile_mmap];
 
 	      return;
 	    }
@@ -786,10 +795,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 (fp) = &IO_vtables[IO_VTABLE_file];
   else
-    _IO_JUMPS_FILE_plus (fp) = &_IO_wfile_jumps;
-  fp->_wide_data->_wide_vtable = &_IO_wfile_jumps;
+    _IO_JUMPS_FILE_plus (fp) = &IO_vtables[IO_VTABLE_wfile];
+  fp->_wide_data->_wide_vtable = &IO_vtables[IO_VTABLE_wfile];
 }
 
 int
@@ -897,7 +906,7 @@  _IO_new_file_sync (_IO_FILE *fp)
 }
 libc_hidden_ver (_IO_new_file_sync, _IO_file_sync)
 
-static int
+int
 _IO_file_sync_mmap (_IO_FILE *fp)
 {
   if (fp->_IO_read_ptr != fp->_IO_read_end)
@@ -1192,7 +1201,7 @@  _IO_file_seekoff_mmap (_IO_FILE *fp, _IO_off64_t offset, int dir, int mode)
   return offset;
 }
 
-static _IO_off64_t
+_IO_off64_t
 _IO_file_seekoff_maybe_mmap (_IO_FILE *fp, _IO_off64_t offset, int dir,
 			     int mode)
 {
@@ -1453,7 +1462,7 @@  _IO_file_xsgetn (_IO_FILE *fp, void *data, _IO_size_t n)
 }
 libc_hidden_def (_IO_file_xsgetn)
 
-static _IO_size_t
+_IO_size_t
 _IO_file_xsgetn_mmap (_IO_FILE *fp, void *data, _IO_size_t n)
 {
   _IO_size_t have;
@@ -1508,7 +1517,7 @@  _IO_file_xsgetn_mmap (_IO_FILE *fp, void *data, _IO_size_t n)
   return s - (char *) data;
 }
 
-static _IO_size_t
+_IO_size_t
 _IO_file_xsgetn_maybe_mmap (_IO_FILE *fp, void *data, _IO_size_t n)
 {
   /* We only get here if this is the first attempt to read something.
@@ -1533,76 +1542,3 @@  versioned_symbol (libc, _IO_new_file_underflow, _IO_file_underflow, GLIBC_2_1);
 versioned_symbol (libc, _IO_new_file_write, _IO_file_write, GLIBC_2_1);
 versioned_symbol (libc, _IO_new_file_xsputn, _IO_file_xsputn, GLIBC_2_1);
 #endif
-
-const struct _IO_jump_t _IO_file_jumps =
-{
-  JUMP_INIT_DUMMY,
-  JUMP_INIT(finish, _IO_file_finish),
-  JUMP_INIT(overflow, _IO_file_overflow),
-  JUMP_INIT(underflow, _IO_file_underflow),
-  JUMP_INIT(uflow, _IO_default_uflow),
-  JUMP_INIT(pbackfail, _IO_default_pbackfail),
-  JUMP_INIT(xsputn, _IO_file_xsputn),
-  JUMP_INIT(xsgetn, _IO_file_xsgetn),
-  JUMP_INIT(seekoff, _IO_new_file_seekoff),
-  JUMP_INIT(seekpos, _IO_default_seekpos),
-  JUMP_INIT(setbuf, _IO_new_file_setbuf),
-  JUMP_INIT(sync, _IO_new_file_sync),
-  JUMP_INIT(doallocate, _IO_file_doallocate),
-  JUMP_INIT(read, _IO_file_read),
-  JUMP_INIT(write, _IO_new_file_write),
-  JUMP_INIT(seek, _IO_file_seek),
-  JUMP_INIT(close, _IO_file_close),
-  JUMP_INIT(stat, _IO_file_stat),
-  JUMP_INIT(showmanyc, _IO_default_showmanyc),
-  JUMP_INIT(imbue, _IO_default_imbue)
-};
-libc_hidden_data_def (_IO_file_jumps)
-
-const struct _IO_jump_t _IO_file_jumps_mmap =
-{
-  JUMP_INIT_DUMMY,
-  JUMP_INIT(finish, _IO_file_finish),
-  JUMP_INIT(overflow, _IO_file_overflow),
-  JUMP_INIT(underflow, _IO_file_underflow_mmap),
-  JUMP_INIT(uflow, _IO_default_uflow),
-  JUMP_INIT(pbackfail, _IO_default_pbackfail),
-  JUMP_INIT(xsputn, _IO_new_file_xsputn),
-  JUMP_INIT(xsgetn, _IO_file_xsgetn_mmap),
-  JUMP_INIT(seekoff, _IO_file_seekoff_mmap),
-  JUMP_INIT(seekpos, _IO_default_seekpos),
-  JUMP_INIT(setbuf, (_IO_setbuf_t) _IO_file_setbuf_mmap),
-  JUMP_INIT(sync, _IO_file_sync_mmap),
-  JUMP_INIT(doallocate, _IO_file_doallocate),
-  JUMP_INIT(read, _IO_file_read),
-  JUMP_INIT(write, _IO_new_file_write),
-  JUMP_INIT(seek, _IO_file_seek),
-  JUMP_INIT(close, _IO_file_close_mmap),
-  JUMP_INIT(stat, _IO_file_stat),
-  JUMP_INIT(showmanyc, _IO_default_showmanyc),
-  JUMP_INIT(imbue, _IO_default_imbue)
-};
-
-const struct _IO_jump_t _IO_file_jumps_maybe_mmap =
-{
-  JUMP_INIT_DUMMY,
-  JUMP_INIT(finish, _IO_file_finish),
-  JUMP_INIT(overflow, _IO_file_overflow),
-  JUMP_INIT(underflow, _IO_file_underflow_maybe_mmap),
-  JUMP_INIT(uflow, _IO_default_uflow),
-  JUMP_INIT(pbackfail, _IO_default_pbackfail),
-  JUMP_INIT(xsputn, _IO_new_file_xsputn),
-  JUMP_INIT(xsgetn, _IO_file_xsgetn_maybe_mmap),
-  JUMP_INIT(seekoff, _IO_file_seekoff_maybe_mmap),
-  JUMP_INIT(seekpos, _IO_default_seekpos),
-  JUMP_INIT(setbuf, (_IO_setbuf_t) _IO_file_setbuf_mmap),
-  JUMP_INIT(sync, _IO_new_file_sync),
-  JUMP_INIT(doallocate, _IO_file_doallocate),
-  JUMP_INIT(read, _IO_file_read),
-  JUMP_INIT(write, _IO_new_file_write),
-  JUMP_INIT(seek, _IO_file_seek),
-  JUMP_INIT(close, _IO_file_close),
-  JUMP_INIT(stat, _IO_file_stat),
-  JUMP_INIT(showmanyc, _IO_default_showmanyc),
-  JUMP_INIT(imbue, _IO_default_imbue)
-};
diff --git a/libio/freopen.c b/libio/freopen.c
index 8a2a417..7aed1d7 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 (fp) = &IO_vtables[IO_VTABLE_old_file];
       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 (fp) = &IO_vtables[IO_VTABLE_file];
       if (_IO_vtable_offset (fp) == 0 && fp->_wide_data != NULL)
-	fp->_wide_data->_wide_vtable = &_IO_wfile_jumps;
+	fp->_wide_data->_wide_vtable = &IO_vtables[IO_VTABLE_wfile];
       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..cca4edf 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 (fp) = &IO_vtables[IO_VTABLE_file];
   if (_IO_vtable_offset (fp) == 0 && fp->_wide_data != NULL)
-    fp->_wide_data->_wide_vtable = &_IO_wfile_jumps;
+    fp->_wide_data->_wide_vtable = &IO_vtables[IO_VTABLE_wfile];
   result = _IO_file_fopen (fp, gfilename, mode, 0);
   fp->_flags2 &= ~_IO_FLAGS2_NOCLOSE;
   if (result != NULL)
diff --git a/libio/iofdopen.c b/libio/iofdopen.c
index e00f337..743c1d1 100644
--- a/libio/iofdopen.c
+++ b/libio/iofdopen.c
@@ -145,23 +145,24 @@  _IO_new_fdopen (int fd, const char *mode)
   _IO_no_init (&new_f->fp.file, 0, 0, &new_f->wd,
 #ifdef _G_HAVE_MMAP
 	       (use_mmap && (read_write & _IO_NO_WRITES))
-	       ? &_IO_wfile_jumps_maybe_mmap :
+	       ? &IO_vtables[IO_VTABLE_wfile_maybe_mmap] :
 #endif
-	       &_IO_wfile_jumps);
+	       &IO_vtables[IO_VTABLE_wfile]);
   _IO_JUMPS (&new_f->fp) =
 #ifdef _G_HAVE_MMAP
-    (use_mmap && (read_write & _IO_NO_WRITES)) ? &_IO_file_jumps_maybe_mmap :
+    (use_mmap && (read_write & _IO_NO_WRITES))
+      ? &IO_vtables[IO_VTABLE_file_maybe_mmap] :
 #endif
-      &_IO_file_jumps;
-  _IO_file_init (&new_f->fp);
+      &IO_vtables[IO_VTABLE_file];
+  _IO_file_init_internal (&new_f->fp);
 #if  !_IO_UNIFIED_JUMPTABLES
   new_f->fp.vtable = NULL;
 #endif
-  /* We only need to record the fd because _IO_file_init will have unset the
-     offset.  It is important to unset the cached offset because the real
-     offset in the file could change between now and when the handle is
-     activated and we would then mislead ftell into believing that we have a
-     valid offset.  */
+  /* We only need to record the fd because _IO_file_init_internal will
+     have unset the offset.  It is important to unset the cached
+     offset because the real offset in the file could change between
+     now and when the handle is activated and we would then mislead
+     ftell into believing that we have a valid offset.  */
   new_f->fp.file._fileno = fd;
   new_f->fp.file._flags &= ~_IO_DELETE_DONT_CLOSE;
 
diff --git a/libio/iofopen.c b/libio/iofopen.c
index 13e3910..85d7e1a 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 (fp) = &IO_vtables[IO_VTABLE_file_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 (fp) = &IO_vtables[IO_VTABLE_wfile_maybe_mmap];
+      fp->_wide_data->_wide_vtable = &IO_vtables[IO_VTABLE_wfile_maybe_mmap];
     }
 #endif
   return fp;
@@ -74,12 +74,13 @@  __fopen_internal (const char *filename, const char *mode, int is32)
   new_f->fp.file._lock = &new_f->lock;
 #endif
 #if defined _LIBC || defined _GLIBCPP_USE_WCHAR_T
-  _IO_no_init (&new_f->fp.file, 0, 0, &new_f->wd, &_IO_wfile_jumps);
+  _IO_no_init (&new_f->fp.file, 0, 0, &new_f->wd,
+	       &IO_vtables[IO_VTABLE_wfile]);
 #else
   _IO_no_init (&new_f->fp.file, 1, 0, NULL, NULL);
 #endif
-  _IO_JUMPS (&new_f->fp) = &_IO_file_jumps;
-  _IO_file_init (&new_f->fp);
+  _IO_JUMPS (&new_f->fp) = &IO_vtables[IO_VTABLE_file];
+  _IO_file_init_internal (&new_f->fp);
 #if  !_IO_UNIFIED_JUMPTABLES
   new_f->fp.vtable = NULL;
 #endif
diff --git a/libio/iofopncook.c b/libio/iofopncook.c
index 9eda7c1..2caae08 100644
--- a/libio/iofopncook.c
+++ b/libio/iofopncook.c
@@ -29,17 +29,7 @@ 
 #include <stdlib.h>
 #include <shlib-compat.h>
 
-/* Prototyped for local functions.  */
-static _IO_ssize_t _IO_cookie_read (_IO_FILE* fp, void* buf,
-				    _IO_ssize_t size);
-static _IO_ssize_t _IO_cookie_write (_IO_FILE* fp,
-				     const void* buf, _IO_ssize_t size);
-static _IO_off64_t _IO_cookie_seek (_IO_FILE *fp, _IO_off64_t offset, int dir);
-static _IO_off64_t _IO_cookie_seekoff (_IO_FILE *fp, _IO_off64_t offset,
-				       int dir, int mode);
-static int _IO_cookie_close (_IO_FILE* fp);
-
-static _IO_ssize_t
+_IO_ssize_t
 _IO_cookie_read (_IO_FILE *fp, void *buf, _IO_ssize_t size)
 {
   struct _IO_cookie_file *cfile = (struct _IO_cookie_file *) fp;
@@ -50,7 +40,7 @@  _IO_cookie_read (_IO_FILE *fp, void *buf, _IO_ssize_t size)
   return cfile->__io_functions.read (cfile->__cookie, buf, size);
 }
 
-static _IO_ssize_t
+_IO_ssize_t
 _IO_cookie_write (_IO_FILE *fp, const void *buf, _IO_ssize_t size)
 {
   struct _IO_cookie_file *cfile = (struct _IO_cookie_file *) fp;
@@ -68,7 +58,7 @@  _IO_cookie_write (_IO_FILE *fp, const void *buf, _IO_ssize_t size)
   return n;
 }
 
-static _IO_off64_t
+_IO_off64_t
 _IO_cookie_seek (_IO_FILE *fp, _IO_off64_t offset, int dir)
 {
   struct _IO_cookie_file *cfile = (struct _IO_cookie_file *) fp;
@@ -80,7 +70,7 @@  _IO_cookie_seek (_IO_FILE *fp, _IO_off64_t offset, int dir)
 	  ? _IO_pos_BAD : offset);
 }
 
-static int
+int
 _IO_cookie_close (_IO_FILE *fp)
 {
   struct _IO_cookie_file *cfile = (struct _IO_cookie_file *) fp;
@@ -92,7 +82,7 @@  _IO_cookie_close (_IO_FILE *fp)
 }
 
 
-static _IO_off64_t
+_IO_off64_t
 _IO_cookie_seekoff (_IO_FILE *fp, _IO_off64_t offset, int dir, int mode)
 {
   /* We must force the fileops code to always use seek to determine
@@ -102,41 +92,17 @@  _IO_cookie_seekoff (_IO_FILE *fp, _IO_off64_t offset, int dir, int mode)
 }
 
 
-static const struct _IO_jump_t _IO_cookie_jumps = {
-  JUMP_INIT_DUMMY,
-  JUMP_INIT(finish, _IO_file_finish),
-  JUMP_INIT(overflow, _IO_file_overflow),
-  JUMP_INIT(underflow, _IO_file_underflow),
-  JUMP_INIT(uflow, _IO_default_uflow),
-  JUMP_INIT(pbackfail, _IO_default_pbackfail),
-  JUMP_INIT(xsputn, _IO_file_xsputn),
-  JUMP_INIT(xsgetn, _IO_default_xsgetn),
-  JUMP_INIT(seekoff, _IO_cookie_seekoff),
-  JUMP_INIT(seekpos, _IO_default_seekpos),
-  JUMP_INIT(setbuf, _IO_file_setbuf),
-  JUMP_INIT(sync, _IO_file_sync),
-  JUMP_INIT(doallocate, _IO_file_doallocate),
-  JUMP_INIT(read, _IO_cookie_read),
-  JUMP_INIT(write, _IO_cookie_write),
-  JUMP_INIT(seek, _IO_cookie_seek),
-  JUMP_INIT(close, _IO_cookie_close),
-  JUMP_INIT(stat, _IO_default_stat),
-  JUMP_INIT(showmanyc, _IO_default_showmanyc),
-  JUMP_INIT(imbue, _IO_default_imbue),
-};
-
-
 void
 _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 (&cfile->__fp) = &IO_vtables[IO_VTABLE_cookie];
 
   cfile->__cookie = cookie;
   cfile->__io_functions = io_functions;
 
-  _IO_file_init (&cfile->__fp);
+  _IO_file_init_internal (&cfile->__fp);
 
   _IO_mask_flags (&cfile->__fp.file, read_write,
 		  _IO_NO_READS+_IO_NO_WRITES+_IO_IS_APPENDING);
@@ -217,29 +183,6 @@  _IO_old_cookie_seek (_IO_FILE *fp, _IO_off64_t offset, int dir)
   return (ret == -1) ? _IO_pos_BAD : ret;
 }
 
-static const struct _IO_jump_t _IO_old_cookie_jumps = {
-  JUMP_INIT_DUMMY,
-  JUMP_INIT(finish, _IO_file_finish),
-  JUMP_INIT(overflow, _IO_file_overflow),
-  JUMP_INIT(underflow, _IO_file_underflow),
-  JUMP_INIT(uflow, _IO_default_uflow),
-  JUMP_INIT(pbackfail, _IO_default_pbackfail),
-  JUMP_INIT(xsputn, _IO_file_xsputn),
-  JUMP_INIT(xsgetn, _IO_default_xsgetn),
-  JUMP_INIT(seekoff, _IO_cookie_seekoff),
-  JUMP_INIT(seekpos, _IO_default_seekpos),
-  JUMP_INIT(setbuf, _IO_file_setbuf),
-  JUMP_INIT(sync, _IO_file_sync),
-  JUMP_INIT(doallocate, _IO_file_doallocate),
-  JUMP_INIT(read, _IO_cookie_read),
-  JUMP_INIT(write, _IO_cookie_write),
-  JUMP_INIT(seek, _IO_old_cookie_seek),
-  JUMP_INIT(close, _IO_cookie_close),
-  JUMP_INIT(stat, _IO_default_stat),
-  JUMP_INIT(showmanyc, _IO_default_showmanyc),
-  JUMP_INIT(imbue, _IO_default_imbue),
-};
-
 _IO_FILE *
 attribute_compat_text_section
 _IO_old_fopencookie (void *cookie, const char *mode,
@@ -249,7 +192,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 (ret) = &IO_vtables[IO_VTABLE_old_cookie];
 
   return ret;
 }
diff --git a/libio/iopopen.c b/libio/iopopen.c
index 9ddde23..2a17b0e 100644
--- a/libio/iopopen.c
+++ b/libio/iopopen.c
@@ -91,8 +91,6 @@  struct _IO_proc_file
 };
 typedef struct _IO_proc_file _IO_proc_file;
 
-static const struct _IO_jump_t _IO_proc_jumps;
-
 static struct _IO_proc_file *proc_file_chain;
 
 #ifdef _IO_MTSAFE_IO
@@ -288,7 +286,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 (&new_f->fpx.file) = &IO_vtables[IO_VTABLE_proc];
   _IO_new_file_init (&new_f->fpx.file);
 #if  !_IO_UNIFIED_JUMPTABLES
   new_f->fpx.file.vtable = NULL;
@@ -344,29 +342,6 @@  _IO_new_proc_close (_IO_FILE *fp)
   return wstatus;
 }
 
-static const struct _IO_jump_t _IO_proc_jumps = {
-  JUMP_INIT_DUMMY,
-  JUMP_INIT(finish, _IO_new_file_finish),
-  JUMP_INIT(overflow, _IO_new_file_overflow),
-  JUMP_INIT(underflow, _IO_new_file_underflow),
-  JUMP_INIT(uflow, _IO_default_uflow),
-  JUMP_INIT(pbackfail, _IO_default_pbackfail),
-  JUMP_INIT(xsputn, _IO_new_file_xsputn),
-  JUMP_INIT(xsgetn, _IO_default_xsgetn),
-  JUMP_INIT(seekoff, _IO_new_file_seekoff),
-  JUMP_INIT(seekpos, _IO_default_seekpos),
-  JUMP_INIT(setbuf, _IO_new_file_setbuf),
-  JUMP_INIT(sync, _IO_new_file_sync),
-  JUMP_INIT(doallocate, _IO_file_doallocate),
-  JUMP_INIT(read, _IO_file_read),
-  JUMP_INIT(write, _IO_new_file_write),
-  JUMP_INIT(seek, _IO_file_seek),
-  JUMP_INIT(close, _IO_new_proc_close),
-  JUMP_INIT(stat, _IO_file_stat),
-  JUMP_INIT(showmanyc, _IO_default_showmanyc),
-  JUMP_INIT(imbue, _IO_default_imbue)
-};
-
 strong_alias (_IO_new_popen, __new_popen)
 versioned_symbol (libc, _IO_new_popen, _IO_popen, GLIBC_2_1);
 versioned_symbol (libc, __new_popen, popen, GLIBC_2_1);
diff --git a/libio/iovdprintf.c b/libio/iovdprintf.c
index 8ca55fc..40fa098 100644
--- a/libio/iovdprintf.c
+++ b/libio/iovdprintf.c
@@ -37,9 +37,10 @@  _IO_vdprintf (int d, const char *format, _IO_va_list arg)
 #ifdef _IO_MTSAFE_IO
   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_file_init (&tmpfil);
+  _IO_no_init (&tmpfil.file, _IO_USER_LOCK, 0, &wd,
+	       &IO_vtables[IO_VTABLE_wfile]);
+  _IO_JUMPS (&tmpfil) = &IO_vtables[IO_VTABLE_file];
+  _IO_file_init_internal (&tmpfil);
 #if  !_IO_UNIFIED_JUMPTABLES
   tmpfil.vtable = NULL;
 #endif
diff --git a/libio/iovsprintf.c b/libio/iovsprintf.c
index 712d178..8d35a02 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 (&sf._sbf) = &IO_vtables[IO_VTABLE_str];
   _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..0b2f64e 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 (&sf._sbf) = &IO_vtables[IO_VTABLE_str];
   _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/iovswscanf.c b/libio/iovswscanf.c
index ff36069..3852438 100644
--- a/libio/iovswscanf.c
+++ b/libio/iovswscanf.c
@@ -37,7 +37,8 @@  __vswscanf (const wchar_t *string, const wchar_t *format, _IO_va_list args)
 #ifdef _IO_MTSAFE_IO
   sf._sbf._f._lock = NULL;
 #endif
-  _IO_no_init (&sf._sbf._f, _IO_USER_LOCK, 0, &wd, &_IO_wstr_jumps);
+  _IO_no_init (&sf._sbf._f, _IO_USER_LOCK, 0, &wd,
+               &IO_vtables[IO_VTABLE_wstr]);
   _IO_fwide (&sf._sbf._f, 1);
   _IO_wstr_init_static (&sf._sbf._f, (wchar_t *)string, 0, NULL);
   ret = _IO_vfwscanf ((_IO_FILE *) &sf._sbf, format, args, NULL);
diff --git a/libio/libioP.h b/libio/libioP.h
index a75522b..335ff4a 100644
--- a/libio/libioP.h
+++ b/libio/libioP.h
@@ -125,11 +125,12 @@  extern "C" {
 
 #if _IO_JUMPS_OFFSET
 # define _IO_JUMPS_FUNC(THIS) \
- (*(struct _IO_jump_t **) ((void *) &_IO_JUMPS_FILE_plus (THIS) \
-			   + (THIS)->_vtable_offset))
+  (IO_validate_vtable							\
+   ((*(struct _IO_jump_t **) ((void *) &_IO_JUMPS_FILE_plus (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_validate_vtable (_IO_JUMPS_FILE_plus (THIS)))
 # define _IO_vtable_offset(THIS) 0
 #endif
 #define _IO_WIDE_JUMPS_FUNC(THIS) _IO_WIDE_JUMPS(THIS)
@@ -481,19 +482,6 @@  extern int _IO_default_sync (_IO_FILE *) __THROW;
 extern int _IO_default_showmanyc (_IO_FILE *) __THROW;
 extern void _IO_default_imbue (_IO_FILE *, void *) __THROW;
 
-extern const struct _IO_jump_t _IO_file_jumps;
-libc_hidden_proto (_IO_file_jumps)
-extern const struct _IO_jump_t _IO_file_jumps_mmap attribute_hidden;
-extern const struct _IO_jump_t _IO_file_jumps_maybe_mmap attribute_hidden;
-extern const struct _IO_jump_t _IO_wfile_jumps;
-libc_hidden_proto (_IO_wfile_jumps)
-extern const struct _IO_jump_t _IO_wfile_jumps_mmap attribute_hidden;
-extern const struct _IO_jump_t _IO_wfile_jumps_maybe_mmap attribute_hidden;
-extern const struct _IO_jump_t _IO_old_file_jumps attribute_hidden;
-extern const struct _IO_jump_t _IO_streambuf_jumps;
-extern const struct _IO_jump_t _IO_old_proc_jumps attribute_hidden;
-extern const struct _IO_jump_t _IO_str_jumps attribute_hidden;
-extern const struct _IO_jump_t _IO_wstr_jumps attribute_hidden;
 extern const struct _IO_codecvt __libio_codecvt attribute_hidden;
 extern int _IO_do_write (_IO_FILE *, const char *, _IO_size_t);
 libc_hidden_proto (_IO_do_write)
@@ -583,8 +571,7 @@  extern int _IO_file_underflow_maybe_mmap (_IO_FILE *);
 extern int _IO_file_overflow (_IO_FILE *, int);
 libc_hidden_proto (_IO_file_overflow)
 #define _IO_file_is_open(__fp) ((__fp)->_fileno != -1)
-extern void _IO_file_init (struct _IO_FILE_plus *) __THROW;
-libc_hidden_proto (_IO_file_init)
+extern void _IO_file_init_internal (struct _IO_FILE_plus *) attribute_hidden;
 extern _IO_FILE* _IO_file_attach (_IO_FILE *, int);
 libc_hidden_proto (_IO_file_attach)
 extern _IO_FILE* _IO_file_open (_IO_FILE *, const char *, int, int, int, int);
@@ -886,3 +873,100 @@  _IO_acquire_lock_clear_flags2_fct (_IO_FILE **p)
                                           | _IO_FLAGS2_SCANF_STD);	      \
   } while (0)
 #endif
+
+/* Index into array of jump tables IO_vtables.  */
+enum
+{
+  IO_VTABLE_file,
+  IO_VTABLE_file_mmap,
+  IO_VTABLE_file_maybe_mmap,
+  IO_VTABLE_wfile,
+  IO_VTABLE_wfile_mmap,
+  IO_VTABLE_wfile_maybe_mmap,
+  IO_VTABLE_mem,
+  IO_VTABLE_wmem,
+  IO_VTABLE_proc,
+  IO_VTABLE_str,
+  IO_VTABLE_str_chk,
+  IO_VTABLE_strn,
+  IO_VTABLE_wstr,
+  IO_VTABLE_wstrn,
+  IO_VTABLE_cookie,
+  IO_VTABLE_obstack,
+
+  /* Used in the implementation of vprintf.   */
+  IO_VTABLE_printf_helper,
+  IO_VTABLE_wprintf_helper,
+
+  /* Obsolete vtables. */
+#if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_1)
+  IO_VTABLE_old_file,
+  IO_VTABLE_old_proc,
+  IO_VTABLE_old_cookie,
+#endif
+
+  /* Number of vtable array elements.  */
+  IO_VTABLE_COUNT
+};
+
+extern const struct _IO_jump_t IO_vtables[IO_VTABLE_COUNT] attribute_hidden;
+
+/* If true, unknown vtable pointers are valid.  */
+extern bool IO_accept_foreign_vtables attribute_hidden;
+
+/* Check that IO_accept_foreign_vtables is true; otherwise, terminate
+   the process.  */
+void IO_vtable_check (void) attribute_hidden;
+
+/* Perform vtable pointer validation.  If validation fails, terminate
+   the process.  */
+static inline const struct _IO_jump_t *
+IO_validate_vtable (const struct _IO_jump_t *vtable)
+{
+  if (!__glibc_likely (&IO_vtables[0] <= vtable
+		       && vtable < &IO_vtables[IO_VTABLE_COUNT]))
+    IO_vtable_check ();
+  return vtable;
+}
+
+/* Prototypes for functions used in vtables.  */
+
+int _IO_file_sync_mmap (_IO_FILE *fp) attribute_hidden;
+_IO_size_t _IO_file_xsgetn_mmap (_IO_FILE *fp, void *data, _IO_size_t n)
+  attribute_hidden;
+_IO_size_t _IO_file_xsgetn_maybe_mmap (_IO_FILE *fp, void *data, _IO_size_t n)
+  attribute_hidden;
+_IO_off64_t _IO_file_seekoff_maybe_mmap
+ (_IO_FILE *fp, _IO_off64_t offset, int dir, int mode) attribute_hidden;
+
+wint_t _IO_wfile_underflow_mmap (_IO_FILE *fp) attribute_hidden;
+wint_t _IO_wfile_underflow_maybe_mmap (_IO_FILE *fp) attribute_hidden;
+
+int _IO_mem_sync (_IO_FILE* fp) attribute_hidden;
+void _IO_mem_finish (_IO_FILE* fp, int) attribute_hidden;
+
+int _IO_wmem_sync (_IO_FILE* fp) attribute_hidden;
+void _IO_wmem_finish (_IO_FILE* fp, int) attribute_hidden;
+
+int _IO_str_chk_overflow (_IO_FILE *fp, int c) attribute_hidden;
+
+int _IO_strn_overflow (_IO_FILE *fp, int c) attribute_hidden;
+
+wint_t _IO_wstrn_overflow (_IO_FILE *fp, wint_t c) attribute_hidden;
+
+_IO_ssize_t _IO_cookie_read (_IO_FILE* fp, void* buf, _IO_ssize_t size)
+  attribute_hidden;
+_IO_ssize_t _IO_cookie_write (_IO_FILE* fp, const void* buf, _IO_ssize_t size)
+  attribute_hidden;
+_IO_off64_t _IO_cookie_seek (_IO_FILE *fp, _IO_off64_t offset, int dir)
+  attribute_hidden;
+_IO_off64_t _IO_cookie_seekoff (_IO_FILE *fp, _IO_off64_t offset,
+				int dir, int mode) attribute_hidden;
+int _IO_cookie_close (_IO_FILE* fp) attribute_hidden;
+
+int _IO_obstack_overflow (_IO_FILE *fp, int c) attribute_hidden;
+_IO_size_t _IO_obstack_xsputn (_IO_FILE *fp, const void *data, _IO_size_t n)
+  attribute_hidden;
+
+int _IO_printf_helper_overflow (_IO_FILE *s, int c) attribute_hidden;
+int _IO_wprintf_helper_overflow (_IO_FILE *s, int c) attribute_hidden;
diff --git a/libio/memstream.c b/libio/memstream.c
index 7fa5245..7f53c83 100644
--- a/libio/memstream.c
+++ b/libio/memstream.c
@@ -29,34 +29,6 @@  struct _IO_FILE_memstream
 };
 
 
-static int _IO_mem_sync (_IO_FILE* fp) __THROW;
-static void _IO_mem_finish (_IO_FILE* fp, int) __THROW;
-
-
-static const struct _IO_jump_t _IO_mem_jumps =
-{
-  JUMP_INIT_DUMMY,
-  JUMP_INIT (finish, _IO_mem_finish),
-  JUMP_INIT (overflow, _IO_str_overflow),
-  JUMP_INIT (underflow, _IO_str_underflow),
-  JUMP_INIT (uflow, _IO_default_uflow),
-  JUMP_INIT (pbackfail, _IO_str_pbackfail),
-  JUMP_INIT (xsputn, _IO_default_xsputn),
-  JUMP_INIT (xsgetn, _IO_default_xsgetn),
-  JUMP_INIT (seekoff, _IO_str_seekoff),
-  JUMP_INIT (seekpos, _IO_default_seekpos),
-  JUMP_INIT (setbuf, _IO_default_setbuf),
-  JUMP_INIT (sync, _IO_mem_sync),
-  JUMP_INIT (doallocate, _IO_default_doallocate),
-  JUMP_INIT (read, _IO_default_read),
-  JUMP_INIT (write, _IO_default_write),
-  JUMP_INIT (seek, _IO_default_seek),
-  JUMP_INIT (close, _IO_default_close),
-  JUMP_INIT (stat, _IO_default_stat),
-  JUMP_INIT(showmanyc, _IO_default_showmanyc),
-  JUMP_INIT(imbue, _IO_default_imbue)
-};
-
 /* Open a stream that writes into a malloc'd buffer that is expanded as
    necessary.  *BUFLOC and *SIZELOC are updated with the buffer's location
    and the number of characters written on fflush or fclose.  */
@@ -87,7 +59,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 (&new_f->fp._sf._sbf) = &IO_vtables[IO_VTABLE_mem];
   _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;
@@ -101,8 +73,7 @@  __open_memstream (char **bufloc, _IO_size_t *sizeloc)
 libc_hidden_def (__open_memstream)
 weak_alias (__open_memstream, open_memstream)
 
-
-static int
+int
 _IO_mem_sync (_IO_FILE *fp)
 {
   struct _IO_FILE_memstream *mp = (struct _IO_FILE_memstream *) fp;
@@ -121,8 +92,7 @@  _IO_mem_sync (_IO_FILE *fp)
   return 0;
 }
 
-
-static void
+void
 _IO_mem_finish (_IO_FILE *fp, int dummy)
 {
   struct _IO_FILE_memstream *mp = (struct _IO_FILE_memstream *) fp;
diff --git a/libio/obprintf.c b/libio/obprintf.c
index aa17b46..62917f3 100644
--- a/libio/obprintf.c
+++ b/libio/obprintf.c
@@ -36,7 +36,7 @@  struct _IO_obstack_file
 };
 
 
-static int
+int
 _IO_obstack_overflow (_IO_FILE *fp, int c)
 {
   struct obstack *obstack = ((struct _IO_obstack_file *) fp)->obstack;
@@ -59,7 +59,7 @@  _IO_obstack_overflow (_IO_FILE *fp, int c)
 }
 
 
-static _IO_size_t
+_IO_size_t
 _IO_obstack_xsputn (_IO_FILE *fp, const void *data, _IO_size_t n)
 {
   struct obstack *obstack = ((struct _IO_obstack_file *) fp)->obstack;
@@ -89,33 +89,6 @@  _IO_obstack_xsputn (_IO_FILE *fp, const void *data, _IO_size_t n)
   return n;
 }
 
-
-/* the jump table.  */
-const struct _IO_jump_t _IO_obstack_jumps attribute_hidden =
-{
-  JUMP_INIT_DUMMY,
-  JUMP_INIT(finish, NULL),
-  JUMP_INIT(overflow, _IO_obstack_overflow),
-  JUMP_INIT(underflow, NULL),
-  JUMP_INIT(uflow, NULL),
-  JUMP_INIT(pbackfail, NULL),
-  JUMP_INIT(xsputn, _IO_obstack_xsputn),
-  JUMP_INIT(xsgetn, NULL),
-  JUMP_INIT(seekoff, NULL),
-  JUMP_INIT(seekpos, NULL),
-  JUMP_INIT(setbuf, NULL),
-  JUMP_INIT(sync, NULL),
-  JUMP_INIT(doallocate, NULL),
-  JUMP_INIT(read, NULL),
-  JUMP_INIT(write, NULL),
-  JUMP_INIT(seek, NULL),
-  JUMP_INIT(close, NULL),
-  JUMP_INIT(stat, NULL),
-  JUMP_INIT(showmanyc, NULL),
-  JUMP_INIT(imbue, NULL)
-};
-
-
 int
 _IO_obstack_vprintf (struct obstack *obstack, const char *format, va_list args)
 {
@@ -132,7 +105,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 (&new_f.ofile.file) = &IO_vtables[IO_VTABLE_obstack];
   room = obstack_room (obstack);
   size = obstack_object_size (obstack) + room;
   if (size == 0)
diff --git a/libio/oldfileops.c b/libio/oldfileops.c
index 4f3bdfe..3916d3b 100644
--- a/libio/oldfileops.c
+++ b/libio/oldfileops.c
@@ -744,29 +744,6 @@  _IO_old_file_xsputn (_IO_FILE *f, const void *data, _IO_size_t n)
   return n - to_do;
 }
 
-
-const struct _IO_jump_t _IO_old_file_jumps =
-{
-  JUMP_INIT_DUMMY,
-  JUMP_INIT(finish, _IO_old_file_finish),
-  JUMP_INIT(overflow, _IO_old_file_overflow),
-  JUMP_INIT(underflow, _IO_old_file_underflow),
-  JUMP_INIT(uflow, _IO_default_uflow),
-  JUMP_INIT(pbackfail, _IO_default_pbackfail),
-  JUMP_INIT(xsputn, _IO_old_file_xsputn),
-  JUMP_INIT(xsgetn, _IO_default_xsgetn),
-  JUMP_INIT(seekoff, _IO_old_file_seekoff),
-  JUMP_INIT(seekpos, _IO_default_seekpos),
-  JUMP_INIT(setbuf, _IO_old_file_setbuf),
-  JUMP_INIT(sync, _IO_old_file_sync),
-  JUMP_INIT(doallocate, _IO_file_doallocate),
-  JUMP_INIT(read, _IO_file_read),
-  JUMP_INIT(write, _IO_old_file_write),
-  JUMP_INIT(seek, _IO_file_seek),
-  JUMP_INIT(close, _IO_file_close),
-  JUMP_INIT(stat, _IO_file_stat)
-};
-
 compat_symbol (libc, _IO_old_do_write, _IO_do_write, GLIBC_2_0);
 compat_symbol (libc, _IO_old_file_attach, _IO_file_attach, GLIBC_2_0);
 compat_symbol (libc, _IO_old_file_close_it, _IO_file_close_it, GLIBC_2_0);
diff --git a/libio/oldiofdopen.c b/libio/oldiofdopen.c
index 33406ff..64ae0df 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 (&new_f->fp) = &IO_vtables[IO_VTABLE_old_file];
   _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..3ab6a8a 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 (&new_f->fp) = &IO_vtables[IO_VTABLE_old_file];
   _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..0682c27 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 (&new_f->fpx.file) = &IO_vtables[IO_VTABLE_old_proc];
   _IO_old_file_init ((struct _IO_FILE_plus *) &new_f->fpx.file);
 #if  !_IO_UNIFIED_JUMPTABLES
   new_f->fpx.file.vtable = NULL;
@@ -267,29 +267,6 @@  _IO_old_proc_close (_IO_FILE *fp)
   return wstatus;
 }
 
-const struct _IO_jump_t _IO_old_proc_jumps = {
-  JUMP_INIT_DUMMY,
-  JUMP_INIT(finish, _IO_old_file_finish),
-  JUMP_INIT(overflow, _IO_old_file_overflow),
-  JUMP_INIT(underflow, _IO_old_file_underflow),
-  JUMP_INIT(uflow, _IO_default_uflow),
-  JUMP_INIT(pbackfail, _IO_default_pbackfail),
-  JUMP_INIT(xsputn, _IO_old_file_xsputn),
-  JUMP_INIT(xsgetn, _IO_default_xsgetn),
-  JUMP_INIT(seekoff, _IO_old_file_seekoff),
-  JUMP_INIT(seekpos, _IO_default_seekpos),
-  JUMP_INIT(setbuf, _IO_old_file_setbuf),
-  JUMP_INIT(sync, _IO_old_file_sync),
-  JUMP_INIT(doallocate, _IO_file_doallocate),
-  JUMP_INIT(read, _IO_file_read),
-  JUMP_INIT(write, _IO_old_file_write),
-  JUMP_INIT(seek, _IO_file_seek),
-  JUMP_INIT(close, _IO_old_proc_close),
-  JUMP_INIT(stat, _IO_file_stat),
-  JUMP_INIT(showmanyc, _IO_default_showmanyc),
-  JUMP_INIT(imbue, _IO_default_imbue)
-};
-
 strong_alias (_IO_old_popen, __old_popen)
 compat_symbol (libc, _IO_old_popen, _IO_popen, GLIBC_2_0);
 compat_symbol (libc, __old_popen, popen, GLIBC_2_0);
diff --git a/libio/oldstdfiles.c b/libio/oldstdfiles.c
index 609b7d9..86e9ded 100644
--- a/libio/oldstdfiles.c
+++ b/libio/oldstdfiles.c
@@ -40,11 +40,13 @@ 
 #define DEF_STDFILE(NAME, FD, CHAIN, FLAGS) \
   static _IO_lock_t _IO_stdfile_##FD##_lock = _IO_lock_initializer; \
   struct _IO_FILE_plus NAME \
-    = {FILEBUF_LITERAL(CHAIN, FLAGS, FD, NULL), &_IO_old_file_jumps};
+    = {FILEBUF_LITERAL(CHAIN, FLAGS, FD, NULL),
+       &IO_vtables[IO_VTABLE_old_file]};
 #else
 #define DEF_STDFILE(NAME, FD, CHAIN, FLAGS) \
   struct _IO_FILE_plus NAME \
-    = {FILEBUF_LITERAL(CHAIN, FLAGS, FD, NULL), &_IO_old_file_jumps};
+    = {FILEBUF_LITERAL(CHAIN, FLAGS, FD, NULL),
+       &IO_vtables[IO_VTABLE_old_file]};
 #endif
 
 DEF_STDFILE(_IO_stdin_, 0, 0, _IO_NO_WRITES);
diff --git a/libio/stdfiles.c b/libio/stdfiles.c
index 1f583ed..7dca8f9 100644
--- a/libio/stdfiles.c
+++ b/libio/stdfiles.c
@@ -38,30 +38,30 @@ 
 #  define DEF_STDFILE(NAME, FD, CHAIN, FLAGS) \
   static _IO_lock_t _IO_stdfile_##FD##_lock = _IO_lock_initializer; \
   static struct _IO_wide_data _IO_wide_data_##FD \
-    = { ._wide_vtable = &_IO_wfile_jumps }; \
+    = { ._wide_vtable = &IO_vtables[IO_VTABLE_wfile] }; \
   struct _IO_FILE_plus NAME \
     = {FILEBUF_LITERAL(CHAIN, FLAGS, FD, &_IO_wide_data_##FD), \
-       &_IO_file_jumps};
+       &IO_vtables[IO_VTABLE_file]};
 # else
 #  define DEF_STDFILE(NAME, FD, CHAIN, FLAGS) \
   static _IO_lock_t _IO_stdfile_##FD##_lock = _IO_lock_initializer; \
   struct _IO_FILE_plus NAME \
     = {FILEBUF_LITERAL(CHAIN, FLAGS, FD, NULL), \
-       &_IO_file_jumps};
+       &IO_vtables[IO_VTABLE_file]};
 # endif
 #else
 # if defined _LIBC || defined _GLIBCPP_USE_WCHAR_T
 #  define DEF_STDFILE(NAME, FD, CHAIN, FLAGS) \
   static struct _IO_wide_data _IO_wide_data_##FD \
-    = { ._wide_vtable = &_IO_wfile_jumps }; \
+    = { ._wide_vtable = &IO_vtables[IO_VTABLE_wfile] }; \
   struct _IO_FILE_plus NAME \
     = {FILEBUF_LITERAL(CHAIN, FLAGS, FD, &_IO_wide_data_##FD), \
-       &_IO_file_jumps};
+       &IO_vtables[IO_VTABLE_file]};
 # else
 #  define DEF_STDFILE(NAME, FD, CHAIN, FLAGS) \
   struct _IO_FILE_plus NAME \
     = {FILEBUF_LITERAL(CHAIN, FLAGS, FD, NULL), \
-       &_IO_file_jumps};
+       &IO_vtables[IO_VTABLE_file]};
 # endif
 #endif
 
diff --git a/libio/strfile.h b/libio/strfile.h
index f7ada8e..bd588d1 100644
--- a/libio/strfile.h
+++ b/libio/strfile.h
@@ -71,9 +71,6 @@  typedef struct
   char overflow_buf[64];
 } _IO_strnfile;
 
-extern const struct _IO_jump_t _IO_strn_jumps attribute_hidden;
-
-
 typedef struct
 {
   _IO_strfile f;
@@ -81,5 +78,3 @@  typedef struct
      provided by the user.  */
   wchar_t overflow_buf[64];
 } _IO_wstrnfile;
-
-extern const struct _IO_jump_t _IO_wstrn_jumps attribute_hidden;
diff --git a/libio/strops.c b/libio/strops.c
index 0932d4c..8b68bff 100644
--- a/libio/strops.c
+++ b/libio/strops.c
@@ -322,27 +322,3 @@  _IO_str_finish (_IO_FILE *fp, int dummy)
 
   _IO_default_finish (fp, 0);
 }
-
-const struct _IO_jump_t _IO_str_jumps =
-{
-  JUMP_INIT_DUMMY,
-  JUMP_INIT(finish, _IO_str_finish),
-  JUMP_INIT(overflow, _IO_str_overflow),
-  JUMP_INIT(underflow, _IO_str_underflow),
-  JUMP_INIT(uflow, _IO_default_uflow),
-  JUMP_INIT(pbackfail, _IO_str_pbackfail),
-  JUMP_INIT(xsputn, _IO_default_xsputn),
-  JUMP_INIT(xsgetn, _IO_default_xsgetn),
-  JUMP_INIT(seekoff, _IO_str_seekoff),
-  JUMP_INIT(seekpos, _IO_default_seekpos),
-  JUMP_INIT(setbuf, _IO_default_setbuf),
-  JUMP_INIT(sync, _IO_default_sync),
-  JUMP_INIT(doallocate, _IO_default_doallocate),
-  JUMP_INIT(read, _IO_default_read),
-  JUMP_INIT(write, _IO_default_write),
-  JUMP_INIT(seek, _IO_default_seek),
-  JUMP_INIT(close, _IO_default_close),
-  JUMP_INIT(stat, _IO_default_stat),
-  JUMP_INIT(showmanyc, _IO_default_showmanyc),
-  JUMP_INIT(imbue, _IO_default_imbue)
-};
diff --git a/libio/vasprintf.c b/libio/vasprintf.c
index 7460f1e..6a18c68 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 (&sf._sbf) = &IO_vtables[IO_VTABLE_str];
   _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..c3c6370 100644
--- a/libio/vsnprintf.c
+++ b/libio/vsnprintf.c
@@ -27,9 +27,7 @@ 
 #include "libioP.h"
 #include "strfile.h"
 
-static int _IO_strn_overflow (_IO_FILE *fp, int c) __THROW;
-
-static int
+int
 _IO_strn_overflow (_IO_FILE *fp, int c)
 {
   /* When we come to here this means the user supplied buffer is
@@ -63,32 +61,6 @@  _IO_strn_overflow (_IO_FILE *fp, int c)
   return c;
 }
 
-
-const struct _IO_jump_t _IO_strn_jumps attribute_hidden =
-{
-  JUMP_INIT_DUMMY,
-  JUMP_INIT(finish, _IO_str_finish),
-  JUMP_INIT(overflow, _IO_strn_overflow),
-  JUMP_INIT(underflow, _IO_str_underflow),
-  JUMP_INIT(uflow, _IO_default_uflow),
-  JUMP_INIT(pbackfail, _IO_str_pbackfail),
-  JUMP_INIT(xsputn, _IO_default_xsputn),
-  JUMP_INIT(xsgetn, _IO_default_xsgetn),
-  JUMP_INIT(seekoff, _IO_str_seekoff),
-  JUMP_INIT(seekpos, _IO_default_seekpos),
-  JUMP_INIT(setbuf, _IO_default_setbuf),
-  JUMP_INIT(sync, _IO_default_sync),
-  JUMP_INIT(doallocate, _IO_default_doallocate),
-  JUMP_INIT(read, _IO_default_read),
-  JUMP_INIT(write, _IO_default_write),
-  JUMP_INIT(seek, _IO_default_seek),
-  JUMP_INIT(close, _IO_default_close),
-  JUMP_INIT(stat, _IO_default_stat),
-  JUMP_INIT(showmanyc, _IO_default_showmanyc),
-  JUMP_INIT(imbue, _IO_default_imbue)
-};
-
-
 int
 _IO_vsnprintf (char *string, _IO_size_t maxlen, const char *format,
 	       _IO_va_list args)
@@ -108,7 +80,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 (&sf.f._sbf) = &IO_vtables[IO_VTABLE_strn];
   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/libio/vswprintf.c b/libio/vswprintf.c
index b90441a..3b00824 100644
--- a/libio/vswprintf.c
+++ b/libio/vswprintf.c
@@ -27,10 +27,7 @@ 
 #include "libioP.h"
 #include "strfile.h"
 
-
-static wint_t _IO_wstrn_overflow (_IO_FILE *fp, wint_t c) __THROW;
-
-static wint_t
+wint_t
 _IO_wstrn_overflow (_IO_FILE *fp, wint_t c)
 {
   /* When we come to here this means the user supplied buffer is
@@ -62,32 +59,6 @@  _IO_wstrn_overflow (_IO_FILE *fp, wint_t c)
   return c;
 }
 
-
-const struct _IO_jump_t _IO_wstrn_jumps attribute_hidden =
-{
-  JUMP_INIT_DUMMY,
-  JUMP_INIT(finish, _IO_wstr_finish),
-  JUMP_INIT(overflow, (_IO_overflow_t) _IO_wstrn_overflow),
-  JUMP_INIT(underflow, (_IO_underflow_t) _IO_wstr_underflow),
-  JUMP_INIT(uflow, (_IO_underflow_t) _IO_wdefault_uflow),
-  JUMP_INIT(pbackfail, (_IO_pbackfail_t) _IO_wstr_pbackfail),
-  JUMP_INIT(xsputn, _IO_wdefault_xsputn),
-  JUMP_INIT(xsgetn, _IO_wdefault_xsgetn),
-  JUMP_INIT(seekoff, _IO_wstr_seekoff),
-  JUMP_INIT(seekpos, _IO_default_seekpos),
-  JUMP_INIT(setbuf, _IO_default_setbuf),
-  JUMP_INIT(sync, _IO_default_sync),
-  JUMP_INIT(doallocate, _IO_wdefault_doallocate),
-  JUMP_INIT(read, _IO_default_read),
-  JUMP_INIT(write, _IO_default_write),
-  JUMP_INIT(seek, _IO_default_seek),
-  JUMP_INIT(close, _IO_default_close),
-  JUMP_INIT(stat, _IO_default_stat),
-  JUMP_INIT(showmanyc, _IO_default_showmanyc),
-  JUMP_INIT(imbue, _IO_default_imbue)
-};
-
-
 int
 _IO_vswprintf (wchar_t *string, _IO_size_t maxlen, const wchar_t *format,
 	       _IO_va_list args)
@@ -104,7 +75,8 @@  _IO_vswprintf (wchar_t *string, _IO_size_t maxlen, const wchar_t *format,
        length of zero always makes the function fail.  */
     return -1;
 
-  _IO_no_init (&sf.f._sbf._f, _IO_USER_LOCK, 0, &wd, &_IO_wstrn_jumps);
+  _IO_no_init (&sf.f._sbf._f, _IO_USER_LOCK, 0, &wd,
+	       &IO_vtables[IO_VTABLE_wstrn]);
   _IO_fwide (&sf.f._sbf._f, 1);
   string[0] = L'\0';
   _IO_wstr_init_static (&sf.f._sbf._f, string, maxlen - 1, string);
diff --git a/libio/vtables.c b/libio/vtables.c
new file mode 100644
index 0000000..0c72e01
--- /dev/null
+++ b/libio/vtables.c
@@ -0,0 +1,597 @@ 
+/* Central definition of libio jump tables.
+   Copyright (C) 1993-2016 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.
+
+   As a special exception, if you link the code in this file with
+   files compiled with a GNU compiler to produce an executable,
+   that does not cause the resulting executable to be covered by
+   the GNU Lesser General Public License.  This exception does not
+   however invalidate any other reasons why the executable file
+   might be covered by the GNU Lesser General Public License.
+   This exception applies to code released by its copyright holders
+   in files containing the exception.  */
+
+#include "libioP.h"
+#include <stdio.h>
+
+/* We keep all jump tables in an array, so that we can verify jump
+   table pointers easily.  */
+
+const struct _IO_jump_t IO_vtables[] =
+  {
+    [IO_VTABLE_file] =
+    {
+      JUMP_INIT_DUMMY,
+      JUMP_INIT(finish, _IO_file_finish),
+      JUMP_INIT(overflow, _IO_file_overflow),
+      JUMP_INIT(underflow, _IO_file_underflow),
+      JUMP_INIT(uflow, _IO_default_uflow),
+      JUMP_INIT(pbackfail, _IO_default_pbackfail),
+      JUMP_INIT(xsputn, _IO_file_xsputn),
+      JUMP_INIT(xsgetn, _IO_file_xsgetn),
+      JUMP_INIT(seekoff, _IO_new_file_seekoff),
+      JUMP_INIT(seekpos, _IO_default_seekpos),
+      JUMP_INIT(setbuf, _IO_new_file_setbuf),
+      JUMP_INIT(sync, _IO_new_file_sync),
+      JUMP_INIT(doallocate, _IO_file_doallocate),
+      JUMP_INIT(read, _IO_file_read),
+      JUMP_INIT(write, _IO_new_file_write),
+      JUMP_INIT(seek, _IO_file_seek),
+      JUMP_INIT(close, _IO_file_close),
+      JUMP_INIT(stat, _IO_file_stat),
+      JUMP_INIT(showmanyc, _IO_default_showmanyc),
+      JUMP_INIT(imbue, _IO_default_imbue)
+    },
+
+    [IO_VTABLE_file_mmap] =
+    {
+      JUMP_INIT_DUMMY,
+      JUMP_INIT(finish, _IO_file_finish),
+      JUMP_INIT(overflow, _IO_file_overflow),
+      JUMP_INIT(underflow, _IO_file_underflow_mmap),
+      JUMP_INIT(uflow, _IO_default_uflow),
+      JUMP_INIT(pbackfail, _IO_default_pbackfail),
+      JUMP_INIT(xsputn, _IO_new_file_xsputn),
+      JUMP_INIT(xsgetn, _IO_file_xsgetn_mmap),
+      JUMP_INIT(seekoff, _IO_file_seekoff_mmap),
+      JUMP_INIT(seekpos, _IO_default_seekpos),
+      JUMP_INIT(setbuf, (_IO_setbuf_t) _IO_file_setbuf_mmap),
+      JUMP_INIT(sync, _IO_file_sync_mmap),
+      JUMP_INIT(doallocate, _IO_file_doallocate),
+      JUMP_INIT(read, _IO_file_read),
+      JUMP_INIT(write, _IO_new_file_write),
+      JUMP_INIT(seek, _IO_file_seek),
+      JUMP_INIT(close, _IO_file_close_mmap),
+      JUMP_INIT(stat, _IO_file_stat),
+      JUMP_INIT(showmanyc, _IO_default_showmanyc),
+      JUMP_INIT(imbue, _IO_default_imbue)
+    },
+
+    [IO_VTABLE_file_maybe_mmap] =
+    {
+      JUMP_INIT_DUMMY,
+      JUMP_INIT(finish, _IO_file_finish),
+      JUMP_INIT(overflow, _IO_file_overflow),
+      JUMP_INIT(underflow, _IO_file_underflow_maybe_mmap),
+      JUMP_INIT(uflow, _IO_default_uflow),
+      JUMP_INIT(pbackfail, _IO_default_pbackfail),
+      JUMP_INIT(xsputn, _IO_new_file_xsputn),
+      JUMP_INIT(xsgetn, _IO_file_xsgetn_maybe_mmap),
+      JUMP_INIT(seekoff, _IO_file_seekoff_maybe_mmap),
+      JUMP_INIT(seekpos, _IO_default_seekpos),
+      JUMP_INIT(setbuf, (_IO_setbuf_t) _IO_file_setbuf_mmap),
+      JUMP_INIT(sync, _IO_new_file_sync),
+      JUMP_INIT(doallocate, _IO_file_doallocate),
+      JUMP_INIT(read, _IO_file_read),
+      JUMP_INIT(write, _IO_new_file_write),
+      JUMP_INIT(seek, _IO_file_seek),
+      JUMP_INIT(close, _IO_file_close),
+      JUMP_INIT(stat, _IO_file_stat),
+      JUMP_INIT(showmanyc, _IO_default_showmanyc),
+      JUMP_INIT(imbue, _IO_default_imbue)
+    },
+
+    [IO_VTABLE_wfile] =
+    {
+      JUMP_INIT_DUMMY,
+      JUMP_INIT(finish, _IO_new_file_finish),
+      JUMP_INIT(overflow, (_IO_overflow_t) _IO_wfile_overflow),
+      JUMP_INIT(underflow, (_IO_underflow_t) _IO_wfile_underflow),
+      JUMP_INIT(uflow, (_IO_underflow_t) _IO_wdefault_uflow),
+      JUMP_INIT(pbackfail, (_IO_pbackfail_t) _IO_wdefault_pbackfail),
+      JUMP_INIT(xsputn, _IO_wfile_xsputn),
+      JUMP_INIT(xsgetn, _IO_file_xsgetn),
+      JUMP_INIT(seekoff, _IO_wfile_seekoff),
+      JUMP_INIT(seekpos, _IO_default_seekpos),
+      JUMP_INIT(setbuf, _IO_new_file_setbuf),
+      JUMP_INIT(sync, (_IO_sync_t) _IO_wfile_sync),
+      JUMP_INIT(doallocate, _IO_wfile_doallocate),
+      JUMP_INIT(read, _IO_file_read),
+      JUMP_INIT(write, _IO_new_file_write),
+      JUMP_INIT(seek, _IO_file_seek),
+      JUMP_INIT(close, _IO_file_close),
+      JUMP_INIT(stat, _IO_file_stat),
+      JUMP_INIT(showmanyc, _IO_default_showmanyc),
+      JUMP_INIT(imbue, _IO_default_imbue)
+    },
+
+    [IO_VTABLE_wfile_mmap] =
+    {
+      JUMP_INIT_DUMMY,
+      JUMP_INIT(finish, _IO_new_file_finish),
+      JUMP_INIT(overflow, (_IO_overflow_t) _IO_wfile_overflow),
+      JUMP_INIT(underflow, (_IO_underflow_t) _IO_wfile_underflow_mmap),
+      JUMP_INIT(uflow, (_IO_underflow_t) _IO_wdefault_uflow),
+      JUMP_INIT(pbackfail, (_IO_pbackfail_t) _IO_wdefault_pbackfail),
+      JUMP_INIT(xsputn, _IO_wfile_xsputn),
+      JUMP_INIT(xsgetn, _IO_file_xsgetn),
+      JUMP_INIT(seekoff, _IO_wfile_seekoff),
+      JUMP_INIT(seekpos, _IO_default_seekpos),
+      JUMP_INIT(setbuf, _IO_file_setbuf_mmap),
+      JUMP_INIT(sync, (_IO_sync_t) _IO_wfile_sync),
+      JUMP_INIT(doallocate, _IO_wfile_doallocate),
+      JUMP_INIT(read, _IO_file_read),
+      JUMP_INIT(write, _IO_new_file_write),
+      JUMP_INIT(seek, _IO_file_seek),
+      JUMP_INIT(close, _IO_file_close_mmap),
+      JUMP_INIT(stat, _IO_file_stat),
+      JUMP_INIT(showmanyc, _IO_default_showmanyc),
+      JUMP_INIT(imbue, _IO_default_imbue)
+    },
+
+    [IO_VTABLE_wfile_maybe_mmap] =
+    {
+      JUMP_INIT_DUMMY,
+      JUMP_INIT(finish, _IO_new_file_finish),
+      JUMP_INIT(overflow, (_IO_overflow_t) _IO_wfile_overflow),
+      JUMP_INIT(underflow, (_IO_underflow_t) _IO_wfile_underflow_maybe_mmap),
+      JUMP_INIT(uflow, (_IO_underflow_t) _IO_wdefault_uflow),
+      JUMP_INIT(pbackfail, (_IO_pbackfail_t) _IO_wdefault_pbackfail),
+      JUMP_INIT(xsputn, _IO_wfile_xsputn),
+      JUMP_INIT(xsgetn, _IO_file_xsgetn),
+      JUMP_INIT(seekoff, _IO_wfile_seekoff),
+      JUMP_INIT(seekpos, _IO_default_seekpos),
+      JUMP_INIT(setbuf, _IO_file_setbuf_mmap),
+      JUMP_INIT(sync, (_IO_sync_t) _IO_wfile_sync),
+      JUMP_INIT(doallocate, _IO_wfile_doallocate),
+      JUMP_INIT(read, _IO_file_read),
+      JUMP_INIT(write, _IO_new_file_write),
+      JUMP_INIT(seek, _IO_file_seek),
+      JUMP_INIT(close, _IO_file_close),
+      JUMP_INIT(stat, _IO_file_stat),
+      JUMP_INIT(showmanyc, _IO_default_showmanyc),
+      JUMP_INIT(imbue, _IO_default_imbue)
+    },
+
+    [IO_VTABLE_mem] =
+    {
+      JUMP_INIT_DUMMY,
+      JUMP_INIT (finish, _IO_mem_finish),
+      JUMP_INIT (overflow, _IO_str_overflow),
+      JUMP_INIT (underflow, _IO_str_underflow),
+      JUMP_INIT (uflow, _IO_default_uflow),
+      JUMP_INIT (pbackfail, _IO_str_pbackfail),
+      JUMP_INIT (xsputn, _IO_default_xsputn),
+      JUMP_INIT (xsgetn, _IO_default_xsgetn),
+      JUMP_INIT (seekoff, _IO_str_seekoff),
+      JUMP_INIT (seekpos, _IO_default_seekpos),
+      JUMP_INIT (setbuf, _IO_default_setbuf),
+      JUMP_INIT (sync, _IO_mem_sync),
+      JUMP_INIT (doallocate, _IO_default_doallocate),
+      JUMP_INIT (read, _IO_default_read),
+      JUMP_INIT (write, _IO_default_write),
+      JUMP_INIT (seek, _IO_default_seek),
+      JUMP_INIT (close, _IO_default_close),
+      JUMP_INIT (stat, _IO_default_stat),
+      JUMP_INIT(showmanyc, _IO_default_showmanyc),
+      JUMP_INIT(imbue, _IO_default_imbue)
+    },
+
+    [IO_VTABLE_wmem] =
+    {
+      JUMP_INIT_DUMMY,
+      JUMP_INIT (finish, _IO_wmem_finish),
+      JUMP_INIT (overflow, (_IO_overflow_t) _IO_wstr_overflow),
+      JUMP_INIT (underflow, (_IO_underflow_t) _IO_wstr_underflow),
+      JUMP_INIT (uflow, (_IO_underflow_t) _IO_wdefault_uflow),
+      JUMP_INIT (pbackfail, (_IO_pbackfail_t) _IO_wstr_pbackfail),
+      JUMP_INIT (xsputn, _IO_wdefault_xsputn),
+      JUMP_INIT (xsgetn, _IO_wdefault_xsgetn),
+      JUMP_INIT (seekoff, _IO_wstr_seekoff),
+      JUMP_INIT (seekpos, _IO_default_seekpos),
+      JUMP_INIT (setbuf, _IO_default_setbuf),
+      JUMP_INIT (sync, _IO_wmem_sync),
+      JUMP_INIT (doallocate, _IO_wdefault_doallocate),
+      JUMP_INIT (read, _IO_default_read),
+      JUMP_INIT (write, _IO_default_write),
+      JUMP_INIT (seek, _IO_default_seek),
+      JUMP_INIT (close, _IO_default_close),
+      JUMP_INIT (stat, _IO_default_stat),
+      JUMP_INIT (showmanyc, _IO_default_showmanyc),
+      JUMP_INIT (imbue, _IO_default_imbue)
+    },
+
+    [IO_VTABLE_proc] =
+    {
+      JUMP_INIT_DUMMY,
+      JUMP_INIT(finish, _IO_new_file_finish),
+      JUMP_INIT(overflow, _IO_new_file_overflow),
+      JUMP_INIT(underflow, _IO_new_file_underflow),
+      JUMP_INIT(uflow, _IO_default_uflow),
+      JUMP_INIT(pbackfail, _IO_default_pbackfail),
+      JUMP_INIT(xsputn, _IO_new_file_xsputn),
+      JUMP_INIT(xsgetn, _IO_default_xsgetn),
+      JUMP_INIT(seekoff, _IO_new_file_seekoff),
+      JUMP_INIT(seekpos, _IO_default_seekpos),
+      JUMP_INIT(setbuf, _IO_new_file_setbuf),
+      JUMP_INIT(sync, _IO_new_file_sync),
+      JUMP_INIT(doallocate, _IO_file_doallocate),
+      JUMP_INIT(read, _IO_file_read),
+      JUMP_INIT(write, _IO_new_file_write),
+      JUMP_INIT(seek, _IO_file_seek),
+      JUMP_INIT(close, _IO_new_proc_close),
+      JUMP_INIT(stat, _IO_file_stat),
+      JUMP_INIT(showmanyc, _IO_default_showmanyc),
+      JUMP_INIT(imbue, _IO_default_imbue)
+    },
+
+    [IO_VTABLE_str] =
+    {
+      JUMP_INIT_DUMMY,
+      JUMP_INIT(finish, _IO_str_finish),
+      JUMP_INIT(overflow, _IO_str_overflow),
+      JUMP_INIT(underflow, _IO_str_underflow),
+      JUMP_INIT(uflow, _IO_default_uflow),
+      JUMP_INIT(pbackfail, _IO_str_pbackfail),
+      JUMP_INIT(xsputn, _IO_default_xsputn),
+      JUMP_INIT(xsgetn, _IO_default_xsgetn),
+      JUMP_INIT(seekoff, _IO_str_seekoff),
+      JUMP_INIT(seekpos, _IO_default_seekpos),
+      JUMP_INIT(setbuf, _IO_default_setbuf),
+      JUMP_INIT(sync, _IO_default_sync),
+      JUMP_INIT(doallocate, _IO_default_doallocate),
+      JUMP_INIT(read, _IO_default_read),
+      JUMP_INIT(write, _IO_default_write),
+      JUMP_INIT(seek, _IO_default_seek),
+      JUMP_INIT(close, _IO_default_close),
+      JUMP_INIT(stat, _IO_default_stat),
+      JUMP_INIT(showmanyc, _IO_default_showmanyc),
+      JUMP_INIT(imbue, _IO_default_imbue)
+    },
+
+    [IO_VTABLE_str_chk] =
+    {
+      JUMP_INIT_DUMMY,
+      JUMP_INIT(finish, _IO_str_finish),
+      JUMP_INIT(overflow, _IO_str_chk_overflow),
+      JUMP_INIT(underflow, _IO_str_underflow),
+      JUMP_INIT(uflow, _IO_default_uflow),
+      JUMP_INIT(pbackfail, _IO_str_pbackfail),
+      JUMP_INIT(xsputn, _IO_default_xsputn),
+      JUMP_INIT(xsgetn, _IO_default_xsgetn),
+      JUMP_INIT(seekoff, _IO_str_seekoff),
+      JUMP_INIT(seekpos, _IO_default_seekpos),
+      JUMP_INIT(setbuf, _IO_default_setbuf),
+      JUMP_INIT(sync, _IO_default_sync),
+      JUMP_INIT(doallocate, _IO_default_doallocate),
+      JUMP_INIT(read, _IO_default_read),
+      JUMP_INIT(write, _IO_default_write),
+      JUMP_INIT(seek, _IO_default_seek),
+      JUMP_INIT(close, _IO_default_close),
+      JUMP_INIT(stat, _IO_default_stat),
+      JUMP_INIT(showmanyc, _IO_default_showmanyc),
+      JUMP_INIT(imbue, _IO_default_imbue)
+    },
+
+    [IO_VTABLE_strn] =
+    {
+      JUMP_INIT_DUMMY,
+      JUMP_INIT(finish, _IO_str_finish),
+      JUMP_INIT(overflow, _IO_strn_overflow),
+      JUMP_INIT(underflow, _IO_str_underflow),
+      JUMP_INIT(uflow, _IO_default_uflow),
+      JUMP_INIT(pbackfail, _IO_str_pbackfail),
+      JUMP_INIT(xsputn, _IO_default_xsputn),
+      JUMP_INIT(xsgetn, _IO_default_xsgetn),
+      JUMP_INIT(seekoff, _IO_str_seekoff),
+      JUMP_INIT(seekpos, _IO_default_seekpos),
+      JUMP_INIT(setbuf, _IO_default_setbuf),
+      JUMP_INIT(sync, _IO_default_sync),
+      JUMP_INIT(doallocate, _IO_default_doallocate),
+      JUMP_INIT(read, _IO_default_read),
+      JUMP_INIT(write, _IO_default_write),
+      JUMP_INIT(seek, _IO_default_seek),
+      JUMP_INIT(close, _IO_default_close),
+      JUMP_INIT(stat, _IO_default_stat),
+      JUMP_INIT(showmanyc, _IO_default_showmanyc),
+      JUMP_INIT(imbue, _IO_default_imbue)
+    },
+
+    [IO_VTABLE_wstr] =
+    {
+      JUMP_INIT_DUMMY,
+      JUMP_INIT(finish, _IO_wstr_finish),
+      JUMP_INIT(overflow, (_IO_overflow_t) _IO_wstr_overflow),
+      JUMP_INIT(underflow, (_IO_underflow_t) _IO_wstr_underflow),
+      JUMP_INIT(uflow, (_IO_underflow_t) _IO_wdefault_uflow),
+      JUMP_INIT(pbackfail, (_IO_pbackfail_t) _IO_wstr_pbackfail),
+      JUMP_INIT(xsputn, _IO_wdefault_xsputn),
+      JUMP_INIT(xsgetn, _IO_wdefault_xsgetn),
+      JUMP_INIT(seekoff, _IO_wstr_seekoff),
+      JUMP_INIT(seekpos, _IO_default_seekpos),
+      JUMP_INIT(setbuf, _IO_default_setbuf),
+      JUMP_INIT(sync, _IO_default_sync),
+      JUMP_INIT(doallocate, _IO_wdefault_doallocate),
+      JUMP_INIT(read, _IO_default_read),
+      JUMP_INIT(write, _IO_default_write),
+      JUMP_INIT(seek, _IO_default_seek),
+      JUMP_INIT(close, _IO_default_close),
+      JUMP_INIT(stat, _IO_default_stat),
+      JUMP_INIT(showmanyc, _IO_default_showmanyc),
+      JUMP_INIT(imbue, _IO_default_imbue)
+    },
+
+    [IO_VTABLE_wstrn] =
+    {
+      JUMP_INIT_DUMMY,
+      JUMP_INIT(finish, _IO_wstr_finish),
+      JUMP_INIT(overflow, (_IO_overflow_t) _IO_wstrn_overflow),
+      JUMP_INIT(underflow, (_IO_underflow_t) _IO_wstr_underflow),
+      JUMP_INIT(uflow, (_IO_underflow_t) _IO_wdefault_uflow),
+      JUMP_INIT(pbackfail, (_IO_pbackfail_t) _IO_wstr_pbackfail),
+      JUMP_INIT(xsputn, _IO_wdefault_xsputn),
+      JUMP_INIT(xsgetn, _IO_wdefault_xsgetn),
+      JUMP_INIT(seekoff, _IO_wstr_seekoff),
+      JUMP_INIT(seekpos, _IO_default_seekpos),
+      JUMP_INIT(setbuf, _IO_default_setbuf),
+      JUMP_INIT(sync, _IO_default_sync),
+      JUMP_INIT(doallocate, _IO_wdefault_doallocate),
+      JUMP_INIT(read, _IO_default_read),
+      JUMP_INIT(write, _IO_default_write),
+      JUMP_INIT(seek, _IO_default_seek),
+      JUMP_INIT(close, _IO_default_close),
+      JUMP_INIT(stat, _IO_default_stat),
+      JUMP_INIT(showmanyc, _IO_default_showmanyc),
+      JUMP_INIT(imbue, _IO_default_imbue)
+    },
+
+    [IO_VTABLE_cookie] =
+    {
+      JUMP_INIT_DUMMY,
+      JUMP_INIT(finish, _IO_file_finish),
+      JUMP_INIT(overflow, _IO_file_overflow),
+      JUMP_INIT(underflow, _IO_file_underflow),
+      JUMP_INIT(uflow, _IO_default_uflow),
+      JUMP_INIT(pbackfail, _IO_default_pbackfail),
+      JUMP_INIT(xsputn, _IO_file_xsputn),
+      JUMP_INIT(xsgetn, _IO_default_xsgetn),
+      JUMP_INIT(seekoff, _IO_cookie_seekoff),
+      JUMP_INIT(seekpos, _IO_default_seekpos),
+      JUMP_INIT(setbuf, _IO_file_setbuf),
+      JUMP_INIT(sync, _IO_file_sync),
+      JUMP_INIT(doallocate, _IO_file_doallocate),
+      JUMP_INIT(read, _IO_cookie_read),
+      JUMP_INIT(write, _IO_cookie_write),
+      JUMP_INIT(seek, _IO_cookie_seek),
+      JUMP_INIT(close, _IO_cookie_close),
+      JUMP_INIT(stat, _IO_default_stat),
+      JUMP_INIT(showmanyc, _IO_default_showmanyc),
+      JUMP_INIT(imbue, _IO_default_imbue),
+    },
+
+    [IO_VTABLE_obstack] =
+    {
+      JUMP_INIT_DUMMY,
+      JUMP_INIT(finish, NULL),
+      JUMP_INIT(overflow, _IO_obstack_overflow),
+      JUMP_INIT(underflow, NULL),
+      JUMP_INIT(uflow, NULL),
+      JUMP_INIT(pbackfail, NULL),
+      JUMP_INIT(xsputn, _IO_obstack_xsputn),
+      JUMP_INIT(xsgetn, NULL),
+      JUMP_INIT(seekoff, NULL),
+      JUMP_INIT(seekpos, NULL),
+      JUMP_INIT(setbuf, NULL),
+      JUMP_INIT(sync, NULL),
+      JUMP_INIT(doallocate, NULL),
+      JUMP_INIT(read, NULL),
+      JUMP_INIT(write, NULL),
+      JUMP_INIT(seek, NULL),
+      JUMP_INIT(close, NULL),
+      JUMP_INIT(stat, NULL),
+      JUMP_INIT(showmanyc, NULL),
+      JUMP_INIT(imbue, NULL)
+    },
+
+    [IO_VTABLE_printf_helper] =
+    {
+      JUMP_INIT_DUMMY,
+      JUMP_INIT (finish, _IO_default_finish),
+      JUMP_INIT (overflow, _IO_printf_helper_overflow),
+      JUMP_INIT (underflow, _IO_default_underflow),
+      JUMP_INIT (uflow, _IO_default_uflow),
+      JUMP_INIT (pbackfail, _IO_default_pbackfail),
+      JUMP_INIT (xsputn, _IO_default_xsputn),
+      JUMP_INIT (xsgetn, _IO_default_xsgetn),
+      JUMP_INIT (seekoff, _IO_default_seekoff),
+      JUMP_INIT (seekpos, _IO_default_seekpos),
+      JUMP_INIT (setbuf, _IO_default_setbuf),
+      JUMP_INIT (sync, _IO_default_sync),
+      JUMP_INIT (doallocate, _IO_default_doallocate),
+      JUMP_INIT (read, _IO_default_read),
+      JUMP_INIT (write, _IO_default_write),
+      JUMP_INIT (seek, _IO_default_seek),
+      JUMP_INIT (close, _IO_default_close),
+      JUMP_INIT (stat, _IO_default_stat)
+    },
+
+    [IO_VTABLE_wprintf_helper] =
+    {
+      JUMP_INIT_DUMMY,
+      JUMP_INIT (finish, _IO_wdefault_finish),
+      JUMP_INIT (overflow, _IO_wprintf_helper_overflow),
+      JUMP_INIT (underflow, _IO_default_underflow),
+      JUMP_INIT (uflow, _IO_default_uflow),
+      JUMP_INIT (pbackfail, (_IO_pbackfail_t) _IO_wdefault_pbackfail),
+      JUMP_INIT (xsputn, _IO_wdefault_xsputn),
+      JUMP_INIT (xsgetn, _IO_wdefault_xsgetn),
+      JUMP_INIT (seekoff, _IO_default_seekoff),
+      JUMP_INIT (seekpos, _IO_default_seekpos),
+      JUMP_INIT (setbuf, _IO_default_setbuf),
+      JUMP_INIT (sync, _IO_default_sync),
+      JUMP_INIT (doallocate, _IO_wdefault_doallocate),
+      JUMP_INIT (read, _IO_default_read),
+      JUMP_INIT (write, _IO_default_write),
+      JUMP_INIT (seek, _IO_default_seek),
+      JUMP_INIT (close, _IO_default_close),
+      JUMP_INIT (stat, _IO_default_stat)
+    },
+
+    /* Obsolete vtables.  */
+#if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_1)
+    [IO_VTABLE_old_file] =
+    {
+      JUMP_INIT_DUMMY,
+      JUMP_INIT(finish, _IO_old_file_finish),
+      JUMP_INIT(overflow, _IO_old_file_overflow),
+      JUMP_INIT(underflow, _IO_old_file_underflow),
+      JUMP_INIT(uflow, _IO_default_uflow),
+      JUMP_INIT(pbackfail, _IO_default_pbackfail),
+      JUMP_INIT(xsputn, _IO_old_file_xsputn),
+      JUMP_INIT(xsgetn, _IO_default_xsgetn),
+      JUMP_INIT(seekoff, _IO_old_file_seekoff),
+      JUMP_INIT(seekpos, _IO_default_seekpos),
+      JUMP_INIT(setbuf, _IO_old_file_setbuf),
+      JUMP_INIT(sync, _IO_old_file_sync),
+      JUMP_INIT(doallocate, _IO_file_doallocate),
+      JUMP_INIT(read, _IO_file_read),
+      JUMP_INIT(write, _IO_old_file_write),
+      JUMP_INIT(seek, _IO_file_seek),
+      JUMP_INIT(close, _IO_file_close),
+      JUMP_INIT(stat, _IO_file_stat)
+    },
+
+    [IO_VTABLE_old_proc] =
+    {
+      JUMP_INIT_DUMMY,
+      JUMP_INIT(finish, _IO_old_file_finish),
+      JUMP_INIT(overflow, _IO_old_file_overflow),
+      JUMP_INIT(underflow, _IO_old_file_underflow),
+      JUMP_INIT(uflow, _IO_default_uflow),
+      JUMP_INIT(pbackfail, _IO_default_pbackfail),
+      JUMP_INIT(xsputn, _IO_old_file_xsputn),
+      JUMP_INIT(xsgetn, _IO_default_xsgetn),
+      JUMP_INIT(seekoff, _IO_old_file_seekoff),
+      JUMP_INIT(seekpos, _IO_default_seekpos),
+      JUMP_INIT(setbuf, _IO_old_file_setbuf),
+      JUMP_INIT(sync, _IO_old_file_sync),
+      JUMP_INIT(doallocate, _IO_file_doallocate),
+      JUMP_INIT(read, _IO_file_read),
+      JUMP_INIT(write, _IO_old_file_write),
+      JUMP_INIT(seek, _IO_file_seek),
+      JUMP_INIT(close, _IO_old_proc_close),
+      JUMP_INIT(stat, _IO_file_stat),
+      JUMP_INIT(showmanyc, _IO_default_showmanyc),
+      JUMP_INIT(imbue, _IO_default_imbue)
+    },
+
+    [IO_VTABLE_old_cookie] =
+    {
+      JUMP_INIT_DUMMY,
+      JUMP_INIT(finish, _IO_file_finish),
+      JUMP_INIT(overflow, _IO_file_overflow),
+      JUMP_INIT(underflow, _IO_file_underflow),
+      JUMP_INIT(uflow, _IO_default_uflow),
+      JUMP_INIT(pbackfail, _IO_default_pbackfail),
+      JUMP_INIT(xsputn, _IO_file_xsputn),
+      JUMP_INIT(xsgetn, _IO_default_xsgetn),
+      JUMP_INIT(seekoff, _IO_cookie_seekoff),
+      JUMP_INIT(seekpos, _IO_default_seekpos),
+      JUMP_INIT(setbuf, _IO_file_setbuf),
+      JUMP_INIT(sync, _IO_file_sync),
+      JUMP_INIT(doallocate, _IO_file_doallocate),
+      JUMP_INIT(read, _IO_cookie_read),
+      JUMP_INIT(write, _IO_cookie_write),
+      JUMP_INIT(seek, _IO_old_cookie_seek),
+      JUMP_INIT(close, _IO_cookie_close),
+      JUMP_INIT(stat, _IO_default_stat),
+      JUMP_INIT(showmanyc, _IO_default_showmanyc),
+      JUMP_INIT(imbue, _IO_default_imbue),
+    },
+#endif
+  };
+
+
+/* These symbols need to be exported for backwards compatibility.
+   They are no longer used by glibc itself.  */
+
+const struct _IO_jump_t _IO_file_jumps =
+{
+  JUMP_INIT_DUMMY,
+  JUMP_INIT(finish, _IO_file_finish),
+  JUMP_INIT(overflow, _IO_file_overflow),
+  JUMP_INIT(underflow, _IO_file_underflow),
+  JUMP_INIT(uflow, _IO_default_uflow),
+  JUMP_INIT(pbackfail, _IO_default_pbackfail),
+  JUMP_INIT(xsputn, _IO_file_xsputn),
+  JUMP_INIT(xsgetn, _IO_file_xsgetn),
+  JUMP_INIT(seekoff, _IO_new_file_seekoff),
+  JUMP_INIT(seekpos, _IO_default_seekpos),
+  JUMP_INIT(setbuf, _IO_new_file_setbuf),
+  JUMP_INIT(sync, _IO_new_file_sync),
+  JUMP_INIT(doallocate, _IO_file_doallocate),
+  JUMP_INIT(read, _IO_file_read),
+  JUMP_INIT(write, _IO_new_file_write),
+  JUMP_INIT(seek, _IO_file_seek),
+  JUMP_INIT(close, _IO_file_close),
+  JUMP_INIT(stat, _IO_file_stat),
+  JUMP_INIT(showmanyc, _IO_default_showmanyc),
+  JUMP_INIT(imbue, _IO_default_imbue)
+};
+
+const struct _IO_jump_t _IO_wfile_jumps =
+{
+  JUMP_INIT_DUMMY,
+  JUMP_INIT(finish, _IO_new_file_finish),
+  JUMP_INIT(overflow, (_IO_overflow_t) _IO_wfile_overflow),
+  JUMP_INIT(underflow, (_IO_underflow_t) _IO_wfile_underflow),
+  JUMP_INIT(uflow, (_IO_underflow_t) _IO_wdefault_uflow),
+  JUMP_INIT(pbackfail, (_IO_pbackfail_t) _IO_wdefault_pbackfail),
+  JUMP_INIT(xsputn, _IO_wfile_xsputn),
+  JUMP_INIT(xsgetn, _IO_file_xsgetn),
+  JUMP_INIT(seekoff, _IO_wfile_seekoff),
+  JUMP_INIT(seekpos, _IO_default_seekpos),
+  JUMP_INIT(setbuf, _IO_new_file_setbuf),
+  JUMP_INIT(sync, (_IO_sync_t) _IO_wfile_sync),
+  JUMP_INIT(doallocate, _IO_wfile_doallocate),
+  JUMP_INIT(read, _IO_file_read),
+  JUMP_INIT(write, _IO_new_file_write),
+  JUMP_INIT(seek, _IO_file_seek),
+  JUMP_INIT(close, _IO_file_close),
+  JUMP_INIT(stat, _IO_file_stat),
+  JUMP_INIT(showmanyc, _IO_default_showmanyc),
+  JUMP_INIT(imbue, _IO_default_imbue)
+};
+
+bool IO_accept_foreign_vtables attribute_hidden;
+
+void attribute_hidden
+IO_vtable_check (void)
+{
+  if (!IO_accept_foreign_vtables)
+    __libc_fatal ("invalid stdio vtable pointer detected\n");
+}
diff --git a/libio/wfileops.c b/libio/wfileops.c
index df1fbda..13cded4 100644
--- a/libio/wfileops.c
+++ b/libio/wfileops.c
@@ -349,7 +349,7 @@  _IO_wfile_underflow (_IO_FILE *fp)
 libc_hidden_def (_IO_wfile_underflow)
 
 
-static wint_t
+wint_t
 _IO_wfile_underflow_mmap (_IO_FILE *fp)
 {
   struct _IO_codecvt *cd;
@@ -410,7 +410,7 @@  _IO_wfile_underflow_mmap (_IO_FILE *fp)
   return WEOF;
 }
 
-static wint_t
+wint_t
 _IO_wfile_underflow_maybe_mmap (_IO_FILE *fp)
 {
   /* This is the first read attempt.  Doing the underflow will choose mmap
@@ -1040,78 +1040,3 @@  _IO_wfile_xsputn (_IO_FILE *f, const void *data, _IO_size_t n)
   return n - to_do;
 }
 libc_hidden_def (_IO_wfile_xsputn)
-
-
-const struct _IO_jump_t _IO_wfile_jumps =
-{
-  JUMP_INIT_DUMMY,
-  JUMP_INIT(finish, _IO_new_file_finish),
-  JUMP_INIT(overflow, (_IO_overflow_t) _IO_wfile_overflow),
-  JUMP_INIT(underflow, (_IO_underflow_t) _IO_wfile_underflow),
-  JUMP_INIT(uflow, (_IO_underflow_t) _IO_wdefault_uflow),
-  JUMP_INIT(pbackfail, (_IO_pbackfail_t) _IO_wdefault_pbackfail),
-  JUMP_INIT(xsputn, _IO_wfile_xsputn),
-  JUMP_INIT(xsgetn, _IO_file_xsgetn),
-  JUMP_INIT(seekoff, _IO_wfile_seekoff),
-  JUMP_INIT(seekpos, _IO_default_seekpos),
-  JUMP_INIT(setbuf, _IO_new_file_setbuf),
-  JUMP_INIT(sync, (_IO_sync_t) _IO_wfile_sync),
-  JUMP_INIT(doallocate, _IO_wfile_doallocate),
-  JUMP_INIT(read, _IO_file_read),
-  JUMP_INIT(write, _IO_new_file_write),
-  JUMP_INIT(seek, _IO_file_seek),
-  JUMP_INIT(close, _IO_file_close),
-  JUMP_INIT(stat, _IO_file_stat),
-  JUMP_INIT(showmanyc, _IO_default_showmanyc),
-  JUMP_INIT(imbue, _IO_default_imbue)
-};
-libc_hidden_data_def (_IO_wfile_jumps)
-
-
-const struct _IO_jump_t _IO_wfile_jumps_mmap =
-{
-  JUMP_INIT_DUMMY,
-  JUMP_INIT(finish, _IO_new_file_finish),
-  JUMP_INIT(overflow, (_IO_overflow_t) _IO_wfile_overflow),
-  JUMP_INIT(underflow, (_IO_underflow_t) _IO_wfile_underflow_mmap),
-  JUMP_INIT(uflow, (_IO_underflow_t) _IO_wdefault_uflow),
-  JUMP_INIT(pbackfail, (_IO_pbackfail_t) _IO_wdefault_pbackfail),
-  JUMP_INIT(xsputn, _IO_wfile_xsputn),
-  JUMP_INIT(xsgetn, _IO_file_xsgetn),
-  JUMP_INIT(seekoff, _IO_wfile_seekoff),
-  JUMP_INIT(seekpos, _IO_default_seekpos),
-  JUMP_INIT(setbuf, _IO_file_setbuf_mmap),
-  JUMP_INIT(sync, (_IO_sync_t) _IO_wfile_sync),
-  JUMP_INIT(doallocate, _IO_wfile_doallocate),
-  JUMP_INIT(read, _IO_file_read),
-  JUMP_INIT(write, _IO_new_file_write),
-  JUMP_INIT(seek, _IO_file_seek),
-  JUMP_INIT(close, _IO_file_close_mmap),
-  JUMP_INIT(stat, _IO_file_stat),
-  JUMP_INIT(showmanyc, _IO_default_showmanyc),
-  JUMP_INIT(imbue, _IO_default_imbue)
-};
-
-const struct _IO_jump_t _IO_wfile_jumps_maybe_mmap =
-{
-  JUMP_INIT_DUMMY,
-  JUMP_INIT(finish, _IO_new_file_finish),
-  JUMP_INIT(overflow, (_IO_overflow_t) _IO_wfile_overflow),
-  JUMP_INIT(underflow, (_IO_underflow_t) _IO_wfile_underflow_maybe_mmap),
-  JUMP_INIT(uflow, (_IO_underflow_t) _IO_wdefault_uflow),
-  JUMP_INIT(pbackfail, (_IO_pbackfail_t) _IO_wdefault_pbackfail),
-  JUMP_INIT(xsputn, _IO_wfile_xsputn),
-  JUMP_INIT(xsgetn, _IO_file_xsgetn),
-  JUMP_INIT(seekoff, _IO_wfile_seekoff),
-  JUMP_INIT(seekpos, _IO_default_seekpos),
-  JUMP_INIT(setbuf, _IO_file_setbuf_mmap),
-  JUMP_INIT(sync, (_IO_sync_t) _IO_wfile_sync),
-  JUMP_INIT(doallocate, _IO_wfile_doallocate),
-  JUMP_INIT(read, _IO_file_read),
-  JUMP_INIT(write, _IO_new_file_write),
-  JUMP_INIT(seek, _IO_file_seek),
-  JUMP_INIT(close, _IO_file_close),
-  JUMP_INIT(stat, _IO_file_stat),
-  JUMP_INIT(showmanyc, _IO_default_showmanyc),
-  JUMP_INIT(imbue, _IO_default_imbue)
-};
diff --git a/libio/wmemstream.c b/libio/wmemstream.c
index 1bdbae9..ccdf2da 100644
--- a/libio/wmemstream.c
+++ b/libio/wmemstream.c
@@ -30,34 +30,6 @@  struct _IO_FILE_wmemstream
 };
 
 
-static int _IO_wmem_sync (_IO_FILE* fp) __THROW;
-static void _IO_wmem_finish (_IO_FILE* fp, int) __THROW;
-
-
-static const struct _IO_jump_t _IO_wmem_jumps =
-{
-  JUMP_INIT_DUMMY,
-  JUMP_INIT (finish, _IO_wmem_finish),
-  JUMP_INIT (overflow, (_IO_overflow_t) _IO_wstr_overflow),
-  JUMP_INIT (underflow, (_IO_underflow_t) _IO_wstr_underflow),
-  JUMP_INIT (uflow, (_IO_underflow_t) _IO_wdefault_uflow),
-  JUMP_INIT (pbackfail, (_IO_pbackfail_t) _IO_wstr_pbackfail),
-  JUMP_INIT (xsputn, _IO_wdefault_xsputn),
-  JUMP_INIT (xsgetn, _IO_wdefault_xsgetn),
-  JUMP_INIT (seekoff, _IO_wstr_seekoff),
-  JUMP_INIT (seekpos, _IO_default_seekpos),
-  JUMP_INIT (setbuf, _IO_default_setbuf),
-  JUMP_INIT (sync, _IO_wmem_sync),
-  JUMP_INIT (doallocate, _IO_wdefault_doallocate),
-  JUMP_INIT (read, _IO_default_read),
-  JUMP_INIT (write, _IO_default_write),
-  JUMP_INIT (seek, _IO_default_seek),
-  JUMP_INIT (close, _IO_default_close),
-  JUMP_INIT (stat, _IO_default_stat),
-  JUMP_INIT (showmanyc, _IO_default_showmanyc),
-  JUMP_INIT (imbue, _IO_default_imbue)
-};
-
 /* Open a stream that writes into a malloc'd buffer that is expanded as
    necessary.  *BUFLOC and *SIZELOC are updated with the buffer's location
    and the number of characters written on fflush or fclose.  */
@@ -87,7 +59,8 @@  open_wmemstream (wchar_t **bufloc, _IO_size_t *sizeloc)
       free (new_f);
       return NULL;
     }
-  _IO_no_init (&new_f->fp._sf._sbf._f, 0, 0, &new_f->wd, &_IO_wmem_jumps);
+  _IO_no_init (&new_f->fp._sf._sbf._f, 0, 0, &new_f->wd,
+	       &IO_vtables[IO_VTABLE_wmem]);
   _IO_fwide (&new_f->fp._sf._sbf._f, 1);
   _IO_wstr_init_static (&new_f->fp._sf._sbf._f, buf,
 			_IO_BUFSIZ / sizeof (wchar_t), buf);
@@ -101,8 +74,7 @@  open_wmemstream (wchar_t **bufloc, _IO_size_t *sizeloc)
   return (_IO_FILE *) &new_f->fp._sf._sbf;
 }
 
-
-static int
+int
 _IO_wmem_sync (_IO_FILE *fp)
 {
   struct _IO_FILE_wmemstream *mp = (struct _IO_FILE_wmemstream *) fp;
@@ -123,7 +95,7 @@  _IO_wmem_sync (_IO_FILE *fp)
 }
 
 
-static void
+void
 _IO_wmem_finish (_IO_FILE *fp, int dummy)
 {
   struct _IO_FILE_wmemstream *mp = (struct _IO_FILE_wmemstream *) fp;
diff --git a/libio/wstrops.c b/libio/wstrops.c
index 2b9d036..c3d5b32 100644
--- a/libio/wstrops.c
+++ b/libio/wstrops.c
@@ -331,27 +331,3 @@  _IO_wstr_finish (_IO_FILE *fp, int dummy)
 
   _IO_wdefault_finish (fp, 0);
 }
-
-const struct _IO_jump_t _IO_wstr_jumps =
-{
-  JUMP_INIT_DUMMY,
-  JUMP_INIT(finish, _IO_wstr_finish),
-  JUMP_INIT(overflow, (_IO_overflow_t) _IO_wstr_overflow),
-  JUMP_INIT(underflow, (_IO_underflow_t) _IO_wstr_underflow),
-  JUMP_INIT(uflow, (_IO_underflow_t) _IO_wdefault_uflow),
-  JUMP_INIT(pbackfail, (_IO_pbackfail_t) _IO_wstr_pbackfail),
-  JUMP_INIT(xsputn, _IO_wdefault_xsputn),
-  JUMP_INIT(xsgetn, _IO_wdefault_xsgetn),
-  JUMP_INIT(seekoff, _IO_wstr_seekoff),
-  JUMP_INIT(seekpos, _IO_default_seekpos),
-  JUMP_INIT(setbuf, _IO_default_setbuf),
-  JUMP_INIT(sync, _IO_default_sync),
-  JUMP_INIT(doallocate, _IO_wdefault_doallocate),
-  JUMP_INIT(read, _IO_default_read),
-  JUMP_INIT(write, _IO_default_write),
-  JUMP_INIT(seek, _IO_default_seek),
-  JUMP_INIT(close, _IO_default_close),
-  JUMP_INIT(stat, _IO_default_stat),
-  JUMP_INIT(showmanyc, _IO_default_showmanyc),
-  JUMP_INIT(imbue, _IO_default_imbue)
-};
diff --git a/stdio-common/isoc99_vsscanf.c b/stdio-common/isoc99_vsscanf.c
index 720f122..6968db9 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 (&sf._sbf) = &IO_vtables[IO_VTABLE_str];
   _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..794e770 100644
--- a/stdio-common/vfprintf.c
+++ b/stdio-common/vfprintf.c
@@ -2207,11 +2207,11 @@  struct helper_file
 #endif
   };
 
-static int
-_IO_helper_overflow (_IO_FILE *s, int c)
+#ifdef COMPILE_WPRINTF
+int
+_IO_wprintf_helper_overflow (_IO_FILE *s, int c)
 {
   _IO_FILE *target = ((struct helper_file*) s)->_put_stream;
-#ifdef COMPILE_WPRINTF
   int used = s->_wide_data->_IO_write_ptr - s->_wide_data->_IO_write_base;
   if (used)
     {
@@ -2224,7 +2224,13 @@  _IO_helper_overflow (_IO_FILE *s, int c)
 		  used - written);
       s->_wide_data->_IO_write_ptr -= written;
     }
+  return PUTC (c, s);
+}
 #else
+int
+_IO_printf_helper_overflow (_IO_FILE *s, int c)
+{
+  _IO_FILE *target = ((struct helper_file*) s)->_put_stream;
   int used = s->_IO_write_ptr - s->_IO_write_base;
   if (used)
     {
@@ -2235,54 +2241,8 @@  _IO_helper_overflow (_IO_FILE *s, int c)
 	       used - written);
       s->_IO_write_ptr -= written;
     }
-#endif
   return PUTC (c, s);
 }
-
-#ifdef COMPILE_WPRINTF
-static const struct _IO_jump_t _IO_helper_jumps =
-{
-  JUMP_INIT_DUMMY,
-  JUMP_INIT (finish, _IO_wdefault_finish),
-  JUMP_INIT (overflow, _IO_helper_overflow),
-  JUMP_INIT (underflow, _IO_default_underflow),
-  JUMP_INIT (uflow, _IO_default_uflow),
-  JUMP_INIT (pbackfail, (_IO_pbackfail_t) _IO_wdefault_pbackfail),
-  JUMP_INIT (xsputn, _IO_wdefault_xsputn),
-  JUMP_INIT (xsgetn, _IO_wdefault_xsgetn),
-  JUMP_INIT (seekoff, _IO_default_seekoff),
-  JUMP_INIT (seekpos, _IO_default_seekpos),
-  JUMP_INIT (setbuf, _IO_default_setbuf),
-  JUMP_INIT (sync, _IO_default_sync),
-  JUMP_INIT (doallocate, _IO_wdefault_doallocate),
-  JUMP_INIT (read, _IO_default_read),
-  JUMP_INIT (write, _IO_default_write),
-  JUMP_INIT (seek, _IO_default_seek),
-  JUMP_INIT (close, _IO_default_close),
-  JUMP_INIT (stat, _IO_default_stat)
-};
-#else
-static const struct _IO_jump_t _IO_helper_jumps =
-{
-  JUMP_INIT_DUMMY,
-  JUMP_INIT (finish, _IO_default_finish),
-  JUMP_INIT (overflow, _IO_helper_overflow),
-  JUMP_INIT (underflow, _IO_default_underflow),
-  JUMP_INIT (uflow, _IO_default_uflow),
-  JUMP_INIT (pbackfail, _IO_default_pbackfail),
-  JUMP_INIT (xsputn, _IO_default_xsputn),
-  JUMP_INIT (xsgetn, _IO_default_xsgetn),
-  JUMP_INIT (seekoff, _IO_default_seekoff),
-  JUMP_INIT (seekpos, _IO_default_seekpos),
-  JUMP_INIT (setbuf, _IO_default_setbuf),
-  JUMP_INIT (sync, _IO_default_sync),
-  JUMP_INIT (doallocate, _IO_default_doallocate),
-  JUMP_INIT (read, _IO_default_read),
-  JUMP_INIT (write, _IO_default_write),
-  JUMP_INIT (seek, _IO_default_seek),
-  JUMP_INIT (close, _IO_default_close),
-  JUMP_INIT (stat, _IO_default_stat)
-};
 #endif
 
 static int
@@ -2318,7 +2278,11 @@  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;
+#ifndef COMPILE_WPRINTF
+  _IO_JUMPS (&helper._f) = &IO_vtables[IO_VTABLE_printf_helper];
+#else
+  _IO_JUMPS (&helper._f) = &IO_vtables[IO_VTABLE_wprintf_helper];
+#endif
 
   /* Now print to helper instead.  */
 #ifndef COMPILE_WPRINTF
diff --git a/stdlib/strfmon_l.c b/stdlib/strfmon_l.c
index 5851a5b..7d2302e 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 (&f._sbf) = &IO_vtables[IO_VTABLE_str];
       _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.  */
diff --git a/wcsmbs/isoc99_vswscanf.c b/wcsmbs/isoc99_vswscanf.c
index b7effb0..3eb51f1 100644
--- a/wcsmbs/isoc99_vswscanf.c
+++ b/wcsmbs/isoc99_vswscanf.c
@@ -38,7 +38,8 @@  __isoc99_vswscanf (const wchar_t *string, const wchar_t *format,
 #ifdef _IO_MTSAFE_IO
   sf._sbf._f._lock = NULL;
 #endif
-  _IO_no_init (&sf._sbf._f, _IO_USER_LOCK, 0, &wd, &_IO_wstr_jumps);
+  _IO_no_init (&sf._sbf._f, _IO_USER_LOCK, 0, &wd,
+	       &IO_vtables[IO_VTABLE_wstr]);
   _IO_fwide (&sf._sbf._f, 1);
   _IO_wstr_init_static (&sf._sbf._f, (wchar_t *)string, 0, NULL);
   sf._sbf._f._flags2 |= _IO_FLAGS2_SCANF_STD;