[v5,02/11] stdio-common: Introduce buffers for implementing printf

Message ID 9e6084af5260c144509be67a906696db99dc5ddf.1670858473.git.fweimer@redhat.com
State Superseded
Headers
Series vfprintf refactor |

Commit Message

Florian Weimer Dec. 12, 2022, 3:22 p.m. UTC
  These buffers will eventually be used instead of FILE * objects
to implement printf functions.  The multibyte buffer is struct
__printf_buffer, the wide buffer is struct __wprintf_buffer.

To enable writing type-generic code, the header files
printf_buffer-char.h and printf_buffer-wchar_t.h define the
Xprintf macro differently, enabling Xprintf (buffer) to stand
for __printf_buffer and __wprintf_buffer as appropriate.  For
common cases, macros like Xprintf_buffer are provided as a more
syntactically convenient shortcut.

Buffer-specific flush callbacks are implemented with a switch
statement instead of a function pointer, to avoid hardening issues
similar to those of libio vtables.  struct __printf_buffer_as_file
is needed to support custom printf specifiers because the public
interface for that requires passing a FILE *, which is why there
is a trapdoor back from these buffers to FILE * streams.

Since the immediate user of these interfaces knows when processing
has finished, there is no flush callback for the end of processing,
only a flush callback for the intermediate buffer flush.
---
 include/printf_buffer.h               | 281 ++++++++++++++++++++++++++
 stdio-common/Makefile                 |  16 ++
 stdio-common/Xprintf_buffer_done.c    |  40 ++++
 stdio-common/Xprintf_buffer_flush.c   |  72 +++++++
 stdio-common/Xprintf_buffer_pad_1.c   |  44 ++++
 stdio-common/Xprintf_buffer_putc_1.c  |  29 +++
 stdio-common/Xprintf_buffer_puts_1.c  |  38 ++++
 stdio-common/Xprintf_buffer_write.c   |  44 ++++
 stdio-common/printf_buffer-char.h     |  24 +++
 stdio-common/printf_buffer-wchar_t.h  |  24 +++
 stdio-common/printf_buffer_as_file.c  | 148 ++++++++++++++
 stdio-common/printf_buffer_as_file.h  |  87 ++++++++
 stdio-common/printf_buffer_done.c     |  21 ++
 stdio-common/printf_buffer_flush.c    |  42 ++++
 stdio-common/printf_buffer_pad_1.c    |  21 ++
 stdio-common/printf_buffer_putc_1.c   |  21 ++
 stdio-common/printf_buffer_puts_1.c   |  21 ++
 stdio-common/printf_buffer_to_file.c  | 122 +++++++++++
 stdio-common/printf_buffer_to_file.h  |  57 ++++++
 stdio-common/printf_buffer_write.c    |  21 ++
 stdio-common/wprintf_buffer_as_file.c | 153 ++++++++++++++
 stdio-common/wprintf_buffer_done.c    |  21 ++
 stdio-common/wprintf_buffer_flush.c   |  36 ++++
 stdio-common/wprintf_buffer_pad_1.c   |  21 ++
 stdio-common/wprintf_buffer_putc_1.c  |  21 ++
 stdio-common/wprintf_buffer_puts_1.c  |  21 ++
 stdio-common/wprintf_buffer_to_file.c |  55 +++++
 stdio-common/wprintf_buffer_write.c   |  21 ++
 28 files changed, 1522 insertions(+)
 create mode 100644 include/printf_buffer.h
 create mode 100644 stdio-common/Xprintf_buffer_done.c
 create mode 100644 stdio-common/Xprintf_buffer_flush.c
 create mode 100644 stdio-common/Xprintf_buffer_pad_1.c
 create mode 100644 stdio-common/Xprintf_buffer_putc_1.c
 create mode 100644 stdio-common/Xprintf_buffer_puts_1.c
 create mode 100644 stdio-common/Xprintf_buffer_write.c
 create mode 100644 stdio-common/printf_buffer-char.h
 create mode 100644 stdio-common/printf_buffer-wchar_t.h
 create mode 100644 stdio-common/printf_buffer_as_file.c
 create mode 100644 stdio-common/printf_buffer_as_file.h
 create mode 100644 stdio-common/printf_buffer_done.c
 create mode 100644 stdio-common/printf_buffer_flush.c
 create mode 100644 stdio-common/printf_buffer_pad_1.c
 create mode 100644 stdio-common/printf_buffer_putc_1.c
 create mode 100644 stdio-common/printf_buffer_puts_1.c
 create mode 100644 stdio-common/printf_buffer_to_file.c
 create mode 100644 stdio-common/printf_buffer_to_file.h
 create mode 100644 stdio-common/printf_buffer_write.c
 create mode 100644 stdio-common/wprintf_buffer_as_file.c
 create mode 100644 stdio-common/wprintf_buffer_done.c
 create mode 100644 stdio-common/wprintf_buffer_flush.c
 create mode 100644 stdio-common/wprintf_buffer_pad_1.c
 create mode 100644 stdio-common/wprintf_buffer_putc_1.c
 create mode 100644 stdio-common/wprintf_buffer_puts_1.c
 create mode 100644 stdio-common/wprintf_buffer_to_file.c
 create mode 100644 stdio-common/wprintf_buffer_write.c
  

Comments

Adhemerval Zanella Netto Dec. 14, 2022, 2:10 p.m. UTC | #1
On 12/12/22 12:22, Florian Weimer via Libc-alpha wrote:
> These buffers will eventually be used instead of FILE * objects
> to implement printf functions.  The multibyte buffer is struct
> __printf_buffer, the wide buffer is struct __wprintf_buffer.
> 
> To enable writing type-generic code, the header files
> printf_buffer-char.h and printf_buffer-wchar_t.h define the
> Xprintf macro differently, enabling Xprintf (buffer) to stand
> for __printf_buffer and __wprintf_buffer as appropriate.  For
> common cases, macros like Xprintf_buffer are provided as a more
> syntactically convenient shortcut.
> 
> Buffer-specific flush callbacks are implemented with a switch
> statement instead of a function pointer, to avoid hardening issues
> similar to those of libio vtables.  struct __printf_buffer_as_file
> is needed to support custom printf specifiers because the public
> interface for that requires passing a FILE *, which is why there
> is a trapdoor back from these buffers to FILE * streams.
> 
> Since the immediate user of these interfaces knows when processing
> has finished, there is no flush callback for the end of processing,
> only a flush callback for the intermediate buffer flush.

Patch looks ok, some question below and just a couple of fixes regarding
the new libio vtables that should be added on tst-relro-symbols.py input
list.

Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>

> ---
>  include/printf_buffer.h               | 281 ++++++++++++++++++++++++++
>  stdio-common/Makefile                 |  16 ++
>  stdio-common/Xprintf_buffer_done.c    |  40 ++++
>  stdio-common/Xprintf_buffer_flush.c   |  72 +++++++
>  stdio-common/Xprintf_buffer_pad_1.c   |  44 ++++
>  stdio-common/Xprintf_buffer_putc_1.c  |  29 +++
>  stdio-common/Xprintf_buffer_puts_1.c  |  38 ++++
>  stdio-common/Xprintf_buffer_write.c   |  44 ++++
>  stdio-common/printf_buffer-char.h     |  24 +++
>  stdio-common/printf_buffer-wchar_t.h  |  24 +++
>  stdio-common/printf_buffer_as_file.c  | 148 ++++++++++++++
>  stdio-common/printf_buffer_as_file.h  |  87 ++++++++
>  stdio-common/printf_buffer_done.c     |  21 ++
>  stdio-common/printf_buffer_flush.c    |  42 ++++
>  stdio-common/printf_buffer_pad_1.c    |  21 ++
>  stdio-common/printf_buffer_putc_1.c   |  21 ++
>  stdio-common/printf_buffer_puts_1.c   |  21 ++
>  stdio-common/printf_buffer_to_file.c  | 122 +++++++++++
>  stdio-common/printf_buffer_to_file.h  |  57 ++++++
>  stdio-common/printf_buffer_write.c    |  21 ++
>  stdio-common/wprintf_buffer_as_file.c | 153 ++++++++++++++
>  stdio-common/wprintf_buffer_done.c    |  21 ++
>  stdio-common/wprintf_buffer_flush.c   |  36 ++++
>  stdio-common/wprintf_buffer_pad_1.c   |  21 ++
>  stdio-common/wprintf_buffer_putc_1.c  |  21 ++
>  stdio-common/wprintf_buffer_puts_1.c  |  21 ++
>  stdio-common/wprintf_buffer_to_file.c |  55 +++++
>  stdio-common/wprintf_buffer_write.c   |  21 ++
>  28 files changed, 1522 insertions(+)
>  create mode 100644 include/printf_buffer.h
>  create mode 100644 stdio-common/Xprintf_buffer_done.c
>  create mode 100644 stdio-common/Xprintf_buffer_flush.c
>  create mode 100644 stdio-common/Xprintf_buffer_pad_1.c
>  create mode 100644 stdio-common/Xprintf_buffer_putc_1.c
>  create mode 100644 stdio-common/Xprintf_buffer_puts_1.c
>  create mode 100644 stdio-common/Xprintf_buffer_write.c
>  create mode 100644 stdio-common/printf_buffer-char.h
>  create mode 100644 stdio-common/printf_buffer-wchar_t.h
>  create mode 100644 stdio-common/printf_buffer_as_file.c
>  create mode 100644 stdio-common/printf_buffer_as_file.h
>  create mode 100644 stdio-common/printf_buffer_done.c
>  create mode 100644 stdio-common/printf_buffer_flush.c
>  create mode 100644 stdio-common/printf_buffer_pad_1.c
>  create mode 100644 stdio-common/printf_buffer_putc_1.c
>  create mode 100644 stdio-common/printf_buffer_puts_1.c
>  create mode 100644 stdio-common/printf_buffer_to_file.c
>  create mode 100644 stdio-common/printf_buffer_to_file.h
>  create mode 100644 stdio-common/printf_buffer_write.c
>  create mode 100644 stdio-common/wprintf_buffer_as_file.c
>  create mode 100644 stdio-common/wprintf_buffer_done.c
>  create mode 100644 stdio-common/wprintf_buffer_flush.c
>  create mode 100644 stdio-common/wprintf_buffer_pad_1.c
>  create mode 100644 stdio-common/wprintf_buffer_putc_1.c
>  create mode 100644 stdio-common/wprintf_buffer_puts_1.c
>  create mode 100644 stdio-common/wprintf_buffer_to_file.c
>  create mode 100644 stdio-common/wprintf_buffer_write.c
> 
> diff --git a/include/printf_buffer.h b/include/printf_buffer.h
> new file mode 100644
> index 0000000000..ad08a72b71
> --- /dev/null
> +++ b/include/printf_buffer.h
> @@ -0,0 +1,281 @@
> +/* Multibyte and wide buffers for implementing printf-related functions.
> +   Copyright (C) 2022 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
> +   <https://www.gnu.org/licenses/>.  */
> +
> +/* The naming of the multibyte and wide variants is intentionally
> +   consistent, so that it is possible to use the Xprintf macro in
> +   stdio-common/printf_buffer-char.h and
> +   stdio-common/printf_buffer-wchar_t.h to select between them in
> +   type-generic code.  */
> +
> +#ifndef PRINTF_BUFFER_H
> +#define PRINTF_BUFFER_H
> +
> +#include <stdbool.h>
> +#include <stdint.h>
> +#include <wchar.h>
> +
> +/* <printf_buffer_as_file.h> introduces a way to use struct
> +   __printf_buffer objects from FILE * streams.  To avoid storing a
> +   function pointer (or vtable pointer) in struct __printf_buffer
> +   (which would defeat libio vtable hardening), a switch statement
> +   over the different flush implementations is used to implement
> +   __printf_buffer_flush.
> +
> +   __printf_buffer_mode_failed is special: it is the sticky failure
> +   indicator.  Unlike struct alloc_buffer, this is not folded into
> +   write_ptr, so that snprintf and other string-writing functions can
> +   discover the end of the string even in the error case, to be able
> +   to add the null terminator.  */
> +enum __printf_buffer_mode
> +  {
> +    __printf_buffer_mode_failed,
> +    __printf_buffer_mode_to_file,
> +  };
> +
> +/* Buffer for fast character writing with overflow handling.
> +   Typically embedded in another struct with further data that is used
> +   by the flush function.  */
> +struct __printf_buffer
> +{
> +  /* These pointer members follow FILE streams.  write_ptr and
> +     write_end must be initialized to cover the target buffer.  See
> +     __printf_buffer_init.
> +
> +     Data can be written directly to *write_ptr while write_ptr !=
> +     write_end, and write_ptr can be advanced accordingly.  Note that
> +     is not possible to use the apparently-unused part of the buffer
> +     as scratch space because sprintf (and snprintf, but that is a bit
> +     iffy) must only write the minimum number of characters produced
> +     by the format string and its arguments.
> +
> +     write_base must be initialized to be equal to write_ptr.  The
> +     framework uses this pointer to compute the total number of
> +     written bytes, together with the written field.  See
> +     __printf_buffer_done.
> +
> +     write_base and write_end are only read by the generic functions
> +     after initialization, only the flush implementation called from
> +     __printf_buffer_flush might change these pointers.  See the
> +     comment on Xprintf (buffer_do_flush) in Xprintf_buffer_flush.c
> +     for details regarding the flush operation.  */
> +  char *write_base;
> +  char *write_ptr;
> +  char *write_end;
> +
> +  /* Number of characters written so far (excluding the current
> +     buffer).  Potentially updated on flush.  The actual number of
> +     written bytes also includes the unflushed-but-written buffer
> +     part, write_ptr - write_base.  A 64-bit value is used to avoid
> +     the need for overflow checks.  */
> +  uint64_t written;
> +
> +  /* Identifies the flush callback.  */
> +  enum __printf_buffer_mode mode;
> +};
> +

Ok.

> +/* Marks the buffer as failed, so that __printf_buffer_has_failed
> +   returns true and future flush operations are no-ops.  */
> +static inline void
> +__printf_buffer_mark_failed (struct __printf_buffer *buf)
> +{
> +  buf->mode = __printf_buffer_mode_failed;
> +}
> +
> +/* Returns true if the sticky error indicator of the buffer has been
> +   set to failed.  */
> +static inline bool __attribute_warn_unused_result__
> +__printf_buffer_has_failed (struct __printf_buffer *buf)
> +{
> +  return buf->mode == __printf_buffer_mode_failed;
> +}
> +
> +/* Initialization of a buffer, using the memory region from [BASE, BASE +LEN)
> +   as the initial buffer contents.  LEN can be zero.  */
> +static inline void
> +__printf_buffer_init (struct __printf_buffer *buf, char *base, size_t len,
> +                      enum __printf_buffer_mode mode)
> +{
> +  buf->write_base = base;
> +  buf->write_ptr = base;
> +  buf->write_end = base + len;
> +  buf->written = 0;
> +  buf->mode = mode;
> +}
> +
> +/* Called by printf_buffer_putc for a full buffer.  */
> +void __printf_buffer_putc_1 (struct __printf_buffer *buf, char ch)
> +  attribute_hidden;
> +
> +/* Writes CH to BUF.  */
> +static inline void
> +__printf_buffer_putc (struct __printf_buffer *buf, char ch)
> +{
> +  if (buf->write_ptr != buf->write_end)
> +      *buf->write_ptr++ = ch;
> +  else
> +    __printf_buffer_putc_1 (buf, ch);
> +}
> +
> +/* Writes COUNT repeats of CH to BUF.  */
> +void __printf_buffer_pad_1 (struct __printf_buffer *buf,
> +                            char ch, size_t count) attribute_hidden;
> +
> +/* __printf_buffer_pad with fast path for no padding.  COUNT is a
> +   long int, to accomodate signed uses in printf and elsewhere.  */

Wouldn't ssize works better here than long int?

> +static inline void
> +__printf_buffer_pad (struct __printf_buffer *buf, char ch, long int count)
> +{
> +  if (count > 0)
> +    __printf_buffer_pad_1 (buf, ch, count);
> +}
> +
> +/* Write COUNT bytes starting at S to BUF.  S must not overlap with
> +   the internal buffer.  */
> +void __printf_buffer_write (struct __printf_buffer *buf, const char *s,
> +                            size_t count) attribute_hidden;
> +
> +/* Write S to BUF.  S must not overlap with the internal buffer.  */
> +void __printf_buffer_puts_1 (struct __printf_buffer *buf, const char *s)
> +  attribute_hidden;

Maybe a space here?

> +static inline void
> +__printf_buffer_puts (struct __printf_buffer *buf, const char *s)
> +{
> +  if (__builtin_constant_p (__builtin_strlen (s)))
> +    __printf_buffer_write (buf, s, __builtin_strlen (s));
> +  else
> +    __printf_buffer_puts_1 (buf, s);
> +}
> +
> +/* Returns the number of bytes written through the buffer, or -1 if
> +   there was an error (that is, __printf_buffer_has_failed (BUF) is true).
> +
> +   The number of written bytes includes pending bytes in the buffer
> +   (between BUF->write_base and BUF->write_ptr).
> +
> +   If the number is larger than INT_MAX, returns -1 and sets errno to
> +   EOVERFLOW.  This function does not flush the buffer.  If the caller
> +   needs the side effect of flushing, it has to do this
> +   separately.  */
> +int __printf_buffer_done (struct __printf_buffer *buf) attribute_hidden;

Wouldn't ssize_t work better for return type?

> +
> +/* Internally used to call the flush function.  This can be called
> +   explicitly for certain modes to flush the buffer prematuraly.  In
> +   such cases, it is often the case that the buffer mode is statically
> +   known, and the flush implementation can be called directly.  */
> +bool __printf_buffer_flush (struct __printf_buffer *buf) attribute_hidden;
> +
> +/* Wide version of struct __printf_buffer follows.  */
> +
> +enum __wprintf_buffer_mode
> +  {
> +    __wprintf_buffer_mode_failed,
> +    __wprintf_buffer_mode_to_file,
> +  };
> +
> +struct __wprintf_buffer
> +{
> +  wchar_t *write_base;
> +  wchar_t *write_ptr;
> +  wchar_t *write_end;
> +  uint64_t written;
> +  enum __wprintf_buffer_mode mode;
> +};
> +
> +static inline void
> +__wprintf_buffer_mark_failed (struct __wprintf_buffer *buf)
> +{
> +  buf->mode = __wprintf_buffer_mode_failed;
> +}
> +
> +static inline bool __attribute_warn_unused_result__
> +__wprintf_buffer_has_failed (struct __wprintf_buffer *buf)
> +{
> +  return buf->mode == __wprintf_buffer_mode_failed;
> +}
> +
> +static inline void
> +__wprintf_buffer_init (struct __wprintf_buffer *buf,
> +                       wchar_t *base, size_t len,
> +                       enum __wprintf_buffer_mode mode)
> +{
> +  buf->write_base = base;
> +  buf->write_ptr = base;
> +  buf->write_end = base + len;
> +  buf->written = 0;
> +  buf->mode = mode;
> +}
> +
> +void __wprintf_buffer_putc_1 (struct __wprintf_buffer *buf, wchar_t ch)
> +  attribute_hidden;
> +
> +static inline void
> +__wprintf_buffer_putc (struct __wprintf_buffer *buf, wchar_t ch)
> +{
> +  if (buf->write_ptr != buf->write_end)
> +      *buf->write_ptr++ = ch;
> +  else
> +    __wprintf_buffer_putc_1 (buf, ch);
> +}
> +
> +void __wprintf_buffer_pad_1 (struct __wprintf_buffer *buf,
> +                             wchar_t ch, size_t count) attribute_hidden;
> +
> +static inline void
> +__wprintf_buffer_pad (struct __wprintf_buffer *buf, char ch, long int count)
> +{
> +  if (count > 0)
> +    __wprintf_buffer_pad_1 (buf, ch, count);
> +}
> +
> +void __wprintf_buffer_write (struct __wprintf_buffer *buf, const wchar_t *s,
> +                             size_t count) attribute_hidden;
> +
> +void __wprintf_buffer_puts (struct __wprintf_buffer *buf, const wchar_t *s)
> +  attribute_hidden;
> +
> +int __wprintf_buffer_done (struct __wprintf_buffer *buf) attribute_hidden;
> +
> +bool __wprintf_buffer_flush (struct __wprintf_buffer *buf) attribute_hidden;
> +
> +/* Type-generic convenience macros.  They are useful if
> +   printf_buffer-char.h or printf_buffer-wchar_t.h is included as
> +   well.  */
> +
> +#define Xprintf_buffer Xprintf (buffer)
> +#define Xprintf_buffer_done Xprintf (buffer_done)
> +#define Xprintf_buffer_flush Xprintf (buffer_flush)
> +#define Xprintf_buffer_has_failed Xprintf (buffer_has_failed)
> +#define Xprintf_buffer_mark_failed Xprintf (buffer_mark_failed)
> +#define Xprintf_buffer_pad Xprintf (buffer_pad)
> +#define Xprintf_buffer_putc Xprintf (buffer_putc)
> +#define Xprintf_buffer_puts Xprintf (buffer_puts)
> +#define Xprintf_buffer_write Xprintf (buffer_write)
> +
> +/* Flush function implementations follow.  They are called from
> +   __printf_buffer_flush.  Generic code should not call these flush
> +   functions directly.  Some modes have inline implementations.  */
> +
> +struct __printf_buffer_to_file;
> +void __printf_buffer_flush_to_file (struct __printf_buffer_to_file *)
> +  attribute_hidden;
> +
> +struct __wprintf_buffer_to_file;
> +void __wprintf_buffer_flush_to_file (struct __wprintf_buffer_to_file *)
> +  attribute_hidden;
> +
> +#endif /* PRINTF_BUFFER_H */

Ok.

> diff --git a/stdio-common/Makefile b/stdio-common/Makefile
> index 8f2524959d..120d66ea93 100644
> --- a/stdio-common/Makefile
> +++ b/stdio-common/Makefile
> @@ -53,6 +53,14 @@ routines := \
>    perror \
>    printf \
>    printf-prs \
> +  printf_buffer_as_file \
> +  printf_buffer_done \
> +  printf_buffer_flush \
> +  printf_buffer_pad_1 \
> +  printf_buffer_putc_1 \
> +  printf_buffer_puts_1 \
> +  printf_buffer_to_file \
> +  printf_buffer_write \
>    printf_fp \
>    printf_fphex \
>    printf_size \
> @@ -85,6 +93,14 @@ routines := \
>    vfwscanf \
>    vfwscanf-internal \
>    vprintf \
> +  wprintf_buffer_as_file \
> +  wprintf_buffer_done \
> +  wprintf_buffer_flush \
> +  wprintf_buffer_pad_1 \
> +  wprintf_buffer_putc_1 \
> +  wprintf_buffer_puts_1 \
> +  wprintf_buffer_to_file \
> +  wprintf_buffer_write \
>    # routines
>  
>  aux := \
> diff --git a/stdio-common/Xprintf_buffer_done.c b/stdio-common/Xprintf_buffer_done.c
> new file mode 100644
> index 0000000000..a3f51c43ef
> --- /dev/null
> +++ b/stdio-common/Xprintf_buffer_done.c
> @@ -0,0 +1,40 @@
> +/* Final status reporting for struct __*printf_buffer.  Generic version.
> +   Copyright (C) 2022 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
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#include <errno.h>
> +#include <intprops.h>
> +#include <stdint.h>
> +
> +int
> +Xprintf_buffer_done (struct Xprintf_buffer *buf)
> +{
> +  if (Xprintf_buffer_has_failed (buf))
> +    return -1;
> +
> +  /* Use uintptr_t here because for sprintf, the buffer range may
> +     cover more than half of the address space.  */
> +  uintptr_t written_current = buf->write_ptr - buf->write_base;

Is this really a valid case?  Working with buffer ranges larger than half
of address space is not valid for malloc buffer anyway, are you handling
some iffy case for mmap case?

> +  int written_total;
> +  if (INT_ADD_WRAPV (buf->written, written_current, &written_total))
> +    {
> +      __set_errno (EOVERFLOW);
> +      return -1;
> +    }
> +  else
> +    return written_total;
> +}
> diff --git a/stdio-common/Xprintf_buffer_flush.c b/stdio-common/Xprintf_buffer_flush.c
> new file mode 100644
> index 0000000000..1368cfe684
> --- /dev/null
> +++ b/stdio-common/Xprintf_buffer_flush.c
> @@ -0,0 +1,72 @@
> +/* Flush wrapper for struct __*printf_buffer.  Generic version.
> +   Copyright (C) 2022 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
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#include <printf_buffer.h>
> +
> +#include <assert.h>
> +#include <stdint.h>
> +
> +/* Xprintf (buffer_do_flush) (BUF) performs the flush operation.  The
> +   actual implementation is specific to the multibyte and wide
> +   variants.
> +
> +   If the flush fails, Xprintf_buffer_mark_failed (BUF) must be
> +   called, and BUF->write_ptr and BUF->write_end can be left
> +   unchanged.
> +
> +   The function must not do anything if failure has already occurred,
> +   that is, if BUF->mode == Xprintf (buffer_mode_failed).
> +
> +   The framework implicitly invokes flush with BUF->write_ptr ==
> +   BUF->write_end only.  (This is particularly relevant to the
> +   __sprintf_chk flush, which just calls __chk_fail.)  But in some
> +   cases, Xprintf_buffer_flush may be called explicitly (when
> +   BUF->mode/the backing function is known).  In that case, it is
> +   possible that BUF->write_ptr < BUF->write_end is true.
> +
> +   If the flush succeeds, the pointers are changed so that
> +   BUF->write_ptr < BUF->write_end.  It is possible to switch to a
> +   completely different buffer here.  If the buffer is moved, it may
> +   be necessary to updated BUF->write_base and BUF->written from the
> +   flush function as well.
> +
> +   Note that when chaining buffers, in the flush function for the
> +   outer buffer (to which data is written first), it is necessary to
> +   check for BUF->next->failed (for the inner buffer) and set
> +   BUF->base.failed to true (for the outer buffer).  This should come
> +   towards the end of the outer flush function.  Usually, there is
> +   also some unwrapping step afterwards; it has to check the outer
> +   buffer (BUF->base.failed) and propagate any error to the inner
> +   buffer (BUF->next->failed), so essentially in the other
> +   direction.  */
> +static void Xprintf (buffer_do_flush) (struct Xprintf_buffer *buf);
> +
> +bool
> +Xprintf_buffer_flush (struct Xprintf_buffer *buf)
> +{
> +  if (__glibc_unlikely (Xprintf_buffer_has_failed (buf)))
> +    return false;
> +
> +  Xprintf (buffer_do_flush) (buf);
> +  if (Xprintf_buffer_has_failed (buf))
> +    return false;
> +
> +  /* Ensure that the flush has made available some bytes.  */
> +  assert (buf->write_ptr != buf->write_end);
> +  return true;
> +}

Ok.

> diff --git a/stdio-common/Xprintf_buffer_pad_1.c b/stdio-common/Xprintf_buffer_pad_1.c
> new file mode 100644
> index 0000000000..2be8270ad2
> --- /dev/null
> +++ b/stdio-common/Xprintf_buffer_pad_1.c
> @@ -0,0 +1,44 @@
> +/* Write repeated characters to struct __*printf_buffer.  Generic version.
> +   Copyright (C) 2022 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
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#include <assert.h>
> +#include <string.h>
> +
> +void
> +Xprintf (buffer_pad_1) (struct Xprintf_buffer *buf, CHAR_T ch, size_t count)
> +{
> +  if (__glibc_unlikely (Xprintf_buffer_has_failed (buf)))
> +    return;
> +
> +  do
> +    {
> +      /* Proactively make room.  __*printf_buffer_pad has already
> +         checked for a zero-length write, so this function is only
> +         called when there is actually data to write.  */
> +      if (buf->write_ptr == buf->write_end && !Xprintf_buffer_flush (buf))
> +        return;
> +      assert (buf->write_ptr != buf->write_end);
> +      size_t to_fill = buf->write_end - buf->write_ptr;
> +      if (to_fill > count)
> +        to_fill = count;
> +      MEMSET (buf->write_ptr, ch, to_fill);
> +      buf->write_ptr += to_fill;
> +      count -= to_fill;
> +    }
> +  while (count > 0);
> +}

Ok.

> diff --git a/stdio-common/Xprintf_buffer_putc_1.c b/stdio-common/Xprintf_buffer_putc_1.c
> new file mode 100644
> index 0000000000..3458775a1c
> --- /dev/null
> +++ b/stdio-common/Xprintf_buffer_putc_1.c
> @@ -0,0 +1,29 @@
> +/* Overflow write function for struct __*printf_buffer.  Generic version.
> +   Copyright (C) 2022 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
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#include <assert.h>
> +
> +void
> +Xprintf (buffer_putc_1) (struct Xprintf_buffer *buf, CHAR_T ch)
> +{
> +  if (__glibc_unlikely (Xprintf_buffer_has_failed (buf))
> +      || !Xprintf_buffer_flush (buf))
> +    return;
> +  assert (buf->write_ptr < buf->write_end);
> +  *buf->write_ptr++ = ch;
> +}

Ok.

> diff --git a/stdio-common/Xprintf_buffer_puts_1.c b/stdio-common/Xprintf_buffer_puts_1.c
> new file mode 100644
> index 0000000000..88d0b8b2bd
> --- /dev/null
> +++ b/stdio-common/Xprintf_buffer_puts_1.c
> @@ -0,0 +1,38 @@
> +/* String write function for struct __*printf_buffer.  Generic version.
> +   Copyright (C) 2022 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
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#include <assert.h>
> +#include <string.h>
> +
> +void
> +Xprintf (buffer_puts_1) (struct Xprintf_buffer *buf, const CHAR_T *s)
> +{
> +  if (__glibc_unlikely (Xprintf_buffer_has_failed (buf)))
> +    return;
> +
> +  while (*s != 0)

Maybe use NULL here?

> +    {
> +      if (buf->write_ptr == buf->write_end && !Xprintf_buffer_flush (buf))
> +        return;
> +      assert (buf->write_ptr != buf->write_end);
> +      size_t to_copy = STRNLEN (s, buf->write_end - buf->write_ptr);
> +      MEMCPY (buf->write_ptr, s, to_copy);
> +      buf->write_ptr += to_copy;
> +      s += to_copy;
> +    }
> +}

Ok.

> diff --git a/stdio-common/Xprintf_buffer_write.c b/stdio-common/Xprintf_buffer_write.c
> new file mode 100644
> index 0000000000..9db2b5066a
> --- /dev/null
> +++ b/stdio-common/Xprintf_buffer_write.c
> @@ -0,0 +1,44 @@
> +/* Blob write function for struct __*printf_buffer.  Generic version.
> +   Copyright (C) 2022 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
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#include <printf_buffer.h>
> +
> +#include <assert.h>
> +#include <string.h>
> +
> +void
> +Xprintf_buffer_write (struct Xprintf_buffer *buf,
> +                        const CHAR_T *s, size_t count)
> +{
> +  if (__glibc_unlikely (Xprintf_buffer_has_failed (buf)))
> +    return;
> +
> +  while (count > 0)
> +    {
> +      if (buf->write_ptr == buf->write_end && !Xprintf_buffer_flush (buf))
> +        return;
> +      assert (buf->write_ptr != buf->write_end);
> +      size_t to_copy = buf->write_end - buf->write_ptr;
> +      if (to_copy > count)
> +        to_copy = count;
> +      MEMCPY (buf->write_ptr, s, to_copy);
> +      buf->write_ptr += to_copy;
> +      s += to_copy;
> +      count -= to_copy;
> +    }
> +}

Ok.

> diff --git a/stdio-common/printf_buffer-char.h b/stdio-common/printf_buffer-char.h
> new file mode 100644
> index 0000000000..44264e24c0
> --- /dev/null
> +++ b/stdio-common/printf_buffer-char.h
> @@ -0,0 +1,24 @@
> +/* Macros for the multibyte (char) implementation of struct __printf_buffer.
> +   Copyright (C) 2022 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
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#define Xprintf(n) __printf_##n
> +
> +#define CHAR_T char
> +#define MEMCPY memcpy
> +#define MEMSET memset
> +#define STRNLEN __strnlen

Ok.

> diff --git a/stdio-common/printf_buffer-wchar_t.h b/stdio-common/printf_buffer-wchar_t.h
> new file mode 100644
> index 0000000000..d8d0c5da46
> --- /dev/null
> +++ b/stdio-common/printf_buffer-wchar_t.h
> @@ -0,0 +1,24 @@
> +/* Macros for wide (wchar_t) implementation of struct __wprintf_buffer.
> +   Copyright (C) 2022 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
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#define Xprintf(n) __wprintf_##n
> +
> +#define CHAR_T wchar_t
> +#define MEMCPY __wmemcpy
> +#define MEMSET __wmemset
> +#define STRNLEN __wcsnlen

Ok.

> diff --git a/stdio-common/printf_buffer_as_file.c b/stdio-common/printf_buffer_as_file.c
> new file mode 100644
> index 0000000000..f27b000d78
> --- /dev/null
> +++ b/stdio-common/printf_buffer_as_file.c
> @@ -0,0 +1,148 @@
> +/* FILE * interface to a struct __printf_buffer.  Multibyte version.
> +   Copyright (C) 2022 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
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#include <printf_buffer_as_file.h>
> +
> +#include <assert.h>
> +#include <printf_buffer.h>
> +
> +/* Commit the data directly written through the stdio stream.  */
> +static void
> +__printf_buffer_as_file_commit (struct __printf_buffer_as_file *file)
> +{
> +  /* Check that the write pointers in the file stream are consistent
> +     with the next buffer.  */
> +  assert (file->stream._IO_write_ptr >= file->next->write_ptr);
> +  assert (file->stream._IO_write_ptr <= file->next->write_end);
> +  assert (file->stream._IO_write_base == file->next->write_base);
> +  assert (file->stream._IO_write_end == file->next->write_end);
> +
> +  file->next->write_ptr = file->stream._IO_write_ptr;
> +}
> +
> +/* Pointer the FILE * write buffer into the active printf_buffer
> +   area.  */
> +static void
> +__printf_buffer_as_file_switch_to_buffer (struct __printf_buffer_as_file *file)
> +{
> +  file->stream._IO_write_base = file->next->write_base;
> +  file->stream._IO_write_ptr = file->next->write_ptr;
> +  file->stream._IO_write_end = file->next->write_end;
> +}
> +
> +/* Only a small subset of the vtable functions is implemented here,
> +   following _IO_obstack_jumps.  */
> +
> +static int
> +__printf_buffer_as_file_overflow (FILE *fp, int ch)
> +{
> +  struct __printf_buffer_as_file *file = (struct __printf_buffer_as_file *) fp;
> +
> +  __printf_buffer_as_file_commit (file);
> +
> +  /* EOF means only a flush is requested.   */
> +  if (ch != EOF)
> +    __printf_buffer_putc (file->next, ch);
> +
> +  /* Ensure that flushing actually produces room.  */
> +  if (!__printf_buffer_has_failed (file->next)
> +      && file->next->write_ptr == file->next->write_end)
> +    __printf_buffer_flush (file->next);
> +
> +  __printf_buffer_as_file_switch_to_buffer (file);
> +
> +  if (!__printf_buffer_has_failed (file->next))
> +    return (unsigned char) ch;
> +  else
> +    return EOF;
> +}
> +
> +static size_t
> +__printf_buffer_as_file_xsputn (FILE *fp, const void *buf, size_t len)
> +{
> +  struct __printf_buffer_as_file *file = (struct __printf_buffer_as_file *) fp;
> +
> +  __printf_buffer_as_file_commit (file);
> +
> +  /* Copy the data.  */
> +  __printf_buffer_write (file->next, buf, len);
> +
> +  __printf_buffer_as_file_switch_to_buffer (file);
> +
> +  if (!__printf_buffer_has_failed (file->next))
> +    return len;
> +  else
> +    /* We may actually have written something.  But the stream is
> +       corrupted in this case anyway, so try not to divine the write
> +       count here.  */
> +    return 0;
> +}
> +
> +static const struct _IO_jump_t _IO_printf_buffer_as_file_jumps libio_vtable =
> +{
> +  JUMP_INIT_DUMMY,
> +  JUMP_INIT(finish, NULL),
> +  JUMP_INIT(overflow, __printf_buffer_as_file_overflow),
> +  JUMP_INIT(underflow, NULL),
> +  JUMP_INIT(uflow, NULL),
> +  JUMP_INIT(pbackfail, NULL),
> +  JUMP_INIT(xsputn, __printf_buffer_as_file_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)
> +};

The _IO_printf_buffer_as_file_jumps should be added on tst-relro-symbols.py required list.

> +
> +void
> +__printf_buffer_as_file_init (struct __printf_buffer_as_file *file,
> +                              struct __printf_buffer *next)
> +{
> +  file->stream._lock = NULL;
> +  _IO_no_init (&file->stream, _IO_USER_LOCK, -1, NULL, NULL);
> +  file->vtable = &_IO_printf_buffer_as_file_jumps;
> +
> +  /* Set up the write buffer from the next buffer.  */
> +  file->next = next;
> +  __printf_buffer_as_file_switch_to_buffer (file);
> +
> +  /* Mark the read area as inactive, by making all pointers equal.  */
> +  file->stream._IO_read_base = file->stream._IO_write_base;
> +  file->stream._IO_read_ptr = file->stream._IO_write_base;
> +  file->stream._IO_read_end = file->stream._IO_write_base;
> +}
> +
> +bool
> +__printf_buffer_as_file_terminate (struct __printf_buffer_as_file *file)
> +{
> +  if (file->stream._flags & _IO_ERR_SEEN)
> +    return false;
> +  else
> +    {
> +      __printf_buffer_as_file_commit (file);
> +      return true;
> +    }
> +}

Ok.

> diff --git a/stdio-common/printf_buffer_as_file.h b/stdio-common/printf_buffer_as_file.h
> new file mode 100644
> index 0000000000..8ac707f2d0
> --- /dev/null
> +++ b/stdio-common/printf_buffer_as_file.h
> @@ -0,0 +1,87 @@
> +/* FILE * interface to a struct __*printf_buffer.
> +   Copyright (C) 2022 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
> +   <https://www.gnu.org/licenses/>.  */
> +
> +/* Registered printf format specifier callbacks produce data via a
> +   FILE * stream.  struct __printf_buffer_as_file enables vfprintf to
> +   create a suitable stdio stream.  Likewise struct
> +   __wprintf_buffer_as_file for vfwprintf.  */
> +
> +#ifndef PRINTF_BUFFER_AS_FILE_H
> +#define PRINTF_BUFFER_AS_FILE_H
> +
> +#include <libio/libioP.h>
> +
> +struct __printf_buffer;
> +
> +struct __printf_buffer_as_file
> +{
> +  /* Interface to libio.  */
> +  FILE stream;
> +  const struct _IO_jump_t *vtable;
> +
> +  /* Pointer to the underlying buffer.  */
> +  struct __printf_buffer *next;
> +};
> +
> +/* Initialization *FP so that data written to its FILE * stream ends
> +   up in NEXT.  */
> +void __printf_buffer_as_file_init (struct __printf_buffer_as_file *fp,
> +                                   struct __printf_buffer *next)
> +  attribute_hidden;
> +
> +/* Returns the FILE * that can be used to write data to the
> +   buffer.  */
> +static inline FILE *
> +__printf_buffer_as_file_get (struct __printf_buffer_as_file *file)
> +{
> +  return &file->stream;
> +}
> +
> +/* Transfers all pending data from the FILE * to the underlying
> +   buffer.  Returns true if there have been no errors.  */
> +bool __printf_buffer_as_file_terminate (struct __printf_buffer_as_file *)
> +  attribute_hidden;
> +
> +/* Wide variant follows.  */
> +
> +struct __wprintf_buffer;
> +struct __wprintf_buffer_as_file
> +{
> +  /* Interface to libio.  */
> +  FILE stream;
> +  const struct _IO_jump_t *vtable;
> +  struct _IO_wide_data wide_stream;
> +
> +  /* Pointer to the underlying buffer.  */
> +  struct __wprintf_buffer *next;
> +};
> +
> +void __wprintf_buffer_as_file_init (struct __wprintf_buffer_as_file *fp,
> +                                    struct __wprintf_buffer *next)
> +  attribute_hidden;
> +
> +static inline FILE *
> +__wprintf_buffer_as_file_get (struct __wprintf_buffer_as_file *file)
> +{
> +  return &file->stream;
> +}
> +
> +bool __wprintf_buffer_as_file_terminate (struct __wprintf_buffer_as_file *)
> +  attribute_hidden;
> +
> +#endif /* PRINTF_BUFFER_AS_FILE_H */

Ok.

> diff --git a/stdio-common/printf_buffer_done.c b/stdio-common/printf_buffer_done.c
> new file mode 100644
> index 0000000000..5f3df1daad
> --- /dev/null
> +++ b/stdio-common/printf_buffer_done.c
> @@ -0,0 +1,21 @@
> +/* Final status reporting for struct __printf_buffer.  Multibyte version.
> +   Copyright (C) 2022 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
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#include <printf_buffer.h>
> +#include "printf_buffer-char.h"
> +#include "Xprintf_buffer_done.c"

Ok.

> diff --git a/stdio-common/printf_buffer_flush.c b/stdio-common/printf_buffer_flush.c
> new file mode 100644
> index 0000000000..9b25c0fde5
> --- /dev/null
> +++ b/stdio-common/printf_buffer_flush.c
> @@ -0,0 +1,42 @@
> +/* Flush a struct __printf_buffer.  Multibyte version.
> +   Copyright (C) 2022 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
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#include <printf_buffer.h>
> +
> +#include "printf_buffer-char.h"
> +#include "Xprintf_buffer_flush.c"
> +
> +/* The __printf_buffer_flush_* functions are defined together with
> +   functions that are pulled in by strong references.  */
> +#ifndef SHARED
> +# pragma weak __printf_buffer_flush_to_file
> +#endif /* !SHARED */
> +
> +static void
> +__printf_buffer_do_flush (struct __printf_buffer *buf)
> +{
> +  switch (buf->mode)
> +    {
> +    case __printf_buffer_mode_failed:
> +      return;
> +    case __printf_buffer_mode_to_file:
> +      __printf_buffer_flush_to_file ((struct __printf_buffer_to_file *) buf);
> +      return;
> +    }
> +  __builtin_trap ();
> +}

Ok.

> diff --git a/stdio-common/printf_buffer_pad_1.c b/stdio-common/printf_buffer_pad_1.c
> new file mode 100644
> index 0000000000..c4b9a7af56
> --- /dev/null
> +++ b/stdio-common/printf_buffer_pad_1.c
> @@ -0,0 +1,21 @@
> +/* Write repeated characters to struct __printf_buffer.  Multibyte version.
> +   Copyright (C) 2022 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
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#include <printf_buffer.h>
> +#include "printf_buffer-char.h"
> +#include "Xprintf_buffer_pad_1.c"

Ok.

> diff --git a/stdio-common/printf_buffer_putc_1.c b/stdio-common/printf_buffer_putc_1.c
> new file mode 100644
> index 0000000000..a15e8d16d1
> --- /dev/null
> +++ b/stdio-common/printf_buffer_putc_1.c
> @@ -0,0 +1,21 @@
> +/* Overflow character write function for struct __printf_buffer.
> +   Copyright (C) 2022 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
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#include <printf_buffer.h>
> +#include "printf_buffer-char.h"
> +#include "Xprintf_buffer_putc_1.c"

Ok.

> diff --git a/stdio-common/printf_buffer_puts_1.c b/stdio-common/printf_buffer_puts_1.c
> new file mode 100644
> index 0000000000..ed97fd6b3f
> --- /dev/null
> +++ b/stdio-common/printf_buffer_puts_1.c
> @@ -0,0 +1,21 @@
> +/* String write function for struct __printf_buffer.  Multibyte version.
> +   Copyright (C) 2022 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
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#include <printf_buffer.h>
> +#include "printf_buffer-char.h"
> +#include "Xprintf_buffer_puts_1.c"

Ok.

> diff --git a/stdio-common/printf_buffer_to_file.c b/stdio-common/printf_buffer_to_file.c
> new file mode 100644
> index 0000000000..3576771f70
> --- /dev/null
> +++ b/stdio-common/printf_buffer_to_file.c
> @@ -0,0 +1,122 @@
> +/* Multibyte printf buffers writing data to a FILE * stream.
> +   Copyright (C) 2022 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
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#include <printf_buffer_to_file.h>
> +
> +#include <assert.h>
> +#include <array_length.h>
> +#include <libio/libioP.h>
> +
> +/* Switch to the file buffer if possible.  If the file has write_ptr
> +   == write_end, use the stage buffer instead.  */
> +void
> +__printf_buffer_to_file_switch (struct __printf_buffer_to_file *buf)
> +{
> +  if (buf->fp->_IO_write_ptr < buf->fp->_IO_write_end)
> +    {
> +      /* buf->fp has a buffer associated with it, so write directly to
> +         it from now on.  */
> +      buf->base.write_ptr = buf->fp->_IO_write_ptr;
> +      buf->base.write_end = buf->fp->_IO_write_end;
> +    }
> +  else
> +    {
> +      /* Use the staging area if no buffer is available in buf->fp.  */
> +      buf->base.write_ptr = buf->stage;
> +      buf->base.write_end = array_end (buf->stage);
> +    }
> +
> +  buf->base.write_base = buf->base.write_ptr;
> +}
> +
> +void
> +__printf_buffer_flush_to_file (struct __printf_buffer_to_file *buf)
> +{
> +  /* The bytes in the buffer are always consumed.  */
> +  buf->base.written += buf->base.write_ptr - buf->base.write_base;
> +
> +  if (buf->base.write_end == array_end (buf->stage))
> +    {
> +      /* If the stage buffer is used, make a copy into the file.  The
> +         stage buffer is always consumed fully, even if just partially
> +         written, to ensure that the file stream has all the data.  */
> +      size_t count = buf->base.write_ptr - buf->stage;
> +      if ((size_t) _IO_sputn (buf->fp, buf->stage, count) != count)
> +        {
> +          __printf_buffer_mark_failed (&buf->base);
> +          return;
> +        }
> +      /* buf->fp may have a buffer now.  */
> +      __printf_buffer_to_file_switch (buf);
> +      return;
> +    }
> +  else if (buf->base.write_end == buf->stage + 1)
> +    {
> +      /* Special one-character buffer case.  This is used to avoid
> +         flush-only overflow below.  */
> +      if (buf->base.write_ptr == buf->base.write_end)
> +        {
> +          if (__overflow (buf->fp, (unsigned char) *buf->stage) == EOF)
> +            {
> +              __printf_buffer_mark_failed (&buf->base);
> +              return;
> +            }
> +          __printf_buffer_to_file_switch (buf);
> +        }
> +      /* Else there is nothing to write.  */
> +      return;
> +    }
> +
> +  /* We have written directly into the buf->fp buffer.  */
> +  assert (buf->base.write_end == buf->fp->_IO_write_end);
> +
> +  /* Mark the bytes as written.  */
> +  buf->fp->_IO_write_ptr = buf->base.write_ptr;
> +
> +  if (buf->base.write_ptr == buf->base.write_end)
> +    {
> +      /* The buffer in buf->fp has been filled.  This should just call
> +         __overflow (buf->fp, EOF), but flush-only overflow is obscure
> +         and not always correctly implemented.  See bug 28949.  Be
> +         conservative and switch to a one-character buffer instead, to
> +         obtain one more character for a regular __overflow call.  */
> +      buf->base.write_ptr = buf->stage;
> +      buf->base.write_end = buf->stage + 1;
> +    }
> +  /* The bytes in the file stream were already marked as written above.  */
> +
> +  buf->base.write_base = buf->base.write_ptr;
> +}
> +
> +void
> +__printf_buffer_to_file_init (struct __printf_buffer_to_file *buf, FILE *fp)
> +{
> +  __printf_buffer_init (&buf->base, buf->stage, array_length (buf->stage),
> +                        __printf_buffer_mode_to_file);
> +  buf->fp = fp;
> +  __printf_buffer_to_file_switch (buf);
> +}
> +
> +int
> +__printf_buffer_to_file_done (struct __printf_buffer_to_file *buf)
> +{
> +  if (__printf_buffer_has_failed (&buf->base))
> +    return -1;
> +  __printf_buffer_flush_to_file (buf);
> +  return __printf_buffer_done (&buf->base);
> +}

Ok.

> diff --git a/stdio-common/printf_buffer_to_file.h b/stdio-common/printf_buffer_to_file.h
> new file mode 100644
> index 0000000000..97ec1d6d95
> --- /dev/null
> +++ b/stdio-common/printf_buffer_to_file.h
> @@ -0,0 +1,57 @@
> +/* Multibyte and wide printf buffers writing data to a FILE * stream.
> +   Copyright (C) 2022 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
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#ifndef PRINTF_BUFFER_TO_FILE_H
> +#define PRINTF_BUFFER_TO_FILE_H
> +
> +#include <bits/types/FILE.h>
> +#include <printf_buffer.h>
> +
> +struct __printf_buffer_to_file
> +{
> +  struct __printf_buffer base;
> +  FILE *fp;
> +
> +  /* Staging buffer.  All data goes through this buffer before
> +     reaching the fp stream.  (Any buffer in fp is not yet used.)  */
> +  char stage[128];
> +};

Out of curiosity, why 128?

> +
> +/* Initializes *BUF to write data to FP.  */
> +void __printf_buffer_to_file_init (struct __printf_buffer_to_file *buf,
> +                                   FILE *fp) attribute_hidden;
> +
> +/* Transfers any pending data in BUF to BUF->FP.  The return value
> +   follows the printf convention (number bytes written; or -1 for error).  */
> +int __printf_buffer_to_file_done (struct __printf_buffer_to_file *buf)
> +  attribute_hidden;
> +
> +/* Wide version below.  */
> +
> +struct __wprintf_buffer_to_file
> +{
> +  struct __wprintf_buffer base;
> +  FILE *fp;
> +  wchar_t stage[128];
> +};
> +void __wprintf_buffer_to_file_init (struct __wprintf_buffer_to_file *buf,
> +                                    FILE *fp) attribute_hidden;
> +int __wprintf_buffer_to_file_done (struct __wprintf_buffer_to_file *buf)
> +  attribute_hidden;
> +
> +#endif /* PRINTF_BUFFER_TO_FILE_H */

Ok.

> diff --git a/stdio-common/printf_buffer_write.c b/stdio-common/printf_buffer_write.c
> new file mode 100644
> index 0000000000..1038f69063
> --- /dev/null
> +++ b/stdio-common/printf_buffer_write.c
> @@ -0,0 +1,21 @@
> +/* Blob write function for struct __printf_buffer.  Multibyte version.
> +   Copyright (C) 2022 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
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#include <printf_buffer.h>
> +#include "printf_buffer-char.h"
> +#include "Xprintf_buffer_write.c"

Ok.

> diff --git a/stdio-common/wprintf_buffer_as_file.c b/stdio-common/wprintf_buffer_as_file.c
> new file mode 100644
> index 0000000000..cd48a7d42a
> --- /dev/null
> +++ b/stdio-common/wprintf_buffer_as_file.c
> @@ -0,0 +1,153 @@
> +/* FILE * interface to a struct __wprintf_buffer.  Wide version.
> +   Copyright (C) 2022 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
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#include <printf_buffer_as_file.h>
> +
> +#include <assert.h>
> +#include <printf_buffer.h>
> +
> +/* Commit the data directly written through the stdio stream.  */
> +static void
> +__wprintf_buffer_as_file_commit (struct __wprintf_buffer_as_file *file)
> +{
> +  /* Check that the write pointers in the file stream are consistent
> +     with the next buffer.  */
> +  assert (file->wide_stream._IO_write_ptr >= file->next->write_ptr);
> +  assert (file->wide_stream._IO_write_ptr <= file->next->write_end);
> +  assert (file->wide_stream._IO_write_base == file->next->write_base);
> +  assert (file->wide_stream._IO_write_end == file->next->write_end);
> +
> +  file->next->write_ptr = file->wide_stream._IO_write_ptr;
> +}
> +
> +/* Pointer the FILE * write buffer into the active struct __wprintf_buffer
> +   area.  */
> +static void
> +__wprintf_buffer_as_file_switch_to_buffer (struct __wprintf_buffer_as_file *file)
> +{
> +  file->wide_stream._IO_write_base = file->next->write_base;
> +  file->wide_stream._IO_write_ptr = file->next->write_ptr;
> +  file->wide_stream._IO_write_end = file->next->write_end;
> +}
> +
> +/* Only a small subset of the vtable functions is implemented here,
> +   following _IO_obstack_jumps.  */
> +
> +static wint_t
> +__wprintf_buffer_as_file_overflow (FILE *fp, int ch)
> +{
> +  struct __wprintf_buffer_as_file *file
> +    = (struct __wprintf_buffer_as_file *) fp;
> +
> +  __wprintf_buffer_as_file_commit (file);
> +
> +  /* EOF means only a flush is requested.   */
> +  if (ch != WEOF)
> +    __wprintf_buffer_putc (file->next, ch);
> +  else
> +    ch = 0;
> +
> +  /* Ensure that flushing actually produces room.  */
> +  if (!__wprintf_buffer_has_failed (file->next)
> +      && file->next->write_ptr == file->next->write_end)
> +    __wprintf_buffer_flush (file->next);
> +
> +  __wprintf_buffer_as_file_switch_to_buffer (file);
> +
> +  if (!__wprintf_buffer_has_failed (file->next))
> +    return (unsigned char) ch;
> +  else
> +    return WEOF;
> +}
> +
> +static size_t
> +__wprintf_buffer_as_file_xsputn (FILE *fp, const void *buf, size_t len)
> +{
> +  struct __wprintf_buffer_as_file *file
> +    = (struct __wprintf_buffer_as_file *) fp;
> +
> +  __wprintf_buffer_as_file_commit (file);
> +
> +  /* Copy the data.  */
> +  __wprintf_buffer_write (file->next, buf, len);
> +
> +  __wprintf_buffer_as_file_switch_to_buffer (file);
> +
> +  if (!__wprintf_buffer_has_failed (file->next))
> +    return len;
> +  else
> +    /* We may actually have written something.  But the stream is
> +       corrupted in this case anyway, so try not to divine the write
> +       count here.  */
> +    return 0;
> +}
> +
> +static const struct _IO_jump_t _IO_wprintf_buffer_as_file_jumps libio_vtable =
> +{
> +  JUMP_INIT_DUMMY,
> +  JUMP_INIT(finish, NULL),
> +  JUMP_INIT(overflow, (_IO_overflow_t) __wprintf_buffer_as_file_overflow),
> +  JUMP_INIT(underflow, NULL),
> +  JUMP_INIT(uflow, NULL),
> +  JUMP_INIT(pbackfail, NULL),
> +  JUMP_INIT(xsputn, __wprintf_buffer_as_file_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)
> +};
> +

As for _IO_printf_buffer_as_file_jumps, _IO_wprintf_buffer_as_file_jumps also should
be added on tst-relro-symbols.py required list.

> +void
> +__wprintf_buffer_as_file_init (struct __wprintf_buffer_as_file *file,
> +                               struct __wprintf_buffer *next)
> +{
> +  file->stream._lock = NULL;
> +  _IO_no_init (&file->stream, _IO_USER_LOCK, 0, &file->wide_stream,
> +               &_IO_wprintf_buffer_as_file_jumps);
> +  _IO_fwide (&file->stream, 1);
> +
> +  /* Set up the write buffer from the next buffer.  */
> +  file->next = next;
> +  __wprintf_buffer_as_file_switch_to_buffer (file);
> +
> +  /* Mark the read area as inactive, by making all pointers equal.  */
> +  file->stream._IO_read_base = file->stream._IO_write_base;
> +  file->stream._IO_read_ptr = file->stream._IO_write_base;
> +  file->stream._IO_read_end = file->stream._IO_write_base;
> +}
> +
> +bool
> +__wprintf_buffer_as_file_terminate (struct __wprintf_buffer_as_file *file)
> +{
> +  if (file->stream._flags & _IO_ERR_SEEN)
> +    return false;
> +  else
> +    {
> +      __wprintf_buffer_as_file_commit (file);
> +      return true;
> +    }
> +}

Ok.

> diff --git a/stdio-common/wprintf_buffer_done.c b/stdio-common/wprintf_buffer_done.c
> new file mode 100644
> index 0000000000..60eac03955
> --- /dev/null
> +++ b/stdio-common/wprintf_buffer_done.c
> @@ -0,0 +1,21 @@
> +/* Final status reporting for struct __wprintf_buffer.  Wide version.
> +   Copyright (C) 2022 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
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#include <printf_buffer.h>
> +#include "printf_buffer-wchar_t.h"
> +#include "Xprintf_buffer_done.c"

Ok.

> diff --git a/stdio-common/wprintf_buffer_flush.c b/stdio-common/wprintf_buffer_flush.c
> new file mode 100644
> index 0000000000..2d91095cca
> --- /dev/null
> +++ b/stdio-common/wprintf_buffer_flush.c
> @@ -0,0 +1,36 @@
> +/* Flush a struct __wprintf_buffer.  Wide version.
> +   Copyright (C) 2022 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
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#include <printf_buffer.h>
> +
> +#include "printf_buffer-wchar_t.h"
> +#include "Xprintf_buffer_flush.c"
> +
> +static void
> +__wprintf_buffer_do_flush (struct __wprintf_buffer *buf)
> +{
> +  switch (buf->mode)
> +    {
> +    case __wprintf_buffer_mode_failed:
> +      return;
> +    case __wprintf_buffer_mode_to_file:
> +      __wprintf_buffer_flush_to_file ((struct __wprintf_buffer_to_file *) buf);
> +      return;
> +    }
> +  __builtin_trap ();
> +}

Ok.

> diff --git a/stdio-common/wprintf_buffer_pad_1.c b/stdio-common/wprintf_buffer_pad_1.c
> new file mode 100644
> index 0000000000..9c91126f42
> --- /dev/null
> +++ b/stdio-common/wprintf_buffer_pad_1.c
> @@ -0,0 +1,21 @@
> +/* Write repeated characters to struct __wprintf_buffer.  Wide version.
> +   Copyright (C) 2022 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
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#include <printf_buffer.h>
> +#include "printf_buffer-wchar_t.h"
> +#include "Xprintf_buffer_pad_1.c"

Ok.

> diff --git a/stdio-common/wprintf_buffer_putc_1.c b/stdio-common/wprintf_buffer_putc_1.c
> new file mode 100644
> index 0000000000..b1af68e721
> --- /dev/null
> +++ b/stdio-common/wprintf_buffer_putc_1.c
> @@ -0,0 +1,21 @@
> +/* Overflow character write function for struct __wprintf_buffer.  Wide version.
> +   Copyright (C) 2022 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
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#include <printf_buffer.h>
> +#include "printf_buffer-wchar_t.h"
> +#include "Xprintf_buffer_putc_1.c"

Ok.

> diff --git a/stdio-common/wprintf_buffer_puts_1.c b/stdio-common/wprintf_buffer_puts_1.c
> new file mode 100644
> index 0000000000..039aebb56e
> --- /dev/null
> +++ b/stdio-common/wprintf_buffer_puts_1.c
> @@ -0,0 +1,21 @@
> +/* String write function struct __wprintf_buffer.  Wide version.
> +   Copyright (C) 2022 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
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#include <printf_buffer.h>
> +#include "printf_buffer-wchar_t.h"
> +#include "Xprintf_buffer_puts_1.c"

Ok.

> diff --git a/stdio-common/wprintf_buffer_to_file.c b/stdio-common/wprintf_buffer_to_file.c
> new file mode 100644
> index 0000000000..ac936fab6c
> --- /dev/null
> +++ b/stdio-common/wprintf_buffer_to_file.c
> @@ -0,0 +1,55 @@
> +/* Wide printf buffers writing data to a FILE *.
> +   Copyright (C) 2022 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
> +   <https://www.gnu.org/licenses/>.  */
> +
> +/* This implementation is not optimized (unlikely the multibyte
> +   implementation) and always writes to the temporary buffer first.  */
> +
> +#include <printf_buffer_to_file.h>
> +
> +#include <array_length.h>
> +#include <libio/libioP.h>
> +
> +void
> +__wprintf_buffer_flush_to_file (struct __wprintf_buffer_to_file *buf)
> +{
> +  size_t count = buf->base.write_ptr - buf->stage;
> +  if ((size_t) _IO_sputn (buf->fp, buf->stage, count) != count)
> +    {
> +      __wprintf_buffer_mark_failed (&buf->base);
> +      return;
> +    }
> +  buf->base.written += count;
> +  buf->base.write_ptr = buf->stage;
> +}
> +
> +void
> +__wprintf_buffer_to_file_init (struct __wprintf_buffer_to_file *buf, FILE *fp)
> +{
> +  __wprintf_buffer_init (&buf->base, buf->stage, array_length (buf->stage),
> +                         __wprintf_buffer_mode_to_file);
> +  buf->fp = fp;
> +}
> +
> +int
> +__wprintf_buffer_to_file_done (struct __wprintf_buffer_to_file *buf)
> +{
> +  if (__wprintf_buffer_has_failed (&buf->base))
> +    return -1;
> +  __wprintf_buffer_flush_to_file (buf);
> +  return __wprintf_buffer_done (&buf->base);
> +}

Ok.

> diff --git a/stdio-common/wprintf_buffer_write.c b/stdio-common/wprintf_buffer_write.c
> new file mode 100644
> index 0000000000..06b53c74c3
> --- /dev/null
> +++ b/stdio-common/wprintf_buffer_write.c
> @@ -0,0 +1,21 @@
> +/* Blob write function for struct __wprintf_buffer.  Wide version.
> +   Copyright (C) 2022 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
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#include <printf_buffer.h>
> +#include "printf_buffer-wchar_t.h"
> +#include "Xprintf_buffer_write.c"

Ok.
  
Florian Weimer Dec. 16, 2022, 5:51 p.m. UTC | #2
* Adhemerval Zanella Netto:

>> +/* Writes COUNT repeats of CH to BUF.  */
>> +void __printf_buffer_pad_1 (struct __printf_buffer *buf,
>> +                            char ch, size_t count) attribute_hidden;
>> +
>> +/* __printf_buffer_pad with fast path for no padding.  COUNT is a
>> +   long int, to accomodate signed uses in printf and elsewhere.  */
>
> Wouldn't ssize works better here than long int?

I'm going to test the change.

>> +static inline void
>> +__printf_buffer_pad (struct __printf_buffer *buf, char ch, long int count)
>> +{
>> +  if (count > 0)
>> +    __printf_buffer_pad_1 (buf, ch, count);
>> +}
>> +
>> +/* Write COUNT bytes starting at S to BUF.  S must not overlap with
>> +   the internal buffer.  */
>> +void __printf_buffer_write (struct __printf_buffer *buf, const char *s,
>> +                            size_t count) attribute_hidden;
>> +
>> +/* Write S to BUF.  S must not overlap with the internal buffer.  */
>> +void __printf_buffer_puts_1 (struct __printf_buffer *buf, const char *s)
>> +  attribute_hidden;
>
> Maybe a space here?

Added an empty line.

>> +static inline void
>> +__printf_buffer_puts (struct __printf_buffer *buf, const char *s)
>> +{
>> +  if (__builtin_constant_p (__builtin_strlen (s)))
>> +    __printf_buffer_write (buf, s, __builtin_strlen (s));
>> +  else
>> +    __printf_buffer_puts_1 (buf, s);
>> +}
>> +
>> +/* Returns the number of bytes written through the buffer, or -1 if
>> +   there was an error (that is, __printf_buffer_has_failed (BUF) is true).
>> +
>> +   The number of written bytes includes pending bytes in the buffer
>> +   (between BUF->write_base and BUF->write_ptr).
>> +
>> +   If the number is larger than INT_MAX, returns -1 and sets errno to
>> +   EOVERFLOW.  This function does not flush the buffer.  If the caller
>> +   needs the side effect of flushing, it has to do this
>> +   separately.  */
>> +int __printf_buffer_done (struct __printf_buffer *buf) attribute_hidden;
>
> Wouldn't ssize_t work better for return type?

No, it's really for use in printf etc., and those return int
(incorrectly, too late to fix now), so we have to check against INT_MAX.

>> +int
>> +Xprintf_buffer_done (struct Xprintf_buffer *buf)
>> +{
>> +  if (Xprintf_buffer_has_failed (buf))
>> +    return -1;
>> +
>> +  /* Use uintptr_t here because for sprintf, the buffer range may
>> +     cover more than half of the address space.  */
>> +  uintptr_t written_current = buf->write_ptr - buf->write_base;
>
> Is this really a valid case?  Working with buffer ranges larger than half
> of address space is not valid for malloc buffer anyway, are you handling
> some iffy case for mmap case?

It's not really supported, but I wanted to mention it.

>> +void
>> +Xprintf (buffer_puts_1) (struct Xprintf_buffer *buf, const CHAR_T *s)
>> +{
>> +  if (__glibc_unlikely (Xprintf_buffer_has_failed (buf)))
>> +    return;
>> +
>> +  while (*s != 0)
>
> Maybe use NULL here?

No, *s is CHAR_T.

>> +static const struct _IO_jump_t _IO_printf_buffer_as_file_jumps libio_vtable =
>> +{
>> +  JUMP_INIT_DUMMY,
>> +  JUMP_INIT(finish, NULL),
>> +  JUMP_INIT(overflow, __printf_buffer_as_file_overflow),
>> +  JUMP_INIT(underflow, NULL),
>> +  JUMP_INIT(uflow, NULL),
>> +  JUMP_INIT(pbackfail, NULL),
>> +  JUMP_INIT(xsputn, __printf_buffer_as_file_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)
>> +};
>
> The _IO_printf_buffer_as_file_jumps should be added on
> tst-relro-symbols.py required list.

Done.

>> diff --git a/stdio-common/printf_buffer_to_file.h b/stdio-common/printf_buffer_to_file.h
>> new file mode 100644
>> index 0000000000..97ec1d6d95
>> --- /dev/null
>> +++ b/stdio-common/printf_buffer_to_file.h
>> @@ -0,0 +1,57 @@
>> +/* Multibyte and wide printf buffers writing data to a FILE * stream.
>> +   Copyright (C) 2022 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
>> +   <https://www.gnu.org/licenses/>.  */
>> +
>> +#ifndef PRINTF_BUFFER_TO_FILE_H
>> +#define PRINTF_BUFFER_TO_FILE_H
>> +
>> +#include <bits/types/FILE.h>
>> +#include <printf_buffer.h>
>> +
>> +struct __printf_buffer_to_file
>> +{
>> +  struct __printf_buffer base;
>> +  FILE *fp;
>> +
>> +  /* Staging buffer.  All data goes through this buffer before
>> +     reaching the fp stream.  (Any buffer in fp is not yet used.)  */
>> +  char stage[128];
>> +};
>
> Out of curiosity, why 128?

Few lines are longer than 128, and this path is used for line-buffered
streams (among others).

>> +static const struct _IO_jump_t _IO_wprintf_buffer_as_file_jumps libio_vtable =
>> +{
>> +  JUMP_INIT_DUMMY,
>> +  JUMP_INIT(finish, NULL),
>> +  JUMP_INIT(overflow, (_IO_overflow_t) __wprintf_buffer_as_file_overflow),
>> +  JUMP_INIT(underflow, NULL),
>> +  JUMP_INIT(uflow, NULL),
>> +  JUMP_INIT(pbackfail, NULL),
>> +  JUMP_INIT(xsputn, __wprintf_buffer_as_file_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)
>> +};
>> +
>
> As for _IO_printf_buffer_as_file_jumps,
> _IO_wprintf_buffer_as_file_jumps also should be added on
> tst-relro-symbols.py required list.

Done.

Thanks,
Florian
  

Patch

diff --git a/include/printf_buffer.h b/include/printf_buffer.h
new file mode 100644
index 0000000000..ad08a72b71
--- /dev/null
+++ b/include/printf_buffer.h
@@ -0,0 +1,281 @@ 
+/* Multibyte and wide buffers for implementing printf-related functions.
+   Copyright (C) 2022 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
+   <https://www.gnu.org/licenses/>.  */
+
+/* The naming of the multibyte and wide variants is intentionally
+   consistent, so that it is possible to use the Xprintf macro in
+   stdio-common/printf_buffer-char.h and
+   stdio-common/printf_buffer-wchar_t.h to select between them in
+   type-generic code.  */
+
+#ifndef PRINTF_BUFFER_H
+#define PRINTF_BUFFER_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <wchar.h>
+
+/* <printf_buffer_as_file.h> introduces a way to use struct
+   __printf_buffer objects from FILE * streams.  To avoid storing a
+   function pointer (or vtable pointer) in struct __printf_buffer
+   (which would defeat libio vtable hardening), a switch statement
+   over the different flush implementations is used to implement
+   __printf_buffer_flush.
+
+   __printf_buffer_mode_failed is special: it is the sticky failure
+   indicator.  Unlike struct alloc_buffer, this is not folded into
+   write_ptr, so that snprintf and other string-writing functions can
+   discover the end of the string even in the error case, to be able
+   to add the null terminator.  */
+enum __printf_buffer_mode
+  {
+    __printf_buffer_mode_failed,
+    __printf_buffer_mode_to_file,
+  };
+
+/* Buffer for fast character writing with overflow handling.
+   Typically embedded in another struct with further data that is used
+   by the flush function.  */
+struct __printf_buffer
+{
+  /* These pointer members follow FILE streams.  write_ptr and
+     write_end must be initialized to cover the target buffer.  See
+     __printf_buffer_init.
+
+     Data can be written directly to *write_ptr while write_ptr !=
+     write_end, and write_ptr can be advanced accordingly.  Note that
+     is not possible to use the apparently-unused part of the buffer
+     as scratch space because sprintf (and snprintf, but that is a bit
+     iffy) must only write the minimum number of characters produced
+     by the format string and its arguments.
+
+     write_base must be initialized to be equal to write_ptr.  The
+     framework uses this pointer to compute the total number of
+     written bytes, together with the written field.  See
+     __printf_buffer_done.
+
+     write_base and write_end are only read by the generic functions
+     after initialization, only the flush implementation called from
+     __printf_buffer_flush might change these pointers.  See the
+     comment on Xprintf (buffer_do_flush) in Xprintf_buffer_flush.c
+     for details regarding the flush operation.  */
+  char *write_base;
+  char *write_ptr;
+  char *write_end;
+
+  /* Number of characters written so far (excluding the current
+     buffer).  Potentially updated on flush.  The actual number of
+     written bytes also includes the unflushed-but-written buffer
+     part, write_ptr - write_base.  A 64-bit value is used to avoid
+     the need for overflow checks.  */
+  uint64_t written;
+
+  /* Identifies the flush callback.  */
+  enum __printf_buffer_mode mode;
+};
+
+/* Marks the buffer as failed, so that __printf_buffer_has_failed
+   returns true and future flush operations are no-ops.  */
+static inline void
+__printf_buffer_mark_failed (struct __printf_buffer *buf)
+{
+  buf->mode = __printf_buffer_mode_failed;
+}
+
+/* Returns true if the sticky error indicator of the buffer has been
+   set to failed.  */
+static inline bool __attribute_warn_unused_result__
+__printf_buffer_has_failed (struct __printf_buffer *buf)
+{
+  return buf->mode == __printf_buffer_mode_failed;
+}
+
+/* Initialization of a buffer, using the memory region from [BASE, BASE +LEN)
+   as the initial buffer contents.  LEN can be zero.  */
+static inline void
+__printf_buffer_init (struct __printf_buffer *buf, char *base, size_t len,
+                      enum __printf_buffer_mode mode)
+{
+  buf->write_base = base;
+  buf->write_ptr = base;
+  buf->write_end = base + len;
+  buf->written = 0;
+  buf->mode = mode;
+}
+
+/* Called by printf_buffer_putc for a full buffer.  */
+void __printf_buffer_putc_1 (struct __printf_buffer *buf, char ch)
+  attribute_hidden;
+
+/* Writes CH to BUF.  */
+static inline void
+__printf_buffer_putc (struct __printf_buffer *buf, char ch)
+{
+  if (buf->write_ptr != buf->write_end)
+      *buf->write_ptr++ = ch;
+  else
+    __printf_buffer_putc_1 (buf, ch);
+}
+
+/* Writes COUNT repeats of CH to BUF.  */
+void __printf_buffer_pad_1 (struct __printf_buffer *buf,
+                            char ch, size_t count) attribute_hidden;
+
+/* __printf_buffer_pad with fast path for no padding.  COUNT is a
+   long int, to accomodate signed uses in printf and elsewhere.  */
+static inline void
+__printf_buffer_pad (struct __printf_buffer *buf, char ch, long int count)
+{
+  if (count > 0)
+    __printf_buffer_pad_1 (buf, ch, count);
+}
+
+/* Write COUNT bytes starting at S to BUF.  S must not overlap with
+   the internal buffer.  */
+void __printf_buffer_write (struct __printf_buffer *buf, const char *s,
+                            size_t count) attribute_hidden;
+
+/* Write S to BUF.  S must not overlap with the internal buffer.  */
+void __printf_buffer_puts_1 (struct __printf_buffer *buf, const char *s)
+  attribute_hidden;
+static inline void
+__printf_buffer_puts (struct __printf_buffer *buf, const char *s)
+{
+  if (__builtin_constant_p (__builtin_strlen (s)))
+    __printf_buffer_write (buf, s, __builtin_strlen (s));
+  else
+    __printf_buffer_puts_1 (buf, s);
+}
+
+/* Returns the number of bytes written through the buffer, or -1 if
+   there was an error (that is, __printf_buffer_has_failed (BUF) is true).
+
+   The number of written bytes includes pending bytes in the buffer
+   (between BUF->write_base and BUF->write_ptr).
+
+   If the number is larger than INT_MAX, returns -1 and sets errno to
+   EOVERFLOW.  This function does not flush the buffer.  If the caller
+   needs the side effect of flushing, it has to do this
+   separately.  */
+int __printf_buffer_done (struct __printf_buffer *buf) attribute_hidden;
+
+/* Internally used to call the flush function.  This can be called
+   explicitly for certain modes to flush the buffer prematuraly.  In
+   such cases, it is often the case that the buffer mode is statically
+   known, and the flush implementation can be called directly.  */
+bool __printf_buffer_flush (struct __printf_buffer *buf) attribute_hidden;
+
+/* Wide version of struct __printf_buffer follows.  */
+
+enum __wprintf_buffer_mode
+  {
+    __wprintf_buffer_mode_failed,
+    __wprintf_buffer_mode_to_file,
+  };
+
+struct __wprintf_buffer
+{
+  wchar_t *write_base;
+  wchar_t *write_ptr;
+  wchar_t *write_end;
+  uint64_t written;
+  enum __wprintf_buffer_mode mode;
+};
+
+static inline void
+__wprintf_buffer_mark_failed (struct __wprintf_buffer *buf)
+{
+  buf->mode = __wprintf_buffer_mode_failed;
+}
+
+static inline bool __attribute_warn_unused_result__
+__wprintf_buffer_has_failed (struct __wprintf_buffer *buf)
+{
+  return buf->mode == __wprintf_buffer_mode_failed;
+}
+
+static inline void
+__wprintf_buffer_init (struct __wprintf_buffer *buf,
+                       wchar_t *base, size_t len,
+                       enum __wprintf_buffer_mode mode)
+{
+  buf->write_base = base;
+  buf->write_ptr = base;
+  buf->write_end = base + len;
+  buf->written = 0;
+  buf->mode = mode;
+}
+
+void __wprintf_buffer_putc_1 (struct __wprintf_buffer *buf, wchar_t ch)
+  attribute_hidden;
+
+static inline void
+__wprintf_buffer_putc (struct __wprintf_buffer *buf, wchar_t ch)
+{
+  if (buf->write_ptr != buf->write_end)
+      *buf->write_ptr++ = ch;
+  else
+    __wprintf_buffer_putc_1 (buf, ch);
+}
+
+void __wprintf_buffer_pad_1 (struct __wprintf_buffer *buf,
+                             wchar_t ch, size_t count) attribute_hidden;
+
+static inline void
+__wprintf_buffer_pad (struct __wprintf_buffer *buf, char ch, long int count)
+{
+  if (count > 0)
+    __wprintf_buffer_pad_1 (buf, ch, count);
+}
+
+void __wprintf_buffer_write (struct __wprintf_buffer *buf, const wchar_t *s,
+                             size_t count) attribute_hidden;
+
+void __wprintf_buffer_puts (struct __wprintf_buffer *buf, const wchar_t *s)
+  attribute_hidden;
+
+int __wprintf_buffer_done (struct __wprintf_buffer *buf) attribute_hidden;
+
+bool __wprintf_buffer_flush (struct __wprintf_buffer *buf) attribute_hidden;
+
+/* Type-generic convenience macros.  They are useful if
+   printf_buffer-char.h or printf_buffer-wchar_t.h is included as
+   well.  */
+
+#define Xprintf_buffer Xprintf (buffer)
+#define Xprintf_buffer_done Xprintf (buffer_done)
+#define Xprintf_buffer_flush Xprintf (buffer_flush)
+#define Xprintf_buffer_has_failed Xprintf (buffer_has_failed)
+#define Xprintf_buffer_mark_failed Xprintf (buffer_mark_failed)
+#define Xprintf_buffer_pad Xprintf (buffer_pad)
+#define Xprintf_buffer_putc Xprintf (buffer_putc)
+#define Xprintf_buffer_puts Xprintf (buffer_puts)
+#define Xprintf_buffer_write Xprintf (buffer_write)
+
+/* Flush function implementations follow.  They are called from
+   __printf_buffer_flush.  Generic code should not call these flush
+   functions directly.  Some modes have inline implementations.  */
+
+struct __printf_buffer_to_file;
+void __printf_buffer_flush_to_file (struct __printf_buffer_to_file *)
+  attribute_hidden;
+
+struct __wprintf_buffer_to_file;
+void __wprintf_buffer_flush_to_file (struct __wprintf_buffer_to_file *)
+  attribute_hidden;
+
+#endif /* PRINTF_BUFFER_H */
diff --git a/stdio-common/Makefile b/stdio-common/Makefile
index 8f2524959d..120d66ea93 100644
--- a/stdio-common/Makefile
+++ b/stdio-common/Makefile
@@ -53,6 +53,14 @@  routines := \
   perror \
   printf \
   printf-prs \
+  printf_buffer_as_file \
+  printf_buffer_done \
+  printf_buffer_flush \
+  printf_buffer_pad_1 \
+  printf_buffer_putc_1 \
+  printf_buffer_puts_1 \
+  printf_buffer_to_file \
+  printf_buffer_write \
   printf_fp \
   printf_fphex \
   printf_size \
@@ -85,6 +93,14 @@  routines := \
   vfwscanf \
   vfwscanf-internal \
   vprintf \
+  wprintf_buffer_as_file \
+  wprintf_buffer_done \
+  wprintf_buffer_flush \
+  wprintf_buffer_pad_1 \
+  wprintf_buffer_putc_1 \
+  wprintf_buffer_puts_1 \
+  wprintf_buffer_to_file \
+  wprintf_buffer_write \
   # routines
 
 aux := \
diff --git a/stdio-common/Xprintf_buffer_done.c b/stdio-common/Xprintf_buffer_done.c
new file mode 100644
index 0000000000..a3f51c43ef
--- /dev/null
+++ b/stdio-common/Xprintf_buffer_done.c
@@ -0,0 +1,40 @@ 
+/* Final status reporting for struct __*printf_buffer.  Generic version.
+   Copyright (C) 2022 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
+   <https://www.gnu.org/licenses/>.  */
+
+#include <errno.h>
+#include <intprops.h>
+#include <stdint.h>
+
+int
+Xprintf_buffer_done (struct Xprintf_buffer *buf)
+{
+  if (Xprintf_buffer_has_failed (buf))
+    return -1;
+
+  /* Use uintptr_t here because for sprintf, the buffer range may
+     cover more than half of the address space.  */
+  uintptr_t written_current = buf->write_ptr - buf->write_base;
+  int written_total;
+  if (INT_ADD_WRAPV (buf->written, written_current, &written_total))
+    {
+      __set_errno (EOVERFLOW);
+      return -1;
+    }
+  else
+    return written_total;
+}
diff --git a/stdio-common/Xprintf_buffer_flush.c b/stdio-common/Xprintf_buffer_flush.c
new file mode 100644
index 0000000000..1368cfe684
--- /dev/null
+++ b/stdio-common/Xprintf_buffer_flush.c
@@ -0,0 +1,72 @@ 
+/* Flush wrapper for struct __*printf_buffer.  Generic version.
+   Copyright (C) 2022 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
+   <https://www.gnu.org/licenses/>.  */
+
+#include <printf_buffer.h>
+
+#include <assert.h>
+#include <stdint.h>
+
+/* Xprintf (buffer_do_flush) (BUF) performs the flush operation.  The
+   actual implementation is specific to the multibyte and wide
+   variants.
+
+   If the flush fails, Xprintf_buffer_mark_failed (BUF) must be
+   called, and BUF->write_ptr and BUF->write_end can be left
+   unchanged.
+
+   The function must not do anything if failure has already occurred,
+   that is, if BUF->mode == Xprintf (buffer_mode_failed).
+
+   The framework implicitly invokes flush with BUF->write_ptr ==
+   BUF->write_end only.  (This is particularly relevant to the
+   __sprintf_chk flush, which just calls __chk_fail.)  But in some
+   cases, Xprintf_buffer_flush may be called explicitly (when
+   BUF->mode/the backing function is known).  In that case, it is
+   possible that BUF->write_ptr < BUF->write_end is true.
+
+   If the flush succeeds, the pointers are changed so that
+   BUF->write_ptr < BUF->write_end.  It is possible to switch to a
+   completely different buffer here.  If the buffer is moved, it may
+   be necessary to updated BUF->write_base and BUF->written from the
+   flush function as well.
+
+   Note that when chaining buffers, in the flush function for the
+   outer buffer (to which data is written first), it is necessary to
+   check for BUF->next->failed (for the inner buffer) and set
+   BUF->base.failed to true (for the outer buffer).  This should come
+   towards the end of the outer flush function.  Usually, there is
+   also some unwrapping step afterwards; it has to check the outer
+   buffer (BUF->base.failed) and propagate any error to the inner
+   buffer (BUF->next->failed), so essentially in the other
+   direction.  */
+static void Xprintf (buffer_do_flush) (struct Xprintf_buffer *buf);
+
+bool
+Xprintf_buffer_flush (struct Xprintf_buffer *buf)
+{
+  if (__glibc_unlikely (Xprintf_buffer_has_failed (buf)))
+    return false;
+
+  Xprintf (buffer_do_flush) (buf);
+  if (Xprintf_buffer_has_failed (buf))
+    return false;
+
+  /* Ensure that the flush has made available some bytes.  */
+  assert (buf->write_ptr != buf->write_end);
+  return true;
+}
diff --git a/stdio-common/Xprintf_buffer_pad_1.c b/stdio-common/Xprintf_buffer_pad_1.c
new file mode 100644
index 0000000000..2be8270ad2
--- /dev/null
+++ b/stdio-common/Xprintf_buffer_pad_1.c
@@ -0,0 +1,44 @@ 
+/* Write repeated characters to struct __*printf_buffer.  Generic version.
+   Copyright (C) 2022 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
+   <https://www.gnu.org/licenses/>.  */
+
+#include <assert.h>
+#include <string.h>
+
+void
+Xprintf (buffer_pad_1) (struct Xprintf_buffer *buf, CHAR_T ch, size_t count)
+{
+  if (__glibc_unlikely (Xprintf_buffer_has_failed (buf)))
+    return;
+
+  do
+    {
+      /* Proactively make room.  __*printf_buffer_pad has already
+         checked for a zero-length write, so this function is only
+         called when there is actually data to write.  */
+      if (buf->write_ptr == buf->write_end && !Xprintf_buffer_flush (buf))
+        return;
+      assert (buf->write_ptr != buf->write_end);
+      size_t to_fill = buf->write_end - buf->write_ptr;
+      if (to_fill > count)
+        to_fill = count;
+      MEMSET (buf->write_ptr, ch, to_fill);
+      buf->write_ptr += to_fill;
+      count -= to_fill;
+    }
+  while (count > 0);
+}
diff --git a/stdio-common/Xprintf_buffer_putc_1.c b/stdio-common/Xprintf_buffer_putc_1.c
new file mode 100644
index 0000000000..3458775a1c
--- /dev/null
+++ b/stdio-common/Xprintf_buffer_putc_1.c
@@ -0,0 +1,29 @@ 
+/* Overflow write function for struct __*printf_buffer.  Generic version.
+   Copyright (C) 2022 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
+   <https://www.gnu.org/licenses/>.  */
+
+#include <assert.h>
+
+void
+Xprintf (buffer_putc_1) (struct Xprintf_buffer *buf, CHAR_T ch)
+{
+  if (__glibc_unlikely (Xprintf_buffer_has_failed (buf))
+      || !Xprintf_buffer_flush (buf))
+    return;
+  assert (buf->write_ptr < buf->write_end);
+  *buf->write_ptr++ = ch;
+}
diff --git a/stdio-common/Xprintf_buffer_puts_1.c b/stdio-common/Xprintf_buffer_puts_1.c
new file mode 100644
index 0000000000..88d0b8b2bd
--- /dev/null
+++ b/stdio-common/Xprintf_buffer_puts_1.c
@@ -0,0 +1,38 @@ 
+/* String write function for struct __*printf_buffer.  Generic version.
+   Copyright (C) 2022 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
+   <https://www.gnu.org/licenses/>.  */
+
+#include <assert.h>
+#include <string.h>
+
+void
+Xprintf (buffer_puts_1) (struct Xprintf_buffer *buf, const CHAR_T *s)
+{
+  if (__glibc_unlikely (Xprintf_buffer_has_failed (buf)))
+    return;
+
+  while (*s != 0)
+    {
+      if (buf->write_ptr == buf->write_end && !Xprintf_buffer_flush (buf))
+        return;
+      assert (buf->write_ptr != buf->write_end);
+      size_t to_copy = STRNLEN (s, buf->write_end - buf->write_ptr);
+      MEMCPY (buf->write_ptr, s, to_copy);
+      buf->write_ptr += to_copy;
+      s += to_copy;
+    }
+}
diff --git a/stdio-common/Xprintf_buffer_write.c b/stdio-common/Xprintf_buffer_write.c
new file mode 100644
index 0000000000..9db2b5066a
--- /dev/null
+++ b/stdio-common/Xprintf_buffer_write.c
@@ -0,0 +1,44 @@ 
+/* Blob write function for struct __*printf_buffer.  Generic version.
+   Copyright (C) 2022 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
+   <https://www.gnu.org/licenses/>.  */
+
+#include <printf_buffer.h>
+
+#include <assert.h>
+#include <string.h>
+
+void
+Xprintf_buffer_write (struct Xprintf_buffer *buf,
+                        const CHAR_T *s, size_t count)
+{
+  if (__glibc_unlikely (Xprintf_buffer_has_failed (buf)))
+    return;
+
+  while (count > 0)
+    {
+      if (buf->write_ptr == buf->write_end && !Xprintf_buffer_flush (buf))
+        return;
+      assert (buf->write_ptr != buf->write_end);
+      size_t to_copy = buf->write_end - buf->write_ptr;
+      if (to_copy > count)
+        to_copy = count;
+      MEMCPY (buf->write_ptr, s, to_copy);
+      buf->write_ptr += to_copy;
+      s += to_copy;
+      count -= to_copy;
+    }
+}
diff --git a/stdio-common/printf_buffer-char.h b/stdio-common/printf_buffer-char.h
new file mode 100644
index 0000000000..44264e24c0
--- /dev/null
+++ b/stdio-common/printf_buffer-char.h
@@ -0,0 +1,24 @@ 
+/* Macros for the multibyte (char) implementation of struct __printf_buffer.
+   Copyright (C) 2022 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
+   <https://www.gnu.org/licenses/>.  */
+
+#define Xprintf(n) __printf_##n
+
+#define CHAR_T char
+#define MEMCPY memcpy
+#define MEMSET memset
+#define STRNLEN __strnlen
diff --git a/stdio-common/printf_buffer-wchar_t.h b/stdio-common/printf_buffer-wchar_t.h
new file mode 100644
index 0000000000..d8d0c5da46
--- /dev/null
+++ b/stdio-common/printf_buffer-wchar_t.h
@@ -0,0 +1,24 @@ 
+/* Macros for wide (wchar_t) implementation of struct __wprintf_buffer.
+   Copyright (C) 2022 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
+   <https://www.gnu.org/licenses/>.  */
+
+#define Xprintf(n) __wprintf_##n
+
+#define CHAR_T wchar_t
+#define MEMCPY __wmemcpy
+#define MEMSET __wmemset
+#define STRNLEN __wcsnlen
diff --git a/stdio-common/printf_buffer_as_file.c b/stdio-common/printf_buffer_as_file.c
new file mode 100644
index 0000000000..f27b000d78
--- /dev/null
+++ b/stdio-common/printf_buffer_as_file.c
@@ -0,0 +1,148 @@ 
+/* FILE * interface to a struct __printf_buffer.  Multibyte version.
+   Copyright (C) 2022 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
+   <https://www.gnu.org/licenses/>.  */
+
+#include <printf_buffer_as_file.h>
+
+#include <assert.h>
+#include <printf_buffer.h>
+
+/* Commit the data directly written through the stdio stream.  */
+static void
+__printf_buffer_as_file_commit (struct __printf_buffer_as_file *file)
+{
+  /* Check that the write pointers in the file stream are consistent
+     with the next buffer.  */
+  assert (file->stream._IO_write_ptr >= file->next->write_ptr);
+  assert (file->stream._IO_write_ptr <= file->next->write_end);
+  assert (file->stream._IO_write_base == file->next->write_base);
+  assert (file->stream._IO_write_end == file->next->write_end);
+
+  file->next->write_ptr = file->stream._IO_write_ptr;
+}
+
+/* Pointer the FILE * write buffer into the active printf_buffer
+   area.  */
+static void
+__printf_buffer_as_file_switch_to_buffer (struct __printf_buffer_as_file *file)
+{
+  file->stream._IO_write_base = file->next->write_base;
+  file->stream._IO_write_ptr = file->next->write_ptr;
+  file->stream._IO_write_end = file->next->write_end;
+}
+
+/* Only a small subset of the vtable functions is implemented here,
+   following _IO_obstack_jumps.  */
+
+static int
+__printf_buffer_as_file_overflow (FILE *fp, int ch)
+{
+  struct __printf_buffer_as_file *file = (struct __printf_buffer_as_file *) fp;
+
+  __printf_buffer_as_file_commit (file);
+
+  /* EOF means only a flush is requested.   */
+  if (ch != EOF)
+    __printf_buffer_putc (file->next, ch);
+
+  /* Ensure that flushing actually produces room.  */
+  if (!__printf_buffer_has_failed (file->next)
+      && file->next->write_ptr == file->next->write_end)
+    __printf_buffer_flush (file->next);
+
+  __printf_buffer_as_file_switch_to_buffer (file);
+
+  if (!__printf_buffer_has_failed (file->next))
+    return (unsigned char) ch;
+  else
+    return EOF;
+}
+
+static size_t
+__printf_buffer_as_file_xsputn (FILE *fp, const void *buf, size_t len)
+{
+  struct __printf_buffer_as_file *file = (struct __printf_buffer_as_file *) fp;
+
+  __printf_buffer_as_file_commit (file);
+
+  /* Copy the data.  */
+  __printf_buffer_write (file->next, buf, len);
+
+  __printf_buffer_as_file_switch_to_buffer (file);
+
+  if (!__printf_buffer_has_failed (file->next))
+    return len;
+  else
+    /* We may actually have written something.  But the stream is
+       corrupted in this case anyway, so try not to divine the write
+       count here.  */
+    return 0;
+}
+
+static const struct _IO_jump_t _IO_printf_buffer_as_file_jumps libio_vtable =
+{
+  JUMP_INIT_DUMMY,
+  JUMP_INIT(finish, NULL),
+  JUMP_INIT(overflow, __printf_buffer_as_file_overflow),
+  JUMP_INIT(underflow, NULL),
+  JUMP_INIT(uflow, NULL),
+  JUMP_INIT(pbackfail, NULL),
+  JUMP_INIT(xsputn, __printf_buffer_as_file_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)
+};
+
+void
+__printf_buffer_as_file_init (struct __printf_buffer_as_file *file,
+                              struct __printf_buffer *next)
+{
+  file->stream._lock = NULL;
+  _IO_no_init (&file->stream, _IO_USER_LOCK, -1, NULL, NULL);
+  file->vtable = &_IO_printf_buffer_as_file_jumps;
+
+  /* Set up the write buffer from the next buffer.  */
+  file->next = next;
+  __printf_buffer_as_file_switch_to_buffer (file);
+
+  /* Mark the read area as inactive, by making all pointers equal.  */
+  file->stream._IO_read_base = file->stream._IO_write_base;
+  file->stream._IO_read_ptr = file->stream._IO_write_base;
+  file->stream._IO_read_end = file->stream._IO_write_base;
+}
+
+bool
+__printf_buffer_as_file_terminate (struct __printf_buffer_as_file *file)
+{
+  if (file->stream._flags & _IO_ERR_SEEN)
+    return false;
+  else
+    {
+      __printf_buffer_as_file_commit (file);
+      return true;
+    }
+}
diff --git a/stdio-common/printf_buffer_as_file.h b/stdio-common/printf_buffer_as_file.h
new file mode 100644
index 0000000000..8ac707f2d0
--- /dev/null
+++ b/stdio-common/printf_buffer_as_file.h
@@ -0,0 +1,87 @@ 
+/* FILE * interface to a struct __*printf_buffer.
+   Copyright (C) 2022 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
+   <https://www.gnu.org/licenses/>.  */
+
+/* Registered printf format specifier callbacks produce data via a
+   FILE * stream.  struct __printf_buffer_as_file enables vfprintf to
+   create a suitable stdio stream.  Likewise struct
+   __wprintf_buffer_as_file for vfwprintf.  */
+
+#ifndef PRINTF_BUFFER_AS_FILE_H
+#define PRINTF_BUFFER_AS_FILE_H
+
+#include <libio/libioP.h>
+
+struct __printf_buffer;
+
+struct __printf_buffer_as_file
+{
+  /* Interface to libio.  */
+  FILE stream;
+  const struct _IO_jump_t *vtable;
+
+  /* Pointer to the underlying buffer.  */
+  struct __printf_buffer *next;
+};
+
+/* Initialization *FP so that data written to its FILE * stream ends
+   up in NEXT.  */
+void __printf_buffer_as_file_init (struct __printf_buffer_as_file *fp,
+                                   struct __printf_buffer *next)
+  attribute_hidden;
+
+/* Returns the FILE * that can be used to write data to the
+   buffer.  */
+static inline FILE *
+__printf_buffer_as_file_get (struct __printf_buffer_as_file *file)
+{
+  return &file->stream;
+}
+
+/* Transfers all pending data from the FILE * to the underlying
+   buffer.  Returns true if there have been no errors.  */
+bool __printf_buffer_as_file_terminate (struct __printf_buffer_as_file *)
+  attribute_hidden;
+
+/* Wide variant follows.  */
+
+struct __wprintf_buffer;
+struct __wprintf_buffer_as_file
+{
+  /* Interface to libio.  */
+  FILE stream;
+  const struct _IO_jump_t *vtable;
+  struct _IO_wide_data wide_stream;
+
+  /* Pointer to the underlying buffer.  */
+  struct __wprintf_buffer *next;
+};
+
+void __wprintf_buffer_as_file_init (struct __wprintf_buffer_as_file *fp,
+                                    struct __wprintf_buffer *next)
+  attribute_hidden;
+
+static inline FILE *
+__wprintf_buffer_as_file_get (struct __wprintf_buffer_as_file *file)
+{
+  return &file->stream;
+}
+
+bool __wprintf_buffer_as_file_terminate (struct __wprintf_buffer_as_file *)
+  attribute_hidden;
+
+#endif /* PRINTF_BUFFER_AS_FILE_H */
diff --git a/stdio-common/printf_buffer_done.c b/stdio-common/printf_buffer_done.c
new file mode 100644
index 0000000000..5f3df1daad
--- /dev/null
+++ b/stdio-common/printf_buffer_done.c
@@ -0,0 +1,21 @@ 
+/* Final status reporting for struct __printf_buffer.  Multibyte version.
+   Copyright (C) 2022 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
+   <https://www.gnu.org/licenses/>.  */
+
+#include <printf_buffer.h>
+#include "printf_buffer-char.h"
+#include "Xprintf_buffer_done.c"
diff --git a/stdio-common/printf_buffer_flush.c b/stdio-common/printf_buffer_flush.c
new file mode 100644
index 0000000000..9b25c0fde5
--- /dev/null
+++ b/stdio-common/printf_buffer_flush.c
@@ -0,0 +1,42 @@ 
+/* Flush a struct __printf_buffer.  Multibyte version.
+   Copyright (C) 2022 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
+   <https://www.gnu.org/licenses/>.  */
+
+#include <printf_buffer.h>
+
+#include "printf_buffer-char.h"
+#include "Xprintf_buffer_flush.c"
+
+/* The __printf_buffer_flush_* functions are defined together with
+   functions that are pulled in by strong references.  */
+#ifndef SHARED
+# pragma weak __printf_buffer_flush_to_file
+#endif /* !SHARED */
+
+static void
+__printf_buffer_do_flush (struct __printf_buffer *buf)
+{
+  switch (buf->mode)
+    {
+    case __printf_buffer_mode_failed:
+      return;
+    case __printf_buffer_mode_to_file:
+      __printf_buffer_flush_to_file ((struct __printf_buffer_to_file *) buf);
+      return;
+    }
+  __builtin_trap ();
+}
diff --git a/stdio-common/printf_buffer_pad_1.c b/stdio-common/printf_buffer_pad_1.c
new file mode 100644
index 0000000000..c4b9a7af56
--- /dev/null
+++ b/stdio-common/printf_buffer_pad_1.c
@@ -0,0 +1,21 @@ 
+/* Write repeated characters to struct __printf_buffer.  Multibyte version.
+   Copyright (C) 2022 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
+   <https://www.gnu.org/licenses/>.  */
+
+#include <printf_buffer.h>
+#include "printf_buffer-char.h"
+#include "Xprintf_buffer_pad_1.c"
diff --git a/stdio-common/printf_buffer_putc_1.c b/stdio-common/printf_buffer_putc_1.c
new file mode 100644
index 0000000000..a15e8d16d1
--- /dev/null
+++ b/stdio-common/printf_buffer_putc_1.c
@@ -0,0 +1,21 @@ 
+/* Overflow character write function for struct __printf_buffer.
+   Copyright (C) 2022 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
+   <https://www.gnu.org/licenses/>.  */
+
+#include <printf_buffer.h>
+#include "printf_buffer-char.h"
+#include "Xprintf_buffer_putc_1.c"
diff --git a/stdio-common/printf_buffer_puts_1.c b/stdio-common/printf_buffer_puts_1.c
new file mode 100644
index 0000000000..ed97fd6b3f
--- /dev/null
+++ b/stdio-common/printf_buffer_puts_1.c
@@ -0,0 +1,21 @@ 
+/* String write function for struct __printf_buffer.  Multibyte version.
+   Copyright (C) 2022 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
+   <https://www.gnu.org/licenses/>.  */
+
+#include <printf_buffer.h>
+#include "printf_buffer-char.h"
+#include "Xprintf_buffer_puts_1.c"
diff --git a/stdio-common/printf_buffer_to_file.c b/stdio-common/printf_buffer_to_file.c
new file mode 100644
index 0000000000..3576771f70
--- /dev/null
+++ b/stdio-common/printf_buffer_to_file.c
@@ -0,0 +1,122 @@ 
+/* Multibyte printf buffers writing data to a FILE * stream.
+   Copyright (C) 2022 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
+   <https://www.gnu.org/licenses/>.  */
+
+#include <printf_buffer_to_file.h>
+
+#include <assert.h>
+#include <array_length.h>
+#include <libio/libioP.h>
+
+/* Switch to the file buffer if possible.  If the file has write_ptr
+   == write_end, use the stage buffer instead.  */
+void
+__printf_buffer_to_file_switch (struct __printf_buffer_to_file *buf)
+{
+  if (buf->fp->_IO_write_ptr < buf->fp->_IO_write_end)
+    {
+      /* buf->fp has a buffer associated with it, so write directly to
+         it from now on.  */
+      buf->base.write_ptr = buf->fp->_IO_write_ptr;
+      buf->base.write_end = buf->fp->_IO_write_end;
+    }
+  else
+    {
+      /* Use the staging area if no buffer is available in buf->fp.  */
+      buf->base.write_ptr = buf->stage;
+      buf->base.write_end = array_end (buf->stage);
+    }
+
+  buf->base.write_base = buf->base.write_ptr;
+}
+
+void
+__printf_buffer_flush_to_file (struct __printf_buffer_to_file *buf)
+{
+  /* The bytes in the buffer are always consumed.  */
+  buf->base.written += buf->base.write_ptr - buf->base.write_base;
+
+  if (buf->base.write_end == array_end (buf->stage))
+    {
+      /* If the stage buffer is used, make a copy into the file.  The
+         stage buffer is always consumed fully, even if just partially
+         written, to ensure that the file stream has all the data.  */
+      size_t count = buf->base.write_ptr - buf->stage;
+      if ((size_t) _IO_sputn (buf->fp, buf->stage, count) != count)
+        {
+          __printf_buffer_mark_failed (&buf->base);
+          return;
+        }
+      /* buf->fp may have a buffer now.  */
+      __printf_buffer_to_file_switch (buf);
+      return;
+    }
+  else if (buf->base.write_end == buf->stage + 1)
+    {
+      /* Special one-character buffer case.  This is used to avoid
+         flush-only overflow below.  */
+      if (buf->base.write_ptr == buf->base.write_end)
+        {
+          if (__overflow (buf->fp, (unsigned char) *buf->stage) == EOF)
+            {
+              __printf_buffer_mark_failed (&buf->base);
+              return;
+            }
+          __printf_buffer_to_file_switch (buf);
+        }
+      /* Else there is nothing to write.  */
+      return;
+    }
+
+  /* We have written directly into the buf->fp buffer.  */
+  assert (buf->base.write_end == buf->fp->_IO_write_end);
+
+  /* Mark the bytes as written.  */
+  buf->fp->_IO_write_ptr = buf->base.write_ptr;
+
+  if (buf->base.write_ptr == buf->base.write_end)
+    {
+      /* The buffer in buf->fp has been filled.  This should just call
+         __overflow (buf->fp, EOF), but flush-only overflow is obscure
+         and not always correctly implemented.  See bug 28949.  Be
+         conservative and switch to a one-character buffer instead, to
+         obtain one more character for a regular __overflow call.  */
+      buf->base.write_ptr = buf->stage;
+      buf->base.write_end = buf->stage + 1;
+    }
+  /* The bytes in the file stream were already marked as written above.  */
+
+  buf->base.write_base = buf->base.write_ptr;
+}
+
+void
+__printf_buffer_to_file_init (struct __printf_buffer_to_file *buf, FILE *fp)
+{
+  __printf_buffer_init (&buf->base, buf->stage, array_length (buf->stage),
+                        __printf_buffer_mode_to_file);
+  buf->fp = fp;
+  __printf_buffer_to_file_switch (buf);
+}
+
+int
+__printf_buffer_to_file_done (struct __printf_buffer_to_file *buf)
+{
+  if (__printf_buffer_has_failed (&buf->base))
+    return -1;
+  __printf_buffer_flush_to_file (buf);
+  return __printf_buffer_done (&buf->base);
+}
diff --git a/stdio-common/printf_buffer_to_file.h b/stdio-common/printf_buffer_to_file.h
new file mode 100644
index 0000000000..97ec1d6d95
--- /dev/null
+++ b/stdio-common/printf_buffer_to_file.h
@@ -0,0 +1,57 @@ 
+/* Multibyte and wide printf buffers writing data to a FILE * stream.
+   Copyright (C) 2022 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
+   <https://www.gnu.org/licenses/>.  */
+
+#ifndef PRINTF_BUFFER_TO_FILE_H
+#define PRINTF_BUFFER_TO_FILE_H
+
+#include <bits/types/FILE.h>
+#include <printf_buffer.h>
+
+struct __printf_buffer_to_file
+{
+  struct __printf_buffer base;
+  FILE *fp;
+
+  /* Staging buffer.  All data goes through this buffer before
+     reaching the fp stream.  (Any buffer in fp is not yet used.)  */
+  char stage[128];
+};
+
+/* Initializes *BUF to write data to FP.  */
+void __printf_buffer_to_file_init (struct __printf_buffer_to_file *buf,
+                                   FILE *fp) attribute_hidden;
+
+/* Transfers any pending data in BUF to BUF->FP.  The return value
+   follows the printf convention (number bytes written; or -1 for error).  */
+int __printf_buffer_to_file_done (struct __printf_buffer_to_file *buf)
+  attribute_hidden;
+
+/* Wide version below.  */
+
+struct __wprintf_buffer_to_file
+{
+  struct __wprintf_buffer base;
+  FILE *fp;
+  wchar_t stage[128];
+};
+void __wprintf_buffer_to_file_init (struct __wprintf_buffer_to_file *buf,
+                                    FILE *fp) attribute_hidden;
+int __wprintf_buffer_to_file_done (struct __wprintf_buffer_to_file *buf)
+  attribute_hidden;
+
+#endif /* PRINTF_BUFFER_TO_FILE_H */
diff --git a/stdio-common/printf_buffer_write.c b/stdio-common/printf_buffer_write.c
new file mode 100644
index 0000000000..1038f69063
--- /dev/null
+++ b/stdio-common/printf_buffer_write.c
@@ -0,0 +1,21 @@ 
+/* Blob write function for struct __printf_buffer.  Multibyte version.
+   Copyright (C) 2022 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
+   <https://www.gnu.org/licenses/>.  */
+
+#include <printf_buffer.h>
+#include "printf_buffer-char.h"
+#include "Xprintf_buffer_write.c"
diff --git a/stdio-common/wprintf_buffer_as_file.c b/stdio-common/wprintf_buffer_as_file.c
new file mode 100644
index 0000000000..cd48a7d42a
--- /dev/null
+++ b/stdio-common/wprintf_buffer_as_file.c
@@ -0,0 +1,153 @@ 
+/* FILE * interface to a struct __wprintf_buffer.  Wide version.
+   Copyright (C) 2022 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
+   <https://www.gnu.org/licenses/>.  */
+
+#include <printf_buffer_as_file.h>
+
+#include <assert.h>
+#include <printf_buffer.h>
+
+/* Commit the data directly written through the stdio stream.  */
+static void
+__wprintf_buffer_as_file_commit (struct __wprintf_buffer_as_file *file)
+{
+  /* Check that the write pointers in the file stream are consistent
+     with the next buffer.  */
+  assert (file->wide_stream._IO_write_ptr >= file->next->write_ptr);
+  assert (file->wide_stream._IO_write_ptr <= file->next->write_end);
+  assert (file->wide_stream._IO_write_base == file->next->write_base);
+  assert (file->wide_stream._IO_write_end == file->next->write_end);
+
+  file->next->write_ptr = file->wide_stream._IO_write_ptr;
+}
+
+/* Pointer the FILE * write buffer into the active struct __wprintf_buffer
+   area.  */
+static void
+__wprintf_buffer_as_file_switch_to_buffer (struct __wprintf_buffer_as_file *file)
+{
+  file->wide_stream._IO_write_base = file->next->write_base;
+  file->wide_stream._IO_write_ptr = file->next->write_ptr;
+  file->wide_stream._IO_write_end = file->next->write_end;
+}
+
+/* Only a small subset of the vtable functions is implemented here,
+   following _IO_obstack_jumps.  */
+
+static wint_t
+__wprintf_buffer_as_file_overflow (FILE *fp, int ch)
+{
+  struct __wprintf_buffer_as_file *file
+    = (struct __wprintf_buffer_as_file *) fp;
+
+  __wprintf_buffer_as_file_commit (file);
+
+  /* EOF means only a flush is requested.   */
+  if (ch != WEOF)
+    __wprintf_buffer_putc (file->next, ch);
+  else
+    ch = 0;
+
+  /* Ensure that flushing actually produces room.  */
+  if (!__wprintf_buffer_has_failed (file->next)
+      && file->next->write_ptr == file->next->write_end)
+    __wprintf_buffer_flush (file->next);
+
+  __wprintf_buffer_as_file_switch_to_buffer (file);
+
+  if (!__wprintf_buffer_has_failed (file->next))
+    return (unsigned char) ch;
+  else
+    return WEOF;
+}
+
+static size_t
+__wprintf_buffer_as_file_xsputn (FILE *fp, const void *buf, size_t len)
+{
+  struct __wprintf_buffer_as_file *file
+    = (struct __wprintf_buffer_as_file *) fp;
+
+  __wprintf_buffer_as_file_commit (file);
+
+  /* Copy the data.  */
+  __wprintf_buffer_write (file->next, buf, len);
+
+  __wprintf_buffer_as_file_switch_to_buffer (file);
+
+  if (!__wprintf_buffer_has_failed (file->next))
+    return len;
+  else
+    /* We may actually have written something.  But the stream is
+       corrupted in this case anyway, so try not to divine the write
+       count here.  */
+    return 0;
+}
+
+static const struct _IO_jump_t _IO_wprintf_buffer_as_file_jumps libio_vtable =
+{
+  JUMP_INIT_DUMMY,
+  JUMP_INIT(finish, NULL),
+  JUMP_INIT(overflow, (_IO_overflow_t) __wprintf_buffer_as_file_overflow),
+  JUMP_INIT(underflow, NULL),
+  JUMP_INIT(uflow, NULL),
+  JUMP_INIT(pbackfail, NULL),
+  JUMP_INIT(xsputn, __wprintf_buffer_as_file_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)
+};
+
+void
+__wprintf_buffer_as_file_init (struct __wprintf_buffer_as_file *file,
+                               struct __wprintf_buffer *next)
+{
+  file->stream._lock = NULL;
+  _IO_no_init (&file->stream, _IO_USER_LOCK, 0, &file->wide_stream,
+               &_IO_wprintf_buffer_as_file_jumps);
+  _IO_fwide (&file->stream, 1);
+
+  /* Set up the write buffer from the next buffer.  */
+  file->next = next;
+  __wprintf_buffer_as_file_switch_to_buffer (file);
+
+  /* Mark the read area as inactive, by making all pointers equal.  */
+  file->stream._IO_read_base = file->stream._IO_write_base;
+  file->stream._IO_read_ptr = file->stream._IO_write_base;
+  file->stream._IO_read_end = file->stream._IO_write_base;
+}
+
+bool
+__wprintf_buffer_as_file_terminate (struct __wprintf_buffer_as_file *file)
+{
+  if (file->stream._flags & _IO_ERR_SEEN)
+    return false;
+  else
+    {
+      __wprintf_buffer_as_file_commit (file);
+      return true;
+    }
+}
diff --git a/stdio-common/wprintf_buffer_done.c b/stdio-common/wprintf_buffer_done.c
new file mode 100644
index 0000000000..60eac03955
--- /dev/null
+++ b/stdio-common/wprintf_buffer_done.c
@@ -0,0 +1,21 @@ 
+/* Final status reporting for struct __wprintf_buffer.  Wide version.
+   Copyright (C) 2022 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
+   <https://www.gnu.org/licenses/>.  */
+
+#include <printf_buffer.h>
+#include "printf_buffer-wchar_t.h"
+#include "Xprintf_buffer_done.c"
diff --git a/stdio-common/wprintf_buffer_flush.c b/stdio-common/wprintf_buffer_flush.c
new file mode 100644
index 0000000000..2d91095cca
--- /dev/null
+++ b/stdio-common/wprintf_buffer_flush.c
@@ -0,0 +1,36 @@ 
+/* Flush a struct __wprintf_buffer.  Wide version.
+   Copyright (C) 2022 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
+   <https://www.gnu.org/licenses/>.  */
+
+#include <printf_buffer.h>
+
+#include "printf_buffer-wchar_t.h"
+#include "Xprintf_buffer_flush.c"
+
+static void
+__wprintf_buffer_do_flush (struct __wprintf_buffer *buf)
+{
+  switch (buf->mode)
+    {
+    case __wprintf_buffer_mode_failed:
+      return;
+    case __wprintf_buffer_mode_to_file:
+      __wprintf_buffer_flush_to_file ((struct __wprintf_buffer_to_file *) buf);
+      return;
+    }
+  __builtin_trap ();
+}
diff --git a/stdio-common/wprintf_buffer_pad_1.c b/stdio-common/wprintf_buffer_pad_1.c
new file mode 100644
index 0000000000..9c91126f42
--- /dev/null
+++ b/stdio-common/wprintf_buffer_pad_1.c
@@ -0,0 +1,21 @@ 
+/* Write repeated characters to struct __wprintf_buffer.  Wide version.
+   Copyright (C) 2022 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
+   <https://www.gnu.org/licenses/>.  */
+
+#include <printf_buffer.h>
+#include "printf_buffer-wchar_t.h"
+#include "Xprintf_buffer_pad_1.c"
diff --git a/stdio-common/wprintf_buffer_putc_1.c b/stdio-common/wprintf_buffer_putc_1.c
new file mode 100644
index 0000000000..b1af68e721
--- /dev/null
+++ b/stdio-common/wprintf_buffer_putc_1.c
@@ -0,0 +1,21 @@ 
+/* Overflow character write function for struct __wprintf_buffer.  Wide version.
+   Copyright (C) 2022 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
+   <https://www.gnu.org/licenses/>.  */
+
+#include <printf_buffer.h>
+#include "printf_buffer-wchar_t.h"
+#include "Xprintf_buffer_putc_1.c"
diff --git a/stdio-common/wprintf_buffer_puts_1.c b/stdio-common/wprintf_buffer_puts_1.c
new file mode 100644
index 0000000000..039aebb56e
--- /dev/null
+++ b/stdio-common/wprintf_buffer_puts_1.c
@@ -0,0 +1,21 @@ 
+/* String write function struct __wprintf_buffer.  Wide version.
+   Copyright (C) 2022 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
+   <https://www.gnu.org/licenses/>.  */
+
+#include <printf_buffer.h>
+#include "printf_buffer-wchar_t.h"
+#include "Xprintf_buffer_puts_1.c"
diff --git a/stdio-common/wprintf_buffer_to_file.c b/stdio-common/wprintf_buffer_to_file.c
new file mode 100644
index 0000000000..ac936fab6c
--- /dev/null
+++ b/stdio-common/wprintf_buffer_to_file.c
@@ -0,0 +1,55 @@ 
+/* Wide printf buffers writing data to a FILE *.
+   Copyright (C) 2022 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
+   <https://www.gnu.org/licenses/>.  */
+
+/* This implementation is not optimized (unlikely the multibyte
+   implementation) and always writes to the temporary buffer first.  */
+
+#include <printf_buffer_to_file.h>
+
+#include <array_length.h>
+#include <libio/libioP.h>
+
+void
+__wprintf_buffer_flush_to_file (struct __wprintf_buffer_to_file *buf)
+{
+  size_t count = buf->base.write_ptr - buf->stage;
+  if ((size_t) _IO_sputn (buf->fp, buf->stage, count) != count)
+    {
+      __wprintf_buffer_mark_failed (&buf->base);
+      return;
+    }
+  buf->base.written += count;
+  buf->base.write_ptr = buf->stage;
+}
+
+void
+__wprintf_buffer_to_file_init (struct __wprintf_buffer_to_file *buf, FILE *fp)
+{
+  __wprintf_buffer_init (&buf->base, buf->stage, array_length (buf->stage),
+                         __wprintf_buffer_mode_to_file);
+  buf->fp = fp;
+}
+
+int
+__wprintf_buffer_to_file_done (struct __wprintf_buffer_to_file *buf)
+{
+  if (__wprintf_buffer_has_failed (&buf->base))
+    return -1;
+  __wprintf_buffer_flush_to_file (buf);
+  return __wprintf_buffer_done (&buf->base);
+}
diff --git a/stdio-common/wprintf_buffer_write.c b/stdio-common/wprintf_buffer_write.c
new file mode 100644
index 0000000000..06b53c74c3
--- /dev/null
+++ b/stdio-common/wprintf_buffer_write.c
@@ -0,0 +1,21 @@ 
+/* Blob write function for struct __wprintf_buffer.  Wide version.
+   Copyright (C) 2022 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
+   <https://www.gnu.org/licenses/>.  */
+
+#include <printf_buffer.h>
+#include "printf_buffer-wchar_t.h"
+#include "Xprintf_buffer_write.c"