tools/include/nolibc/compiler.h | 3 + tools/include/nolibc/stdio.h | 427 ++++++++++++++----- tools/testing/selftests/nolibc/nolibc-test.c | 99 +++-- 3 files changed, 371 insertions(+), 158 deletions(-)
From: David Laight <david.laight.linux@gmail.com>
Update printf() so that it handles almost all the non-fp formats.
In particular:
- Left alignment.
- Zero padding.
- Alternate form "%#x" and "%#o".
- Field precision.
- Variable field width and precision.
- Width modifiers q, L, t and z.
- Conversion specifiers i, o and X (X generates lower case).
About the only things that are missing are wide chanacters and floating point.
The tests are updated to match.
Bloat/savings (in nolibc-test, but excluding the program) to patch 11:
(Measured for v3)
Function old new delta
_nolibc_u64toa_base.isra - 143 +143
strerror - 78 +78
__nolibc_sprintf_cb 58 91 +33
itoa_r.isra 60 75 +15
utoa_r.isra 144 - -144
__nolibc_printf 1081 729 -352
(All these functions include ~40 bytes for the stack protector code.)
utoa_r.isra and _nolibc_u64toa_base.isra pretty much cancel each other out.
itoa_r.isra grows slightly since it calls _nolibc_u64toa_base().
strerror() used to be inlined, but over half of it is the stack check.
While some of the code added to __nolibc_sprintf_cb() has come out of
__nolibc_printf() 16-20 bytes is removed from the caller.
So there is a net saving of about 280 bytes (including losing a copy of
the number to ascii code).
The later patches add code back in:
patch 13 - conversion flags " +#" +80 bytes
patch 14 - left aligning fields +38 bytes
patch 15 - zero padding and field precision +260 bytes
patch 16 - octal output +34 bytes
So probably about +130 bytes, but it will depend on what the application
actually calls and inlining decisions made by the compiler.
(All x86-64, other architectures will vary.)
The biggest size change is probably removing the .data from strerror().
This reduced the program binary file by 4k if it is the only initialised
data in a small program.
Changes for v5:
- Old patches 2 to 7 have been applied to nolibc-next and removed.
- Patch 3 changed to return the correct (success/fail not length)
value from strerror_r() and to return ERANGE if the buffer is short.
- Patch 11 (prepend sign) has an updated comment.
Changes for v4:
- Old patches 2, 3 and 6 have been applied to nolibc-next and removed.
- Patch 7 (fix padding) has been moved before the selftest changes
so that the tests don't fail.
- NOLIBC_IGNORE_ERRNO is left only applying to %m and not strerror().
- Avoid calling memcpy(tgt, NULL, 0) in the vsnprintf callback function.
- Some of the patches have been split.
The final version of stdio.h only differs from v3 in the strerror()
and vsnprintf callback code.
Significant changes for v3:
The patches have been re-ordered, split and joined but the final code
is pretty much the same as v2.
- Include the patch to stdlib.h that optimises the 'number to ascii'
functions. This is needed for the final patch that adds octal support.
This includes a fix to the previous version that could generate negative
digits.
- Add octal support in the final patch.
- Update the selftests as new features are added.
- The patch to buffer fprintf() output has been removed.
Changes for v2:
Mostly changes to improve the readability of the code.
- New patch #1 inserted to rename the variable 'c' to 'ch'.
- Use #define 'magic' for the bit-masks that check multiple characters.
The check for the conversion flag characters is then based on:
ch_flag = _NOLIBC_PF_CHAR_IS_ONE_OF(ch, ' ', '#', '+', '-', '0');
- Re-order the changes so that the old patch 10 (Use bit-pattern for
integral formats) is done at the same time as bit-masks are used for
the flags characters and length modifiers.
This means the restructuring changes are done before new features are
added.
- Put all the changes to the selftest together at the end.
There is one extra test for ("%#01x", 0x1234) (should be "0x1234")
which is problematic because once you've removed the length of the "0x"
from the field width there are -1 character postions for the digits.
David Laight (17):
tools/nolibc: Add _NOLIBC_OPTIMIZER_HIDE_VAR() to compiler.h
selftests/nolibc: Rename w to written in expect_vfprintf()
tools/nolibc: Implement strerror() in terms of strerror_r()
tools/nolibc: Rename the 'errnum' parameter to strerror()
tools/nolibc/printf: Output pad characters in 16 byte chunks
tools/nolibc/printf: Simplify __nolibc_printf()
tools/nolibc/printf: Use goto and reduce indentation
tools/nolibc/printf: Use bit-masks to hold requested flag, length and
conversion chars
tools/nolibc/printf: Add support for length modifiers tzqL and formats
iX
tools/nolibc/printf: Handle "%s" with the numeric formats
tools/nolibc/printf: Prepend sign to converted number
tools/nolibc/printf: Add support for conversion flags space and plus
tools/nolibc/printf: Special case 0 and add support for %#x
tools/nolibc/printf: Add support for left aligning fields
tools/nolibc/printf: Add support for zero padding and field precision
tools/nolibc/printf: Add support for octal output
selftests/nolibc: Use printf variable field widths and precisions
tools/include/nolibc/compiler.h | 3 +
tools/include/nolibc/stdio.h | 427 ++++++++++++++-----
tools/testing/selftests/nolibc/nolibc-test.c | 99 +++--
3 files changed, 371 insertions(+), 158 deletions(-)
--
2.39.5
Hi David,
thanks again for your patches!
On 2026-03-08 11:37:25+0000, david.laight.linux@gmail.com wrote:
> From: David Laight <david.laight.linux@gmail.com>
> David Laight (17):
> tools/nolibc: Add _NOLIBC_OPTIMIZER_HIDE_VAR() to compiler.h
> selftests/nolibc: Rename w to written in expect_vfprintf()
> tools/nolibc: Implement strerror() in terms of strerror_r()
> tools/nolibc: Rename the 'errnum' parameter to strerror()
> tools/nolibc/printf: Output pad characters in 16 byte chunks
> tools/nolibc/printf: Simplify __nolibc_printf()
> tools/nolibc/printf: Use goto and reduce indentation
> tools/nolibc/printf: Use bit-masks to hold requested flag, length and
> conversion chars
> tools/nolibc/printf: Add support for length modifiers tzqL and formats
> iX
> tools/nolibc/printf: Handle "%s" with the numeric formats
> tools/nolibc/printf: Prepend sign to converted number
> tools/nolibc/printf: Add support for conversion flags space and plus
> tools/nolibc/printf: Special case 0 and add support for %#x
> tools/nolibc/printf: Add support for left aligning fields
> tools/nolibc/printf: Add support for zero padding and field precision
> tools/nolibc/printf: Add support for octal output
Beginning from here we have another sign-compare warning:
/home/t-8ch/.cache/crosstools/gcc-13.2.0-nolibc/i386-linux/bin/i386-linux-gcc -Os -fno-ident -fno-asynchronous-unwind-tables -std=c89 -W -Wall -Wextra -fno-stack-protector -Wmissing-prototypes -fstack-protector-all -mstack-protector-guard=global -fsanitize=undefined -fsanitize-trap=all -m32 -Werror -Wl,--fatal-warnings -o nolibc-test \
-nostdlib -nostdinc -static -Isysroot/i386/include nolibc-test.c nolibc-test-linkage.c -lgcc
In file included from sysroot/i386/include/nolibc.h:123,
from sysroot/i386/include/stdio.h:8,
from nolibc-test.c:12:
sysroot/i386/include/stdio.h: In function '__nolibc_printf':
sysroot/i386/include/stdio.h:569:41: error: comparison of integer expressions of different signedness: 'unsigned int' and 'char' [-Werror=sign-compare]
569 | if (sign_prefix != *out) {
| ^~
cc1: all warnings being treated as errors
I have applied all of the patches *before*
"tools/nolibc/printf: Prepend sign to converted number", which
introduced the sign_prefix variable.
Could you fix this up and repost the remaining patches?
Thomas
> selftests/nolibc: Use printf variable field widths and precisions
>
> tools/include/nolibc/compiler.h | 3 +
> tools/include/nolibc/stdio.h | 427 ++++++++++++++-----
> tools/testing/selftests/nolibc/nolibc-test.c | 99 +++--
> 3 files changed, 371 insertions(+), 158 deletions(-)
On Sun, 8 Mar 2026 22:01:19 +0100
Thomas Weißschuh <linux@weissschuh.net> wrote:
> Hi David,
>
> thanks again for your patches!
>
> On 2026-03-08 11:37:25+0000, david.laight.linux@gmail.com wrote:
> > From: David Laight <david.laight.linux@gmail.com>
> > David Laight (17):
> > tools/nolibc: Add _NOLIBC_OPTIMIZER_HIDE_VAR() to compiler.h
> > selftests/nolibc: Rename w to written in expect_vfprintf()
> > tools/nolibc: Implement strerror() in terms of strerror_r()
> > tools/nolibc: Rename the 'errnum' parameter to strerror()
> > tools/nolibc/printf: Output pad characters in 16 byte chunks
> > tools/nolibc/printf: Simplify __nolibc_printf()
> > tools/nolibc/printf: Use goto and reduce indentation
> > tools/nolibc/printf: Use bit-masks to hold requested flag, length and
> > conversion chars
> > tools/nolibc/printf: Add support for length modifiers tzqL and formats
> > iX
> > tools/nolibc/printf: Handle "%s" with the numeric formats
> > tools/nolibc/printf: Prepend sign to converted number
> > tools/nolibc/printf: Add support for conversion flags space and plus
> > tools/nolibc/printf: Special case 0 and add support for %#x
> > tools/nolibc/printf: Add support for left aligning fields
> > tools/nolibc/printf: Add support for zero padding and field precision
> > tools/nolibc/printf: Add support for octal output
>
> Beginning from here we have another sign-compare warning:
>
> /home/t-8ch/.cache/crosstools/gcc-13.2.0-nolibc/i386-linux/bin/i386-linux-gcc -Os -fno-ident -fno-asynchronous-unwind-tables -std=c89 -W -Wall -Wextra -fno-stack-protector -Wmissing-prototypes -fstack-protector-all -mstack-protector-guard=global -fsanitize=undefined -fsanitize-trap=all -m32 -Werror -Wl,--fatal-warnings -o nolibc-test \
> -nostdlib -nostdinc -static -Isysroot/i386/include nolibc-test.c nolibc-test-linkage.c -lgcc
> In file included from sysroot/i386/include/nolibc.h:123,
> from sysroot/i386/include/stdio.h:8,
> from nolibc-test.c:12:
> sysroot/i386/include/stdio.h: In function '__nolibc_printf':
> sysroot/i386/include/stdio.h:569:41: error: comparison of integer expressions of different signedness: 'unsigned int' and 'char' [-Werror=sign-compare]
> 569 | if (sign_prefix != *out) {
> | ^~
> cc1: all warnings being treated as errors
>
> I have applied all of the patches *before*
> "tools/nolibc/printf: Prepend sign to converted number", which
> introduced the sign_prefix variable.
>
> Could you fix this up and repost the remaining patches?
I hate 'sign compare' ...
I dislike using casts to 'fix' it - random 'integer' casts have caused me
grief in the past.
I don't want to make 'sign_prefix' signed - stops you adding 4 characters
(should you so desire); not to mention >> being either UB or implementation
defined on signed values (or maybe just negative ones).
The two obvious fixes are:
if (sign_prefix - *out)
or:
if (sign_prefix != *out + 0u)
Your pick :-)
At least it is only the last couple of patches.
David
>
>
> Thomas
>
> > selftests/nolibc: Use printf variable field widths and precisions
> >
> > tools/include/nolibc/compiler.h | 3 +
> > tools/include/nolibc/stdio.h | 427 ++++++++++++++-----
> > tools/testing/selftests/nolibc/nolibc-test.c | 99 +++--
> > 3 files changed, 371 insertions(+), 158 deletions(-)
Hi David,
On 2026-03-08 22:41:21+0000, David Laight wrote:
> On Sun, 8 Mar 2026 22:01:19 +0100
> Thomas Weißschuh <linux@weissschuh.net> wrote:
> > On 2026-03-08 11:37:25+0000, david.laight.linux@gmail.com wrote:
> > > From: David Laight <david.laight.linux@gmail.com>
> > > David Laight (17):
> > > tools/nolibc: Add _NOLIBC_OPTIMIZER_HIDE_VAR() to compiler.h
> > > selftests/nolibc: Rename w to written in expect_vfprintf()
> > > tools/nolibc: Implement strerror() in terms of strerror_r()
> > > tools/nolibc: Rename the 'errnum' parameter to strerror()
> > > tools/nolibc/printf: Output pad characters in 16 byte chunks
> > > tools/nolibc/printf: Simplify __nolibc_printf()
> > > tools/nolibc/printf: Use goto and reduce indentation
> > > tools/nolibc/printf: Use bit-masks to hold requested flag, length and
> > > conversion chars
> > > tools/nolibc/printf: Add support for length modifiers tzqL and formats
> > > iX
> > > tools/nolibc/printf: Handle "%s" with the numeric formats
> > > tools/nolibc/printf: Prepend sign to converted number
> > > tools/nolibc/printf: Add support for conversion flags space and plus
> > > tools/nolibc/printf: Special case 0 and add support for %#x
> > > tools/nolibc/printf: Add support for left aligning fields
> > > tools/nolibc/printf: Add support for zero padding and field precision
> > > tools/nolibc/printf: Add support for octal output
> >
> > Beginning from here we have another sign-compare warning:
> >
> > /home/t-8ch/.cache/crosstools/gcc-13.2.0-nolibc/i386-linux/bin/i386-linux-gcc -Os -fno-ident -fno-asynchronous-unwind-tables -std=c89 -W -Wall -Wextra -fno-stack-protector -Wmissing-prototypes -fstack-protector-all -mstack-protector-guard=global -fsanitize=undefined -fsanitize-trap=all -m32 -Werror -Wl,--fatal-warnings -o nolibc-test \
> > -nostdlib -nostdinc -static -Isysroot/i386/include nolibc-test.c nolibc-test-linkage.c -lgcc
> > In file included from sysroot/i386/include/nolibc.h:123,
> > from sysroot/i386/include/stdio.h:8,
> > from nolibc-test.c:12:
> > sysroot/i386/include/stdio.h: In function '__nolibc_printf':
> > sysroot/i386/include/stdio.h:569:41: error: comparison of integer expressions of different signedness: 'unsigned int' and 'char' [-Werror=sign-compare]
> > 569 | if (sign_prefix != *out) {
> > | ^~
> > cc1: all warnings being treated as errors
> >
> > I have applied all of the patches *before*
> > "tools/nolibc/printf: Prepend sign to converted number", which
> > introduced the sign_prefix variable.
> >
> > Could you fix this up and repost the remaining patches?
>
> I hate 'sign compare' ...
> I dislike using casts to 'fix' it - random 'integer' casts have caused me
> grief in the past.
> I don't want to make 'sign_prefix' signed - stops you adding 4 characters
> (should you so desire); not to mention >> being either UB or implementation
> defined on signed values (or maybe just negative ones).
Sorry for the long delay, I missed these mails.
>
> The two obvious fixes are:
> if (sign_prefix - *out)
I went with this and applied the rest of the series.
Thanks for all your patches!
> or:
> if (sign_prefix != *out + 0u)
>
> Your pick :-)
>
> At least it is only the last couple of patches.
Thomas
On Fri, 13 Mar 2026 21:07:28 +0100
Thomas Weißschuh <linux@weissschuh.net> wrote:
> Hi David,
>
> On 2026-03-08 22:41:21+0000, David Laight wrote:
> > On Sun, 8 Mar 2026 22:01:19 +0100
> > Thomas Weißschuh <linux@weissschuh.net> wrote:
> > > On 2026-03-08 11:37:25+0000, david.laight.linux@gmail.com wrote:
> > > > From: David Laight <david.laight.linux@gmail.com>
> > > > David Laight (17):
> > > > tools/nolibc: Add _NOLIBC_OPTIMIZER_HIDE_VAR() to compiler.h
> > > > selftests/nolibc: Rename w to written in expect_vfprintf()
> > > > tools/nolibc: Implement strerror() in terms of strerror_r()
> > > > tools/nolibc: Rename the 'errnum' parameter to strerror()
> > > > tools/nolibc/printf: Output pad characters in 16 byte chunks
> > > > tools/nolibc/printf: Simplify __nolibc_printf()
> > > > tools/nolibc/printf: Use goto and reduce indentation
> > > > tools/nolibc/printf: Use bit-masks to hold requested flag, length and
> > > > conversion chars
> > > > tools/nolibc/printf: Add support for length modifiers tzqL and formats
> > > > iX
> > > > tools/nolibc/printf: Handle "%s" with the numeric formats
> > > > tools/nolibc/printf: Prepend sign to converted number
> > > > tools/nolibc/printf: Add support for conversion flags space and plus
> > > > tools/nolibc/printf: Special case 0 and add support for %#x
> > > > tools/nolibc/printf: Add support for left aligning fields
> > > > tools/nolibc/printf: Add support for zero padding and field precision
> > > > tools/nolibc/printf: Add support for octal output
> > >
> > > Beginning from here we have another sign-compare warning:
> > >
> > > /home/t-8ch/.cache/crosstools/gcc-13.2.0-nolibc/i386-linux/bin/i386-linux-gcc -Os -fno-ident -fno-asynchronous-unwind-tables -std=c89 -W -Wall -Wextra -fno-stack-protector -Wmissing-prototypes -fstack-protector-all -mstack-protector-guard=global -fsanitize=undefined -fsanitize-trap=all -m32 -Werror -Wl,--fatal-warnings -o nolibc-test \
> > > -nostdlib -nostdinc -static -Isysroot/i386/include nolibc-test.c nolibc-test-linkage.c -lgcc
> > > In file included from sysroot/i386/include/nolibc.h:123,
> > > from sysroot/i386/include/stdio.h:8,
> > > from nolibc-test.c:12:
> > > sysroot/i386/include/stdio.h: In function '__nolibc_printf':
> > > sysroot/i386/include/stdio.h:569:41: error: comparison of integer expressions of different signedness: 'unsigned int' and 'char' [-Werror=sign-compare]
> > > 569 | if (sign_prefix != *out) {
> > > | ^~
> > > cc1: all warnings being treated as errors
> > >
> > > I have applied all of the patches *before*
> > > "tools/nolibc/printf: Prepend sign to converted number", which
> > > introduced the sign_prefix variable.
> > >
> > > Could you fix this up and repost the remaining patches?
> >
> > I hate 'sign compare' ...
> > I dislike using casts to 'fix' it - random 'integer' casts have caused me
> > grief in the past.
> > I don't want to make 'sign_prefix' signed - stops you adding 4 characters
> > (should you so desire); not to mention >> being either UB or implementation
> > defined on signed values (or maybe just negative ones).
>
> Sorry for the long delay, I missed these mails.
>
> >
> > The two obvious fixes are:
> > if (sign_prefix - *out)
>
> I went with this and applied the rest of the series.
> Thanks for all your patches!
Thanks - I hadn't got around to resending them.
David
>
> > or:
> > if (sign_prefix != *out + 0u)
> >
> > Your pick :-)
> >
> > At least it is only the last couple of patches.
>
>
> Thomas
On Fri, Mar 13, 2026 at 10:40:48PM +0000, David Laight wrote:
> On Fri, 13 Mar 2026 21:07:28 +0100
> Thomas Weißschuh <linux@weissschuh.net> wrote:
>
> > Hi David,
> >
> > On 2026-03-08 22:41:21+0000, David Laight wrote:
> > > On Sun, 8 Mar 2026 22:01:19 +0100
> > > Thomas Weißschuh <linux@weissschuh.net> wrote:
> > > > On 2026-03-08 11:37:25+0000, david.laight.linux@gmail.com wrote:
> > > > > From: David Laight <david.laight.linux@gmail.com>
> > > > > David Laight (17):
> > > > > tools/nolibc: Add _NOLIBC_OPTIMIZER_HIDE_VAR() to compiler.h
> > > > > selftests/nolibc: Rename w to written in expect_vfprintf()
> > > > > tools/nolibc: Implement strerror() in terms of strerror_r()
> > > > > tools/nolibc: Rename the 'errnum' parameter to strerror()
> > > > > tools/nolibc/printf: Output pad characters in 16 byte chunks
> > > > > tools/nolibc/printf: Simplify __nolibc_printf()
> > > > > tools/nolibc/printf: Use goto and reduce indentation
> > > > > tools/nolibc/printf: Use bit-masks to hold requested flag, length and
> > > > > conversion chars
> > > > > tools/nolibc/printf: Add support for length modifiers tzqL and formats
> > > > > iX
> > > > > tools/nolibc/printf: Handle "%s" with the numeric formats
> > > > > tools/nolibc/printf: Prepend sign to converted number
> > > > > tools/nolibc/printf: Add support for conversion flags space and plus
> > > > > tools/nolibc/printf: Special case 0 and add support for %#x
> > > > > tools/nolibc/printf: Add support for left aligning fields
> > > > > tools/nolibc/printf: Add support for zero padding and field precision
> > > > > tools/nolibc/printf: Add support for octal output
> > > >
> > > > Beginning from here we have another sign-compare warning:
> > > >
> > > > /home/t-8ch/.cache/crosstools/gcc-13.2.0-nolibc/i386-linux/bin/i386-linux-gcc -Os -fno-ident -fno-asynchronous-unwind-tables -std=c89 -W -Wall -Wextra -fno-stack-protector -Wmissing-prototypes -fstack-protector-all -mstack-protector-guard=global -fsanitize=undefined -fsanitize-trap=all -m32 -Werror -Wl,--fatal-warnings -o nolibc-test \
> > > > -nostdlib -nostdinc -static -Isysroot/i386/include nolibc-test.c nolibc-test-linkage.c -lgcc
> > > > In file included from sysroot/i386/include/nolibc.h:123,
> > > > from sysroot/i386/include/stdio.h:8,
> > > > from nolibc-test.c:12:
> > > > sysroot/i386/include/stdio.h: In function '__nolibc_printf':
> > > > sysroot/i386/include/stdio.h:569:41: error: comparison of integer expressions of different signedness: 'unsigned int' and 'char' [-Werror=sign-compare]
> > > > 569 | if (sign_prefix != *out) {
> > > > | ^~
> > > > cc1: all warnings being treated as errors
> > > >
> > > > I have applied all of the patches *before*
> > > > "tools/nolibc/printf: Prepend sign to converted number", which
> > > > introduced the sign_prefix variable.
> > > >
> > > > Could you fix this up and repost the remaining patches?
> > >
> > > I hate 'sign compare' ...
> > > I dislike using casts to 'fix' it - random 'integer' casts have caused me
> > > grief in the past.
> > > I don't want to make 'sign_prefix' signed - stops you adding 4 characters
> > > (should you so desire); not to mention >> being either UB or implementation
> > > defined on signed values (or maybe just negative ones).
> >
> > Sorry for the long delay, I missed these mails.
> >
> > >
> > > The two obvious fixes are:
> > > if (sign_prefix - *out)
> >
> > I went with this and applied the rest of the series.
> > Thanks for all your patches!
>
> Thanks - I hadn't got around to resending them.
Thanks to both of you, I think it's a really nice improvement, and the
final quality was worth the number of round trips.
Willy
Hi David,
On Sun, Mar 08, 2026 at 10:41:21PM +0000, David Laight wrote:
> On Sun, 8 Mar 2026 22:01:19 +0100
> Thomas Weißschuh <linux@weissschuh.net> wrote:
>
> > Hi David,
> >
> > thanks again for your patches!
> >
> > On 2026-03-08 11:37:25+0000, david.laight.linux@gmail.com wrote:
> > > From: David Laight <david.laight.linux@gmail.com>
> > > David Laight (17):
> > > tools/nolibc: Add _NOLIBC_OPTIMIZER_HIDE_VAR() to compiler.h
> > > selftests/nolibc: Rename w to written in expect_vfprintf()
> > > tools/nolibc: Implement strerror() in terms of strerror_r()
> > > tools/nolibc: Rename the 'errnum' parameter to strerror()
> > > tools/nolibc/printf: Output pad characters in 16 byte chunks
> > > tools/nolibc/printf: Simplify __nolibc_printf()
> > > tools/nolibc/printf: Use goto and reduce indentation
> > > tools/nolibc/printf: Use bit-masks to hold requested flag, length and
> > > conversion chars
> > > tools/nolibc/printf: Add support for length modifiers tzqL and formats
> > > iX
> > > tools/nolibc/printf: Handle "%s" with the numeric formats
> > > tools/nolibc/printf: Prepend sign to converted number
> > > tools/nolibc/printf: Add support for conversion flags space and plus
> > > tools/nolibc/printf: Special case 0 and add support for %#x
> > > tools/nolibc/printf: Add support for left aligning fields
> > > tools/nolibc/printf: Add support for zero padding and field precision
> > > tools/nolibc/printf: Add support for octal output
> >
> > Beginning from here we have another sign-compare warning:
> >
> > /home/t-8ch/.cache/crosstools/gcc-13.2.0-nolibc/i386-linux/bin/i386-linux-gcc -Os -fno-ident -fno-asynchronous-unwind-tables -std=c89 -W -Wall -Wextra -fno-stack-protector -Wmissing-prototypes -fstack-protector-all -mstack-protector-guard=global -fsanitize=undefined -fsanitize-trap=all -m32 -Werror -Wl,--fatal-warnings -o nolibc-test \
> > -nostdlib -nostdinc -static -Isysroot/i386/include nolibc-test.c nolibc-test-linkage.c -lgcc
> > In file included from sysroot/i386/include/nolibc.h:123,
> > from sysroot/i386/include/stdio.h:8,
> > from nolibc-test.c:12:
> > sysroot/i386/include/stdio.h: In function '__nolibc_printf':
> > sysroot/i386/include/stdio.h:569:41: error: comparison of integer expressions of different signedness: 'unsigned int' and 'char' [-Werror=sign-compare]
> > 569 | if (sign_prefix != *out) {
> > | ^~
> > cc1: all warnings being treated as errors
> >
> > I have applied all of the patches *before*
> > "tools/nolibc/printf: Prepend sign to converted number", which
> > introduced the sign_prefix variable.
> >
> > Could you fix this up and repost the remaining patches?
>
> I hate 'sign compare' ...
> I dislike using casts to 'fix' it - random 'integer' casts have caused me
> grief in the past.
The cast here is unavoidable. One is a signed char, the other is an unsigned
int, so the comparison can be done in 3 methods depending on the developer's
original intent:
- 8 bits of the char against 8 lower bits of the unsigned int
- signed comparison where char is sign-extended and both are compared
as a 32-bit signed int
- unsigned comparison where char is zero-extended and both are compared
as a 32-bit unsigned int
> I don't want to make 'sign_prefix' signed - stops you adding 4 characters
> (should you so desire); not to mention >> being either UB or implementation
> defined on signed values (or maybe just negative ones).
>
> The two obvious fixes are:
> if (sign_prefix - *out)
> or:
> if (sign_prefix != *out + 0u)
>
> Your pick :-)
>
> At least it is only the last couple of patches.
It will not change much or will just even more hide the problem. My
understanding of that code is that it's neither of these cases. Based
on the comment you apparently want in this test to only check for the
first byte of sign_prefix against *out, right ? So that should be:
if ((char)sign_prefix != *out)
Did I get it right ?
Willy
On Mon, 9 Mar 2026 07:55:30 +0100
Willy Tarreau <w@1wt.eu> wrote:
> Hi David,
>
> On Sun, Mar 08, 2026 at 10:41:21PM +0000, David Laight wrote:
> > On Sun, 8 Mar 2026 22:01:19 +0100
> > Thomas Weißschuh <linux@weissschuh.net> wrote:
> >
> > > Hi David,
> > >
> > > thanks again for your patches!
> > >
> > > On 2026-03-08 11:37:25+0000, david.laight.linux@gmail.com wrote:
> > > > From: David Laight <david.laight.linux@gmail.com>
> > > > David Laight (17):
> > > > tools/nolibc: Add _NOLIBC_OPTIMIZER_HIDE_VAR() to compiler.h
> > > > selftests/nolibc: Rename w to written in expect_vfprintf()
> > > > tools/nolibc: Implement strerror() in terms of strerror_r()
> > > > tools/nolibc: Rename the 'errnum' parameter to strerror()
> > > > tools/nolibc/printf: Output pad characters in 16 byte chunks
> > > > tools/nolibc/printf: Simplify __nolibc_printf()
> > > > tools/nolibc/printf: Use goto and reduce indentation
> > > > tools/nolibc/printf: Use bit-masks to hold requested flag, length and
> > > > conversion chars
> > > > tools/nolibc/printf: Add support for length modifiers tzqL and formats
> > > > iX
> > > > tools/nolibc/printf: Handle "%s" with the numeric formats
> > > > tools/nolibc/printf: Prepend sign to converted number
> > > > tools/nolibc/printf: Add support for conversion flags space and plus
> > > > tools/nolibc/printf: Special case 0 and add support for %#x
> > > > tools/nolibc/printf: Add support for left aligning fields
> > > > tools/nolibc/printf: Add support for zero padding and field precision
> > > > tools/nolibc/printf: Add support for octal output
> > >
> > > Beginning from here we have another sign-compare warning:
> > >
> > > /home/t-8ch/.cache/crosstools/gcc-13.2.0-nolibc/i386-linux/bin/i386-linux-gcc -Os -fno-ident -fno-asynchronous-unwind-tables -std=c89 -W -Wall -Wextra -fno-stack-protector -Wmissing-prototypes -fstack-protector-all -mstack-protector-guard=global -fsanitize=undefined -fsanitize-trap=all -m32 -Werror -Wl,--fatal-warnings -o nolibc-test \
> > > -nostdlib -nostdinc -static -Isysroot/i386/include nolibc-test.c nolibc-test-linkage.c -lgcc
> > > In file included from sysroot/i386/include/nolibc.h:123,
> > > from sysroot/i386/include/stdio.h:8,
> > > from nolibc-test.c:12:
> > > sysroot/i386/include/stdio.h: In function '__nolibc_printf':
> > > sysroot/i386/include/stdio.h:569:41: error: comparison of integer expressions of different signedness: 'unsigned int' and 'char' [-Werror=sign-compare]
> > > 569 | if (sign_prefix != *out) {
> > > | ^~
> > > cc1: all warnings being treated as errors
> > >
> > > I have applied all of the patches *before*
> > > "tools/nolibc/printf: Prepend sign to converted number", which
> > > introduced the sign_prefix variable.
> > >
> > > Could you fix this up and repost the remaining patches?
> >
> > I hate 'sign compare' ...
> > I dislike using casts to 'fix' it - random 'integer' casts have caused me
> > grief in the past.
>
> The cast here is unavoidable. One is a signed char, the other is an unsigned
> int, so the comparison can be done in 3 methods depending on the developer's
> original intent:
> - 8 bits of the char against 8 lower bits of the unsigned int
> - signed comparison where char is sign-extended and both are compared
> as a 32-bit signed int
> - unsigned comparison where char is zero-extended and both are compared
> as a 32-bit unsigned int
>
> > I don't want to make 'sign_prefix' signed - stops you adding 4 characters
> > (should you so desire); not to mention >> being either UB or implementation
> > defined on signed values (or maybe just negative ones).
> >
> > The two obvious fixes are:
> > if (sign_prefix - *out)
> > or:
> > if (sign_prefix != *out + 0u)
> >
> > Your pick :-)
> >
> > At least it is only the last couple of patches.
>
> It will not change much or will just even more hide the problem.
There is nothing to change, the 'problem' just need hiding so the
compiler doesn't complain.
> My understanding of that code is that it's neither of these cases. Based
> on the comment you apparently want in this test to only check for the
> first byte of sign_prefix against *out, right ? So that should be:
>
> if ((char)sign_prefix != *out)
>
> Did I get it right ?
Not really, the check can only succeed when both values are '0'
(which is the real condition being tested - as in the comment).
If you add that (char) cast you'll get an unnecessary '& 0xff' on
everything except x86 (where you might get a byte compare to memory
instead of a sign/zero extending memory read and register compare).
An alternative way of stopping the compiler complaining would be:
if (sign_prefix != *(unsigned char *)out)
I'm waiting for the 'security' people to stop worrying about string
truncation and turn their attention to casts of integers :-)
David
>
> Willy
On Sun, Mar 08, 2026 at 11:37:25AM +0000, david.laight.linux@gmail.com wrote: > From: David Laight <david.laight.linux@gmail.com> > > Update printf() so that it handles almost all the non-fp formats. > In particular: > - Left alignment. > - Zero padding. > - Alternate form "%#x" and "%#o". > - Field precision. > - Variable field width and precision. > - Width modifiers q, L, t and z. > - Conversion specifiers i, o and X (X generates lower case). > About the only things that are missing are wide chanacters and floating point. > > The tests are updated to match. > > Bloat/savings (in nolibc-test, but excluding the program) to patch 11: > (Measured for v3) > Function old new delta > _nolibc_u64toa_base.isra - 143 +143 > strerror - 78 +78 > __nolibc_sprintf_cb 58 91 +33 > itoa_r.isra 60 75 +15 > utoa_r.isra 144 - -144 > __nolibc_printf 1081 729 -352 > (All these functions include ~40 bytes for the stack protector code.) > utoa_r.isra and _nolibc_u64toa_base.isra pretty much cancel each other out. > itoa_r.isra grows slightly since it calls _nolibc_u64toa_base(). > strerror() used to be inlined, but over half of it is the stack check. > While some of the code added to __nolibc_sprintf_cb() has come out of > __nolibc_printf() 16-20 bytes is removed from the caller. > So there is a net saving of about 280 bytes (including losing a copy of > the number to ascii code). > > The later patches add code back in: > patch 13 - conversion flags " +#" +80 bytes > patch 14 - left aligning fields +38 bytes > patch 15 - zero padding and field precision +260 bytes > patch 16 - octal output +34 bytes > So probably about +130 bytes, but it will depend on what the application > actually calls and inlining decisions made by the compiler. > (All x86-64, other architectures will vary.) > > The biggest size change is probably removing the .data from strerror(). > This reduced the program binary file by 4k if it is the only initialised > data in a small program. > > Changes for v5: > - Old patches 2 to 7 have been applied to nolibc-next and removed. > - Patch 3 changed to return the correct (success/fail not length) > value from strerror_r() and to return ERANGE if the buffer is short. > - Patch 11 (prepend sign) has an updated comment. Thank you David. For me this is all OK. Great work! Willy
© 2016 - 2026 Red Hat, Inc.