[06/25] Add struct scratch_buffer and its internal helper functions
Commit Message
These will be used from NSS modules, so they have to be exported.
---
include/scratch_buffer.h | 133 ++++++++++++++++++
malloc/Makefile | 6 +-
malloc/Versions | 5 +
malloc/scratch_buffer_grow.c | 52 +++++++
malloc/scratch_buffer_grow_preserve.c | 65 +++++++++
malloc/scratch_buffer_set_array_size.c | 61 ++++++++
malloc/tst-scratch_buffer.c | 155 +++++++++++++++++++++
sysdeps/unix/sysv/linux/aarch64/libc.abilist | 5 +
sysdeps/unix/sysv/linux/alpha/libc.abilist | 5 +
sysdeps/unix/sysv/linux/arm/libc.abilist | 5 +
sysdeps/unix/sysv/linux/hppa/libc.abilist | 5 +
sysdeps/unix/sysv/linux/i386/libc.abilist | 5 +
sysdeps/unix/sysv/linux/ia64/libc.abilist | 5 +
sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist | 5 +
sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist | 5 +
sysdeps/unix/sysv/linux/microblaze/libc.abilist | 5 +
.../unix/sysv/linux/mips/mips32/fpu/libc.abilist | 5 +
.../unix/sysv/linux/mips/mips32/nofpu/libc.abilist | 5 +
.../unix/sysv/linux/mips/mips64/n32/libc.abilist | 5 +
.../unix/sysv/linux/mips/mips64/n64/libc.abilist | 5 +
sysdeps/unix/sysv/linux/nios2/libc.abilist | 5 +
.../sysv/linux/powerpc/powerpc32/fpu/libc.abilist | 5 +
.../linux/powerpc/powerpc32/nofpu/libc.abilist | 5 +
.../unix/sysv/linux/powerpc/powerpc64/libc.abilist | 5 +
sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist | 5 +
sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist | 5 +
sysdeps/unix/sysv/linux/sh/libc.abilist | 5 +
sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist | 5 +
sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist | 5 +
.../sysv/linux/tile/tilegx/tilegx32/libc.abilist | 5 +
.../sysv/linux/tile/tilegx/tilegx64/libc.abilist | 5 +
sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist | 5 +
sysdeps/unix/sysv/linux/x86_64/64/libc.abilist | 5 +
sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist | 5 +
34 files changed, 610 insertions(+), 2 deletions(-)
create mode 100644 include/scratch_buffer.h
create mode 100644 malloc/scratch_buffer_grow.c
create mode 100644 malloc/scratch_buffer_grow_preserve.c
create mode 100644 malloc/scratch_buffer_set_array_size.c
create mode 100644 malloc/tst-scratch_buffer.c
Comments
Florian Weimer <fweimer@redhat.com> writes:
> +#pragma once
Don't use pragma once.
> + GLIBC_2.22 {
> + __libc_scratch_buffer_grow;
> + __libc_scratch_buffer_grow_preserve;
> + __libc_scratch_buffer_set_array_size;
Are they supposed to be used outside of glibc? Since scratch_buffer.h
isn't installed, it doesn't look like.
Andreas.
On 03/02/2015 10:35 AM, Andreas Schwab wrote:
> Florian Weimer <fweimer@redhat.com> writes:
>
>> +#pragma once
>
> Don't use pragma once.
Why? The GCC implementation was fixed ages ago, and it's not an
installed header file.
>> + GLIBC_2.22 {
>> + __libc_scratch_buffer_grow;
>> + __libc_scratch_buffer_grow_preserve;
>> + __libc_scratch_buffer_set_array_size;
>
> Are they supposed to be used outside of glibc? Since scratch_buffer.h
> isn't installed, it doesn't look like.
See the commit message. nscd and some of the NSS modules need these
symbols.
Florian Weimer <fweimer@redhat.com> writes:
> See the commit message. nscd and some of the NSS modules need these
> symbols.
Then use GLIBC_PRIVATE.
Andreas.
On Mon, Mar 02, 2015 at 10:40:46AM +0100, Florian Weimer wrote:
> On 03/02/2015 10:35 AM, Andreas Schwab wrote:
> > Florian Weimer <fweimer@redhat.com> writes:
> >
> >> +#pragma once
> >
> > Don't use pragma once.
>
> Why? The GCC implementation was fixed ages ago, and it's not an
> installed header file.
If for no other reason, cargo-culting. This is the wrong way to
achieve multiple-include protection (it's non-portable and has a
perfectly portable way to do the same thing) and programmers will see
and copy it into non-glibc code.
Rich
On 03/02/2015 06:42 PM, Rich Felker wrote:
> On Mon, Mar 02, 2015 at 10:40:46AM +0100, Florian Weimer wrote:
>> On 03/02/2015 10:35 AM, Andreas Schwab wrote:
>>> Florian Weimer <fweimer@redhat.com> writes:
>>>
>>>> +#pragma once
>>>
>>> Don't use pragma once.
>>
>> Why? The GCC implementation was fixed ages ago, and it's not an
>> installed header file.
>
> If for no other reason, cargo-culting. This is the wrong way to
> achieve multiple-include protection (it's non-portable and has a
> perfectly portable way to do the same thing)
It's portable to anything except Solaris Studio. It's supported by the
EDG front end, so you have to do actual work to build a compiler that
doesn't support it. (Not that it matters in this case because it is an
internal header anyway.)
Compare this to most C99 or even C11 features, which keep most of our
code in a world of its own (which is fine from my point of view,
pretend-portability without actual users doesn't work so well in practice).
And no, there is nothing else which provides a similar capability to
#pragma once, in a way that is equally portable in practice.
> and programmers will see and copy it into non-glibc code.
And that's a good thing because it avoids real bugs.
But of course I'll add a traditional #include guard. Okay to commit
with such a change?
On 03/01/2015 06:28 AM, Florian Weimer wrote:
> + The non-inlined functions are implemented in such a way that it is
> + possible to change the size of the pre-allocated buffer without
> + impacting ABI.
Why does this matter? The ABI is not exported to users, right? The
comment should explain why the ABI business matters.
>
> +#define SCRATCH_BUFFER_ALIGNMENT \
> + __attribute__ ((aligned (__alignof__ (union {void *p; double d;}))))
This should use __attribute__ ((aligned (alignof (max_align_t)))), at
least on C11 platforms.
> +struct scratch_buffer {
> + void *data; /* Pointer to the beginning of the scratch area. */
> + size_t length; /* Allocated space at the data pointer, in bytes. */
> + char __space[1024 - sizeof (size_t)] SCRATCH_BUFFER_ALIGNMENT;
> +} SCRATCH_BUFFER_ALIGNMENT;
Why are there two SCRATCH_BUFFER_ALIGNMENTs there? One suffices, no?
Why 1024? And why "- sizeof (size_t)"? A comment should say.
> +/* Grows *BUFFER by some arbitrary amount. The buffer contents is NOT
> + preserved. Returns true on success, fails on allocation failure
> + (in which case the old buffer is freed). On success, the new
> + buffer is slightly larger (by at least 16 bytes) than the previous
> + size. On failure, *BUFFER is deallocated, but remains in a
> + free-able state. */
Why 16? The comment should say.
Shouldn't the caller have some say on how big to grow the buffer? I can
see a caller knowing how much space it'll need. As things stand, such a
caller needs to repeatedly grow the buffer until it's big enough, which
is awkward. Hmm, I see that scratch_buffer_set_array_size lets one set
the array size to any value, but there's no variant of
scratch_buffer_set_array_size that preserves the buffer.
On failure this function sets errno, right? The comment should say this.
> +/* Grows *BUFFER so that it can store at least NELEM elemnts of SIZE
> + bytes. The buffer contents is NOT preserved. Returns true on
> + success, fails on allocation failure (in which case the old buffer
> + is freed, but *BUFFER remains a free-able state). */
Similar remark about errno. Also, is this function allowed to shrink
*BUFFER? Also, is SIZE allowed to be zero? The comment should say.
> + size_t new_length = buffer->length * 2;
In Gnulib we originally did it this way, but nowadays we grow by a
factor of 1.5 (new_length = old_length + old_length / 2 + 1) rather than
by a factor of 2, as this was less jerky for large buffers. Perhaps do
something similar here?
> + size_t size_max_square_root = ((size_t)1) << (sizeof (size_t) * 4);
> + /* Avoid overflow check if both values are small. */
> + if (nelem >= size_max_square_root || size >= size_max_square_root)
"4" is too much a mystery here and should be spelled out as CHAR_BIT /
2. Also, this is smaller and faster when done this way:
if ((nelem | size) >> (sizeof (size_t) * CHAR_BIT / 2) != 0)
> + if (nelem != 0 && size > SIZE_MAX / nelem)
This is a run-time integer division. It can be a bit faster to do the
division at compile-time. Gnulib does this by computing 'SIZE_MAX /
size' in an inline function; 'size' is normally a constant (and must be
nonzero in Gnulib, which is a reasonable restriction here too). With
this optimization, Gnulib doesn't need to mess with size square roots
either, another minor performance win.
Paul,
thank you for your extensive comments.
On 03/02/2015 08:33 PM, Paul Eggert wrote:
> On 03/01/2015 06:28 AM, Florian Weimer wrote:
>> + The non-inlined functions are implemented in such a way that it is
>> + possible to change the size of the pre-allocated buffer without
>> + impacting ABI.
>
> Why does this matter? The ABI is not exported to users, right? The
> comment should explain why the ABI business matters.
It helps slightly with online updates. I don't want to introduce any
additional problems for them if we fine-tune the buffer size.
>> +#define SCRATCH_BUFFER_ALIGNMENT \
>> + __attribute__ ((aligned (__alignof__ (union {void *p; double d;}))))
>
> This should use __attribute__ ((aligned (alignof (max_align_t)))), at
> least on C11 platforms.
Hmm. Isn't intmax_t enough?
>> +struct scratch_buffer {
>> + void *data; /* Pointer to the beginning of the scratch area. */
>> + size_t length; /* Allocated space at the data pointer, in bytes. */
>> + char __space[1024 - sizeof (size_t)] SCRATCH_BUFFER_ALIGNMENT;
>> +} SCRATCH_BUFFER_ALIGNMENT;
>
> Why are there two SCRATCH_BUFFER_ALIGNMENTs there? One suffices, no?
Right.
> Why 1024?
That's the magic constant most of the existing code (in NSS) used.
> And why "- sizeof (size_t)"? A comment should say.
It turns into - 2 * sizeof (size_t) after doubling for the first heap
allocation, which is the current size of the malloc metadata. I'll drop
it, it's probably too cute. If we want to optimize the malloc case,
checking for lack of malloc overloading and then extending transparently
to malloc_usable_size bytes (if no malloc override is in place) seems
more reasonable.
>> +/* Grows *BUFFER by some arbitrary amount. The buffer contents is NOT
>> + preserved. Returns true on success, fails on allocation failure
>> + (in which case the old buffer is freed). On success, the new
>> + buffer is slightly larger (by at least 16 bytes) than the previous
>> + size. On failure, *BUFFER is deallocated, but remains in a
>> + free-able state. */
>
> Why 16? The comment should say.
I bumped it to 512 and added an explanation:
scratch_buffer_grow and scratch_buffer_grow_preserve are guaranteed
to grow the buffer by at least 512 bytes. This means that when
using the scratch buffer as a backing store for a non-character
array whose element size, in bytes, is 512 or smaller, the scratch
buffer only has to grown once to make room for at least one more
element.
> Shouldn't the caller have some say on how big to grow the buffer? I can
> see a caller knowing how much space it'll need.
Most callers don't. And for the very few remaining ones, there is
scratch_buffer_set_array_size.
> As things stand, such a
> caller needs to repeatedly grow the buffer until it's big enough, which
> is awkward.
It's the main use case in NSS because that's the way gethostbyname_r and
friends were specified. You don't get to know the required buffer size
on failure.
> Hmm, I see that scratch_buffer_set_array_size lets one set
> the array size to any value, but there's no variant of
> scratch_buffer_set_array_size that preserves the buffer.
It has not come up yet.
> On failure this function sets errno, right? The comment should say this.
Right.
>> +/* Grows *BUFFER so that it can store at least NELEM elemnts of SIZE
>> + bytes. The buffer contents is NOT preserved. Returns true on
>> + success, fails on allocation failure (in which case the old buffer
>> + is freed, but *BUFFER remains a free-able state). */
>
> Similar remark about errno. Also, is this function allowed to shrink
> *BUFFER? Also, is SIZE allowed to be zero? The comment should say.
What about this?
/* Grows *BUFFER so that it can store at least NELEM elements of SIZE
bytes. The buffer contents is NOT preserved. Both NELEM and SIZE
can be zero. It is unspecified wheter this function can reduce the
array size. It returns true on success, false on allocation
failure (in which case the old buffer is freed, but *BUFFER remains
a free-able state, and errno is set). */
>> + size_t new_length = buffer->length * 2;
>
> In Gnulib we originally did it this way, but nowadays we grow by a
> factor of 1.5 (new_length = old_length + old_length / 2 + 1) rather than
> by a factor of 2, as this was less jerky for large buffers. Perhaps do
> something similar here?
I think we should leave the fine-tuning to a later stage. See also the
comment above about malloc_usable_size.
The original code used a growth factor of 3 during the alloca phase, and
2 during the malloc phase. (After the i386 ABI change for stack
alignment, it's effectively 2 and 2.)
>> + size_t size_max_square_root = ((size_t)1) << (sizeof (size_t) * 4);
>> + /* Avoid overflow check if both values are small. */
>> + if (nelem >= size_max_square_root || size >= size_max_square_root)
>
> "4" is too much a mystery here and should be spelled out as CHAR_BIT /
> 2. Also, this is smaller and faster when done this way:
>
> if ((nelem | size) >> (sizeof (size_t) * CHAR_BIT / 2) != 0)
Thanks, that's indeed nicer.
In the end, it will best what the compiler will be able to turn in a
multiply-with-overflow-check (which most CPUs provide, but not MIPS, so
it was never standardized as part of C). Very recent GCC has
__builtin_umull_overflow, so maybe we can use that one day.
>> + if (nelem != 0 && size > SIZE_MAX / nelem)
>
> This is a run-time integer division. It can be a bit faster to do the
> division at compile-time.
I know. G++ uses a constant which is rounded down to make sure the
constant can be stored directly in the instruction stream on RISC. The
exact value is often very difficult to encode.
> Gnulib does this by computing 'SIZE_MAX /
> size' in an inline function; 'size' is normally a constant (and must be
> nonzero in Gnulib, which is a reasonable restriction here too). With
> this optimization, Gnulib doesn't need to mess with size square roots
> either, another minor performance win.
It's hard to tell what's better here. Even on 32 bit, you need at least
65,536 array elements before the division kicks in. The current callers
do not call this function repeatedly in a loop. That's why I think it's
reasonable to simplify the call site instead. And with
__builtin_umull_overflow, the division will go away eventually anyway.
Empirically, I'm pretty certain the division does not matter
performance-wise. On x86_64 and i386, the old alloca code had an
integer division, too, and no one complained about its performance.
Florian Weimer wrote:
>>> +#define SCRATCH_BUFFER_ALIGNMENT \
>>> + __attribute__ ((aligned (__alignof__ (union {void *p; double d;}))))
>>
>> This should use __attribute__ ((aligned (alignof (max_align_t)))), at
>> least on C11 platforms.
>
> Hmm. Isn't intmax_t enough?
I don't know about all the platforms, but in theory C11 says one should use
max_align_t and that types other than intmax_t might have alignments stricter
than that needed by intmax_t.
> array whose element size, in bytes, is 512 or smaller, the scratch
> buffer only has to grown once to make room for at least one more
grown -> grow
> /* Grows *BUFFER so that it can store at least NELEM elements of SIZE
Grows -> grow
> bytes. The buffer contents is NOT preserved. Both NELEM and SIZE
is NOT -> are NOT
> can be zero. It is unspecified wheter this function can reduce the
wheter -> whether
> array size. It returns true on success, false on allocation
It returns -> Return
> failure (in which case the old buffer is freed, but *BUFFER remains
> a free-able state, and errno is set). */
a free-able -> in a free-able
> Very recent GCC has
> __builtin_umull_overflow, so maybe we can use that one day.
Yes, that's the plan in Gnulib too.
> Empirically, I'm pretty certain the division does not matter
> performance-wise.
Even on platforms where integer division is a subroutine? Perhaps glibc isn't
meant for such platforms, but still....
On Fri, Mar 20, 2015 at 04:08:39PM +0100, Florian Weimer wrote:
>
> >> + size_t new_length = buffer->length * 2;
> >
> > In Gnulib we originally did it this way, but nowadays we grow by a
> > factor of 1.5 (new_length = old_length + old_length / 2 + 1) rather than
> > by a factor of 2, as this was less jerky for large buffers. Perhaps do
> > something similar here?
>
> I think we should leave the fine-tuning to a later stage. See also the
> comment above about malloc_usable_size.
>
> The original code used a growth factor of 3 during the alloca phase, and
> 2 during the malloc phase. (After the i386 ABI change for stack
> alignment, it's effectively 2 and 2.)
>
That does not make much sense. Its mostly used in funcions that cannot
realloc array themself so we double space until it fits. As its
temporary its better to overallocate than try save space.
You could use factor 16 to improve performance. If large buffers are
concern then add treshold after which you use 1.5 but I doubt that you
reach 1MB with functions used.
On 04/03/2015 06:25 PM, Ondřej Bílka wrote:
>> The original code used a growth factor of 3 during the alloca phase, and
>> 2 during the malloc phase. (After the i386 ABI change for stack
>> alignment, it's effectively 2 and 2.)
> That does not make much sense. Its mostly used in funcions that cannot
> realloc array themself so we double space until it fits. As its
> temporary its better to overallocate than try save space.
I suppose that could be true. There are two exceptions: When we cross
the trim threshold, and when we cross the mmap threshold. Both could be
quite bad performance-wise. I think we could come up with a sizing
strategy that avoids that—or change NSS to allocate the buffer at the
point where the size is known (my preference).
However, it's true that it was more important to conserve space when the
code used alloca.
Thanks for your review of five of the other patches; I committed them
with suitable ChangeLog entries.
From: Florian Weimer <fweimer@redhat.com>
Date: Wed, 08 Apr 2015 22:03:05 +0200
> Thanks for your review of five of the other patches; I committed them
> with suitable ChangeLog entries.
I'm getting a lot of warnings from the scratch_buffer test case on
32-bit sparc, which fails the compile during make check.
The first problem is that 1ULL << 32 doesn't fit in a size_t,
therefore gcc complains about the value being implicitly
truncated to an unsigned type.
For that, I think these >=1<<32 value tests should simply be elided on
32-bit systems.
Secondly I get inlining failure warnings for the scratch_buffer_free
calls in do_test(). I do not have a recommendation for how to solve
that. Frankly I think if something can't be inlined, in many cases
that's fine and opaque details of gcc's inlining heuristics shouldn't
fail the build ever.
These warnings failing the build are generally very frustrating, and
they have been so for those of us who have limited time to work on
glibc arch maintainence. We come in to attack a specific problem and
find that we can't even begin to do so because the tree doesn't even
build due to some obscure warning that the developer who installed the
warning causing change doesn't even see.
It's not like we had a one big project to clean up the existing
warnings and then things got better. This is a continual and ongoing
problem that is pervasive and doesn't improve, as new changes getting
installed are causing new warnings for someone all the time.
On 04/09/2015 04:56 AM, David Miller wrote:
> The first problem is that 1ULL << 32 doesn't fit in a size_t,
> therefore gcc complains about the value being implicitly
> truncated to an unsigned type.
>
> For that, I think these >=1<<32 value tests should simply be elided on
> 32-bit systems.
It's just a matter of adding a cast to size_t, at least on i386 with GCC
4.9.2.
> Secondly I get inlining failure warnings for the scratch_buffer_free
> calls in do_test(). I do not have a recommendation for how to solve
> that. Frankly I think if something can't be inlined, in many cases
> that's fine and opaque details of gcc's inlining heuristics shouldn't
> fail the build ever.
What's the exact failure? Does it refuse to inline because it's not a
hot path?
I could add __always_inline, which should address, at the cost of worse
code. Or I could move scratch_buffer_free out of line.
> These warnings failing the build are generally very frustrating, and
> they have been so for those of us who have limited time to work on
> glibc arch maintainence. We come in to attack a specific problem and
> find that we can't even begin to do so because the tree doesn't even
> build due to some obscure warning that the developer who installed the
> warning causing change doesn't even see.
Yes, I cautioned against enabling optimizer-dependent warnings (which
includes certain inline failure warnings) for -Werror because they are a
pain to deal with.
new file mode 100644
@@ -0,0 +1,133 @@
+/* Variable-sized buffer with on-stack default allocation.
+ Copyright (C) 2015 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <http://www.gnu.org/licenses/>. */
+
+#pragma once
+
+/* Scratch buffers with a default stack allocation and fallback to
+ heap allocation. It is expected that this function is used in this
+ way:
+
+ struct scratch_buffer tmpbuf;
+ scratch_buffer_init (&tmpbuf);
+
+ while (!function_that_uses_buffer (tmpbuf.data, tmpbuf.length))
+ if (!scratch_buffer_grow (&tmpbuf))
+ return -1;
+
+ scratch_buffer_free (&tmpbuf);
+ return 0;
+
+ The allocation functions (scratch_buffer_grow,
+ scratch_buffer_grow_preserve, scratch_buffer_set_array_size) make
+ sure that the heap allocation, if any, is freed, so that the code
+ above does not have a memory leak. The buffer still remains in a
+ state that can be deallocated using scratch_buffer_free, so a loop
+ like this is valid as well:
+
+ struct scratch_buffer tmpbuf;
+ scratch_buffer_init (&tmpbuf);
+
+ while (!function_that_uses_buffer (tmpbuf.data, tmpbuf.length))
+ if (!scratch_buffer_grow (&tmpbuf))
+ break;
+
+ scratch_buffer_free (&tmpbuf);
+
+ The non-inlined functions are implemented in such a way that it is
+ possible to change the size of the pre-allocated buffer without
+ impacting ABI.
+*/
+
+#include <stdbool.h>
+#include <stdlib.h>
+
+#define SCRATCH_BUFFER_ALIGNMENT \
+ __attribute__ ((aligned (__alignof__ (union {void *p; double d;}))))
+
+/* Scratch buffer. Must be initialized with scratch_buffer_init
+ before its use. */
+struct scratch_buffer {
+ void *data; /* Pointer to the beginning of the scratch area. */
+ size_t length; /* Allocated space at the data pointer, in bytes. */
+ char __space[1024 - sizeof (size_t)] SCRATCH_BUFFER_ALIGNMENT;
+} SCRATCH_BUFFER_ALIGNMENT;
+
+/* Initializes *BUFFER so that BUFFER->data points to BUFFER->__space
+ and BUFFER->length reflects the available space. */
+static inline void
+scratch_buffer_init (struct scratch_buffer *buffer)
+ __attribute__ ((always_inline));
+static inline void
+scratch_buffer_init (struct scratch_buffer *buffer)
+{
+ buffer->data = buffer->__space;
+ buffer->length = sizeof (buffer->__space);
+}
+
+/* Deallocates *BUFFER (if it was heap-allocated). */
+static inline void
+scratch_buffer_free (struct scratch_buffer *buffer)
+{
+ if (buffer->data != buffer->__space)
+ free (buffer->data);
+}
+
+/* Grows *BUFFER by some arbitrary amount. The buffer contents is NOT
+ preserved. Returns true on success, fails on allocation failure
+ (in which case the old buffer is freed). On success, the new
+ buffer is slightly larger (by at least 16 bytes) than the previous
+ size. On failure, *BUFFER is deallocated, but remains in a
+ free-able state. */
+bool __libc_scratch_buffer_grow (struct scratch_buffer *buffer);
+libc_hidden_proto (__libc_scratch_buffer_grow)
+
+/* Alias for __libc_scratch_buffer_grow. */
+static inline bool
+scratch_buffer_grow (struct scratch_buffer *buffer)
+{
+ return __glibc_likely (__libc_scratch_buffer_grow (buffer));
+}
+
+/* Like __libc_scratch_buffer_grow, but preserves the old buffer
+ contents on success, as a prefix of the new buffer. */
+bool __libc_scratch_buffer_grow_preserve (struct scratch_buffer *buffer);
+libc_hidden_proto (__libc_scratch_buffer_grow_preserve)
+
+/* Alias for __libc_scratch_buffer_grow_preserve. */
+static inline bool
+scratch_buffer_grow_preserve (struct scratch_buffer *buffer)
+{
+ return __glibc_likely (__libc_scratch_buffer_grow_preserve (buffer));
+}
+
+/* Grows *BUFFER so that it can store at least NELEM elemnts of SIZE
+ bytes. The buffer contents is NOT preserved. Returns true on
+ success, fails on allocation failure (in which case the old buffer
+ is freed, but *BUFFER remains a free-able state). */
+bool __libc_scratch_buffer_set_array_size (struct scratch_buffer *buffer,
+ size_t nelem, size_t size);
+libc_hidden_proto (__libc_scratch_buffer_set_array_size)
+
+/* Alias for __libc_scratch_set_array_size. */
+static inline bool
+scratch_buffer_set_array_size (struct scratch_buffer *buffer,
+ size_t nelem, size_t size)
+{
+ return __glibc_likely (__libc_scratch_buffer_set_array_size
+ (buffer, nelem, size));
+}
@@ -27,10 +27,12 @@ headers := $(dist-headers) obstack.h mcheck.h
tests := mallocbug tst-malloc tst-valloc tst-calloc tst-obstack \
tst-mallocstate tst-mcheck tst-mallocfork tst-trim1 \
tst-malloc-usable tst-realloc tst-posix_memalign \
- tst-pvalloc tst-memalign tst-mallopt
+ tst-pvalloc tst-memalign tst-mallopt tst-scratch_buffer
test-srcs = tst-mtrace
-routines = malloc morecore mcheck mtrace obstack
+routines = malloc morecore mcheck mtrace obstack \
+ scratch_buffer_grow scratch_buffer_grow_preserve \
+ scratch_buffer_set_array_size
install-lib := libmcheck.a
non-lib.a := libmcheck.a
@@ -61,6 +61,11 @@ libc {
GLIBC_2.16 {
aligned_alloc;
}
+ GLIBC_2.22 {
+ __libc_scratch_buffer_grow;
+ __libc_scratch_buffer_grow_preserve;
+ __libc_scratch_buffer_set_array_size;
+ }
GLIBC_PRIVATE {
# Internal startup hook for libpthread.
__libc_malloc_pthread_startup;
new file mode 100644
@@ -0,0 +1,52 @@
+/* Variable-sized buffer with on-stack default allocation.
+ Copyright (C) 2015 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <http://www.gnu.org/licenses/>. */
+
+#include <scratch_buffer.h>
+#include <errno.h>
+
+bool
+__libc_scratch_buffer_grow (struct scratch_buffer *buffer)
+{
+ size_t new_length = buffer->length * 2;
+
+ /* Discard old buffer. */
+ scratch_buffer_free (buffer);
+
+ /* Check for overflow. */
+ if (__glibc_unlikely (new_length < buffer->length))
+ {
+ /* Buffer must remain valid to free. */
+ scratch_buffer_init (buffer);
+ __set_errno (ENOMEM);
+ return false;
+ }
+
+ void *new_ptr = malloc (new_length);
+ if (new_ptr == NULL)
+ {
+ /* Buffer must remain valid to free. */
+ scratch_buffer_init (buffer);
+ return false;
+ }
+
+ /* Install new heap-based buffer. */
+ buffer->data = new_ptr;
+ buffer->length = new_length;
+ return true;
+}
+libc_hidden_def (__libc_scratch_buffer_grow);
new file mode 100644
@@ -0,0 +1,65 @@
+/* Variable-sized buffer with on-stack default allocation.
+ Copyright (C) 2015 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <http://www.gnu.org/licenses/>. */
+
+#include <scratch_buffer.h>
+#include <errno.h>
+
+bool
+__libc_scratch_buffer_grow_preserve (struct scratch_buffer *buffer)
+{
+ size_t new_length = 2 * buffer->length;
+ void *new_ptr;
+
+ if (buffer->data == buffer->__space)
+ {
+ /* Move buffer to the heap. No overflow is possible because
+ buffer->length describes a small buffer on the stack. We use
+ buffer->length instead of sizeof (buffer->__space) to avoid
+ making the size of struct scratch_buffer part of the ABI. */
+ new_ptr = malloc (new_length);
+ if (new_ptr == NULL)
+ return false;
+ memcpy (new_ptr, buffer->__space, buffer->length);
+ }
+ else
+ {
+ /* Buffer was already on the heap. Check for overflow. */
+ if (__glibc_unlikely (new_length < buffer->length))
+ {
+ /* Deallocate, but buffer must remain valid to free. */
+ free (buffer->data);
+ scratch_buffer_init (buffer);
+ __set_errno (ENOMEM);
+ return false;
+ }
+ new_ptr = realloc (buffer->data, new_length);
+ if (new_ptr == NULL)
+ {
+ /* Deallocate, but buffer must remain valid to free. */
+ free (buffer->data);
+ scratch_buffer_init (buffer);
+ return false;
+ }
+ }
+
+ /* Install new heap-based buffer. */
+ buffer->data = new_ptr;
+ buffer->length = new_length;
+ return true;
+}
+libc_hidden_def (__libc_scratch_buffer_grow_preserve);
new file mode 100644
@@ -0,0 +1,61 @@
+/* Variable-sized buffer with on-stack default allocation.
+ Copyright (C) 2015 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <http://www.gnu.org/licenses/>. */
+
+#include <scratch_buffer.h>
+#include <errno.h>
+
+bool
+__libc_scratch_buffer_set_array_size (struct scratch_buffer *buffer,
+ size_t nelem, size_t size)
+{
+ size_t size_max_square_root = ((size_t)1) << (sizeof (size_t) * 4);
+ /* Avoid overflow check if both values are small. */
+ if (nelem >= size_max_square_root || size >= size_max_square_root)
+ {
+ if (nelem != 0 && size > SIZE_MAX / nelem)
+ {
+ /* Discard the old buffer, but it must remain valid to
+ free. */
+ scratch_buffer_free (buffer);
+ scratch_buffer_init (buffer);
+ __set_errno (ENOMEM);
+ return false;
+ }
+ }
+
+ size_t new_length = nelem * size;
+ if (new_length <= buffer->length)
+ return true;
+
+ /* Discard old buffer. */
+ scratch_buffer_free (buffer);
+
+ char *new_ptr = malloc (new_length);
+ if (new_ptr == NULL)
+ {
+ /* Buffer must remain valid to free. */
+ scratch_buffer_init (buffer);
+ return false;
+ }
+
+ /* Install new heap-based buffer. */
+ buffer->data = new_ptr;
+ buffer->length = new_length;
+ return true;
+}
+libc_hidden_def (__libc_scratch_buffer_set_array_size);
new file mode 100644
@@ -0,0 +1,155 @@
+/*
+ Copyright (C) 2015 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <http://www.gnu.org/licenses/>. */
+
+#include <scratch_buffer.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+
+static bool
+unchanged_array_size (struct scratch_buffer *buf, size_t a, size_t b)
+{
+ size_t old_length = buf->length;
+ if (!scratch_buffer_set_array_size (buf, a, b))
+ {
+ printf ("scratch_buffer_set_array_size failed: %zu %zu\n",
+ a, b);
+ return false;
+ }
+ if (old_length != buf->length)
+ {
+ printf ("scratch_buffer_set_array_size did not preserve size: %zu %zu\n",
+ a, b);
+ return false;
+ }
+ return true;
+}
+
+static bool
+array_size_must_fail (size_t a, size_t b)
+{
+ for (int pass = 0; pass < 2; ++pass)
+ {
+ struct scratch_buffer buf;
+ scratch_buffer_init (&buf);
+ if (pass > 0)
+ if (!scratch_buffer_grow (&buf))
+ {
+ printf ("scratch_buffer_grow in array_size_must_fail failed\n");
+ return false;
+ }
+ if (scratch_buffer_set_array_size (&buf, a, b))
+ {
+ printf ("scratch_buffer_set_array_size passed: %d %zu %zu\n",
+ pass, a, b);
+ return false;
+ }
+ if (buf.data != buf.__space)
+ {
+ printf ("scratch_buffer_set_array_size did not free: %d %zu %zu\n",
+ pass, a, b);
+ return false;
+ }
+ }
+ return true;
+}
+
+static int
+do_test (void)
+{
+ {
+ struct scratch_buffer buf;
+ scratch_buffer_init (&buf);
+ memset (buf.data, ' ', buf.length);
+ scratch_buffer_free (&buf);
+ }
+ {
+ struct scratch_buffer buf;
+ scratch_buffer_init (&buf);
+ memset (buf.data, ' ', buf.length);
+ size_t old_length = buf.length;
+ scratch_buffer_grow (&buf);
+ if (buf.length <= old_length)
+ {
+ printf ("scratch_buffer_grow did not enlarge buffer\n");
+ return 1;
+ }
+ memset (buf.data, ' ', buf.length);
+ scratch_buffer_free (&buf);
+ }
+ {
+ struct scratch_buffer buf;
+ scratch_buffer_init (&buf);
+ memset (buf.data, '@', buf.length);
+ strcpy (buf.data, "prefix");
+ size_t old_length = buf.length;
+ scratch_buffer_grow_preserve (&buf);
+ if (buf.length <= old_length)
+ {
+ printf ("scratch_buffer_grow_preserve did not enlarge buffer\n");
+ return 1;
+ }
+ if (strcmp (buf.data, "prefix") != 0)
+ {
+ printf ("scratch_buffer_grow_preserve did not copy buffer\n");
+ return 1;
+ }
+ for (unsigned i = 7; i < old_length; ++i)
+ if (((char *)buf.data)[i] != '@')
+ {
+ printf ("scratch_buffer_grow_preserve did not copy buffer (%u)\n",
+ i);
+ return 1;
+ }
+ scratch_buffer_free (&buf);
+ }
+ {
+ struct scratch_buffer buf;
+ scratch_buffer_init (&buf);
+ for (int pass = 0; pass < 4; ++pass)
+ {
+ if (!(unchanged_array_size (&buf, 0, 0)
+ && unchanged_array_size (&buf, 1, 0)
+ && unchanged_array_size (&buf, 0, 1)
+ && unchanged_array_size (&buf, -1, 0)
+ && unchanged_array_size (&buf, 0, -1)
+ && unchanged_array_size (&buf, 1ULL << 16, 0)
+ && unchanged_array_size (&buf, 0, 1ULL << 16)
+ && unchanged_array_size (&buf, 1ULL << 32, 0)
+ && unchanged_array_size (&buf, 0, 1ULL << 32)))
+ return 1;
+ if (!scratch_buffer_grow (&buf))
+ {
+ printf ("scratch_buffer_grow_failed (pass %d)\n", pass);
+ }
+ }
+ scratch_buffer_free (&buf);
+ }
+ {
+ if (!(array_size_must_fail (-1, 1)
+ && array_size_must_fail (-1, -1)
+ && array_size_must_fail (1, -1)
+ && array_size_must_fail (((size_t)-1) / 4, 4)
+ && array_size_must_fail (4, ((size_t)-1) / 4)))
+ return 1;
+ }
+ return 0;
+}
+
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"
@@ -2081,3 +2081,8 @@ GLIBC_2.18
GLIBC_2.18 A
__cxa_thread_atexit_impl F
_mcount F
+GLIBC_2.22
+ GLIBC_2.22 A
+ __libc_scratch_buffer_grow F
+ __libc_scratch_buffer_grow_preserve F
+ __libc_scratch_buffer_set_array_size F
@@ -2012,6 +2012,11 @@ GLIBC_2.2.4
GLIBC_2.2.6
GLIBC_2.2.6 A
__nanosleep F
+GLIBC_2.22
+ GLIBC_2.22 A
+ __libc_scratch_buffer_grow F
+ __libc_scratch_buffer_grow_preserve F
+ __libc_scratch_buffer_set_array_size F
GLIBC_2.3
GLIBC_2.3 A
__ctype_b_loc F
@@ -89,6 +89,11 @@ GLIBC_2.17
GLIBC_2.18
GLIBC_2.18 A
__cxa_thread_atexit_impl F
+GLIBC_2.22
+ GLIBC_2.22 A
+ __libc_scratch_buffer_grow F
+ __libc_scratch_buffer_grow_preserve F
+ __libc_scratch_buffer_set_array_size F
GLIBC_2.4
GLIBC_2.4 A
_Exit F
@@ -1860,6 +1860,11 @@ GLIBC_2.2.4
GLIBC_2.2.6
GLIBC_2.2.6 A
__nanosleep F
+GLIBC_2.22
+ GLIBC_2.22 A
+ __libc_scratch_buffer_grow F
+ __libc_scratch_buffer_grow_preserve F
+ __libc_scratch_buffer_set_array_size F
GLIBC_2.3
GLIBC_2.3 A
__ctype_b_loc F
@@ -2023,6 +2023,11 @@ GLIBC_2.2.4
GLIBC_2.2.6
GLIBC_2.2.6 A
__nanosleep F
+GLIBC_2.22
+ GLIBC_2.22 A
+ __libc_scratch_buffer_grow F
+ __libc_scratch_buffer_grow_preserve F
+ __libc_scratch_buffer_set_array_size F
GLIBC_2.3
GLIBC_2.3 A
__ctype_b_loc F
@@ -1881,6 +1881,11 @@ GLIBC_2.2.6
GLIBC_2.2.6 A
__nanosleep F
getunwind F
+GLIBC_2.22
+ GLIBC_2.22 A
+ __libc_scratch_buffer_grow F
+ __libc_scratch_buffer_grow_preserve F
+ __libc_scratch_buffer_set_array_size F
GLIBC_2.3
GLIBC_2.3 A
__ctype_b_loc F
@@ -90,6 +90,11 @@ GLIBC_2.17
GLIBC_2.18
GLIBC_2.18 A
__cxa_thread_atexit_impl F
+GLIBC_2.22
+ GLIBC_2.22 A
+ __libc_scratch_buffer_grow F
+ __libc_scratch_buffer_grow_preserve F
+ __libc_scratch_buffer_set_array_size F
GLIBC_2.4
GLIBC_2.4 A
_Exit F
@@ -1979,6 +1979,11 @@ GLIBC_2.2.4
GLIBC_2.2.6
GLIBC_2.2.6 A
__nanosleep F
+GLIBC_2.22
+ GLIBC_2.22 A
+ __libc_scratch_buffer_grow F
+ __libc_scratch_buffer_grow_preserve F
+ __libc_scratch_buffer_set_array_size F
GLIBC_2.3
GLIBC_2.3 A
__ctype_b_loc F
@@ -2080,3 +2080,8 @@ GLIBC_2.18
xencrypt F
xprt_register F
xprt_unregister F
+GLIBC_2.22
+ GLIBC_2.22 A
+ __libc_scratch_buffer_grow F
+ __libc_scratch_buffer_grow_preserve F
+ __libc_scratch_buffer_set_array_size F
@@ -1951,6 +1951,11 @@ GLIBC_2.2.4
GLIBC_2.2.6
GLIBC_2.2.6 A
__nanosleep F
+GLIBC_2.22
+ GLIBC_2.22 A
+ __libc_scratch_buffer_grow F
+ __libc_scratch_buffer_grow_preserve F
+ __libc_scratch_buffer_set_array_size F
GLIBC_2.3
GLIBC_2.3 A
__ctype_b_loc F
@@ -1949,6 +1949,11 @@ GLIBC_2.2.4
GLIBC_2.2.6
GLIBC_2.2.6 A
__nanosleep F
+GLIBC_2.22
+ GLIBC_2.22 A
+ __libc_scratch_buffer_grow F
+ __libc_scratch_buffer_grow_preserve F
+ __libc_scratch_buffer_set_array_size F
GLIBC_2.3
GLIBC_2.3 A
__ctype_b_loc F
@@ -1947,6 +1947,11 @@ GLIBC_2.2.4
GLIBC_2.2.6
GLIBC_2.2.6 A
__nanosleep F
+GLIBC_2.22
+ GLIBC_2.22 A
+ __libc_scratch_buffer_grow F
+ __libc_scratch_buffer_grow_preserve F
+ __libc_scratch_buffer_set_array_size F
GLIBC_2.3
GLIBC_2.3 A
__ctype_b_loc F
@@ -1941,6 +1941,11 @@ GLIBC_2.2.4
GLIBC_2.2.6
GLIBC_2.2.6 A
__nanosleep F
+GLIBC_2.22
+ GLIBC_2.22 A
+ __libc_scratch_buffer_grow F
+ __libc_scratch_buffer_grow_preserve F
+ __libc_scratch_buffer_set_array_size F
GLIBC_2.3
GLIBC_2.3 A
__ctype_b_loc F
@@ -2121,3 +2121,8 @@ GLIBC_2.21
xencrypt F
xprt_register F
xprt_unregister F
+GLIBC_2.22
+ GLIBC_2.22 A
+ __libc_scratch_buffer_grow F
+ __libc_scratch_buffer_grow_preserve F
+ __libc_scratch_buffer_set_array_size F
@@ -1983,6 +1983,11 @@ GLIBC_2.2.4
GLIBC_2.2.6
GLIBC_2.2.6 A
__nanosleep F
+GLIBC_2.22
+ GLIBC_2.22 A
+ __libc_scratch_buffer_grow F
+ __libc_scratch_buffer_grow_preserve F
+ __libc_scratch_buffer_set_array_size F
GLIBC_2.3
GLIBC_2.3 A
__ctype_b_loc F
@@ -1989,6 +1989,11 @@ GLIBC_2.2.4
GLIBC_2.2.6
GLIBC_2.2.6 A
__nanosleep F
+GLIBC_2.22
+ GLIBC_2.22 A
+ __libc_scratch_buffer_grow F
+ __libc_scratch_buffer_grow_preserve F
+ __libc_scratch_buffer_set_array_size F
GLIBC_2.3
GLIBC_2.3 A
__ctype_b_loc F
@@ -90,6 +90,11 @@ GLIBC_2.17
GLIBC_2.18
GLIBC_2.18 A
__cxa_thread_atexit_impl F
+GLIBC_2.22
+ GLIBC_2.22 A
+ __libc_scratch_buffer_grow F
+ __libc_scratch_buffer_grow_preserve F
+ __libc_scratch_buffer_set_array_size F
GLIBC_2.3
GLIBC_2.3 A
_Exit F
@@ -1984,6 +1984,11 @@ GLIBC_2.2.4
GLIBC_2.2.6
GLIBC_2.2.6 A
__nanosleep F
+GLIBC_2.22
+ GLIBC_2.22 A
+ __libc_scratch_buffer_grow F
+ __libc_scratch_buffer_grow_preserve F
+ __libc_scratch_buffer_set_array_size F
GLIBC_2.3
GLIBC_2.3 A
__ctype_b_loc F
@@ -1880,6 +1880,11 @@ GLIBC_2.2.4
GLIBC_2.2.6
GLIBC_2.2.6 A
__nanosleep F
+GLIBC_2.22
+ GLIBC_2.22 A
+ __libc_scratch_buffer_grow F
+ __libc_scratch_buffer_grow_preserve F
+ __libc_scratch_buffer_set_array_size F
GLIBC_2.3
GLIBC_2.3 A
__ctype_b_loc F
@@ -1864,6 +1864,11 @@ GLIBC_2.2.4
GLIBC_2.2.6
GLIBC_2.2.6 A
__nanosleep F
+GLIBC_2.22
+ GLIBC_2.22 A
+ __libc_scratch_buffer_grow F
+ __libc_scratch_buffer_grow_preserve F
+ __libc_scratch_buffer_set_array_size F
GLIBC_2.3
GLIBC_2.3 A
__ctype_b_loc F
@@ -1975,6 +1975,11 @@ GLIBC_2.2.4
GLIBC_2.2.6
GLIBC_2.2.6 A
__nanosleep F
+GLIBC_2.22
+ GLIBC_2.22 A
+ __libc_scratch_buffer_grow F
+ __libc_scratch_buffer_grow_preserve F
+ __libc_scratch_buffer_set_array_size F
GLIBC_2.3
GLIBC_2.3 A
__ctype_b_loc F
@@ -1908,6 +1908,11 @@ GLIBC_2.2.4
GLIBC_2.2.6
GLIBC_2.2.6 A
__nanosleep F
+GLIBC_2.22
+ GLIBC_2.22 A
+ __libc_scratch_buffer_grow F
+ __libc_scratch_buffer_grow_preserve F
+ __libc_scratch_buffer_set_array_size F
GLIBC_2.3
GLIBC_2.3 A
__ctype_b_loc F
@@ -2091,3 +2091,8 @@ GLIBC_2.17
GLIBC_2.18
GLIBC_2.18 A
__cxa_thread_atexit_impl F
+GLIBC_2.22
+ GLIBC_2.22 A
+ __libc_scratch_buffer_grow F
+ __libc_scratch_buffer_grow_preserve F
+ __libc_scratch_buffer_set_array_size F
@@ -2091,3 +2091,8 @@ GLIBC_2.17
GLIBC_2.18
GLIBC_2.18 A
__cxa_thread_atexit_impl F
+GLIBC_2.22
+ GLIBC_2.22 A
+ __libc_scratch_buffer_grow F
+ __libc_scratch_buffer_grow_preserve F
+ __libc_scratch_buffer_set_array_size F
@@ -2091,3 +2091,8 @@ GLIBC_2.17
GLIBC_2.18
GLIBC_2.18 A
__cxa_thread_atexit_impl F
+GLIBC_2.22
+ GLIBC_2.22 A
+ __libc_scratch_buffer_grow F
+ __libc_scratch_buffer_grow_preserve F
+ __libc_scratch_buffer_set_array_size F
@@ -1854,6 +1854,11 @@ GLIBC_2.2.5
GLIBC_2.2.6
GLIBC_2.2.6 A
__nanosleep F
+GLIBC_2.22
+ GLIBC_2.22 A
+ __libc_scratch_buffer_grow F
+ __libc_scratch_buffer_grow_preserve F
+ __libc_scratch_buffer_set_array_size F
GLIBC_2.3
GLIBC_2.3 A
__ctype_b_loc F
@@ -2089,3 +2089,8 @@ GLIBC_2.17
GLIBC_2.18
GLIBC_2.18 A
__cxa_thread_atexit_impl F
+GLIBC_2.22
+ GLIBC_2.22 A
+ __libc_scratch_buffer_grow F
+ __libc_scratch_buffer_grow_preserve F
+ __libc_scratch_buffer_set_array_size F