From nobody Fri Apr 17 06:13:05 2026 Received: from mailtransmit05.runbox.com (mailtransmit05.runbox.com [185.226.149.38]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 19B9F21D590 for ; Mon, 23 Feb 2026 10:18:29 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.226.149.38 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771841912; cv=none; b=JMR6CFgyOY9M1NCo/NzjJ69ZB4qoQmMjkZFQIeg29No4kGKuwiWFJImQFg/e4galkebwwqreKQHF1WSmx6Cg8cYqudjd8UV+iMWV/1DR9lllEG4U6+z4WHi6/qv7ZGImcCmKdy88Xbe27YswyONWRCJszJaPJj797wKqgUBvH60= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771841912; c=relaxed/simple; bh=3oxPJqFGw6PXcn/4/15ddBROLFqIZnvBKMjY5EbkJXc=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=abGoR9dcvkoPsiiL7yVLw/e6DAmo9J/fyuF2Vjy/cHfCFjH2CilM3NQnyPldvvj3EW9BG8+gRHqayyT6onykH6JbKL+UrvnbxMLRmK+CLhPtVk+82KM4r/xwxeuj+U6rWL/CzVEGAMnMgLn86s3X+i4yDKGTWGJSCYGoVUAMWEE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=fail (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=runbox.com; dkim=pass (2048-bit key) header.d=runbox.com header.i=@runbox.com header.b=SAmLhjvx; arc=none smtp.client-ip=185.226.149.38 Authentication-Results: smtp.subspace.kernel.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=runbox.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=runbox.com header.i=@runbox.com header.b="SAmLhjvx" Received: from mailtransmit02.runbox ([10.9.9.162] helo=aibo.runbox.com) by mailtransmit05.runbox.com with esmtps (TLS1.2) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.93) (envelope-from ) id 1vuT1H-00GoES-DG; Mon, 23 Feb 2026 11:18:27 +0100 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=runbox.com; s=selector2; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To :Message-Id:Date:Subject:Cc:To:From; bh=JvXMIvt4OrGnaIUlletELQS6GM9X/V34gZK/+qwksg8=; b=SAmLhjvx+0PVNZW2DZDz2G/UpN bVZxh73K9ptkO6tWVcKoWGOhF8pHYHv65gEJg4R0qjZ54NnA21cC8Rf3jgC1nTITXdQwkaVF6UXKR F2KxTfQcLfKjBeUuUNOKDdvcMwX8VlV9Hfj5xKNsKJUYGmgXpkvQdESeYezYXG1BMwLoA1JVJOg2Q bcLCoMgd/sVjbwsHtXwHOFoVSkbcksS85lAfs1S+ZmwlosfywODKVABoywlS7X7PVGiF2E/1pqbMi RRz7zGrXIXhTfLs9bl0nxnhgZaN3Lgi68tMfXgpO6tyAC9xvg9RZtokjtb7WzlsbpTGK7CUmfL3pR yKTux0jQ==; Received: from [10.9.9.73] (helo=submission02.runbox) by mailtransmit02.runbox with esmtp (Exim 4.86_2) (envelope-from ) id 1vuT1H-0005Qm-3D; Mon, 23 Feb 2026 11:18:27 +0100 Received: by submission02.runbox with esmtpsa [Authenticated ID (1493616)] (TLS1.2:ECDHE_SECP256R1__RSA_PSS_RSAE_SHA256__AES_256_GCM:256) (Exim 4.93) id 1vuT10-006AjD-I1; Mon, 23 Feb 2026 11:18:10 +0100 From: david.laight.linux@gmail.com To: Willy Tarreau , =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= , linux-kernel@vger.kernel.org, Cheng Li Cc: David Laight Subject: [PATCH v3 next 01/17] tools/nolibc: Add _NOLIBC_OPTIMIZER_HIDE_VAR() to compiler.h Date: Mon, 23 Feb 2026 10:17:19 +0000 Message-Id: <20260223101735.2922-2-david.laight.linux@gmail.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20260223101735.2922-1-david.laight.linux@gmail.com> References: <20260223101735.2922-1-david.laight.linux@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: David Laight Needed to stop compiler 'optimisations' bloating code. Equivalent to the definition in include/linux/compiler.h Signed-off-by: David Laight --- New patch for v3.=20 tools/include/nolibc/compiler.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tools/include/nolibc/compiler.h b/tools/include/nolibc/compile= r.h index a8c7619dcdde..8e0eef434eec 100644 --- a/tools/include/nolibc/compiler.h +++ b/tools/include/nolibc/compiler.h @@ -71,4 +71,9 @@ # define __nolibc_static_assert(_t) #endif =20 +#ifndef _NOLIBC_OPTIMIZER_HIDE_VAR +/* Make the optimizer believe the variable can be manipulated arbitrarily.= */ +#define _NOLIBC_OPTIMIZER_HIDE_VAR(var) __asm__ ("" : "+r" (var)) +#endif + #endif /* _NOLIBC_COMPILER_H */ --=20 2.39.5 From nobody Fri Apr 17 06:13:05 2026 Received: from mailtransmit05.runbox.com (mailtransmit05.runbox.com [185.226.149.38]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9F92935295C for ; Mon, 23 Feb 2026 10:18:29 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.226.149.38 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771841913; cv=none; b=Qqo5njh+hZVfcZxiPZlbxDQsofNuNJDhp0+XdTgpZbcMJqpd8ngkYhOaQBe6wx1B8GeAMHqlTf+u0bu2Owiv1INaERooqs1B5DLpzsn2JpfSWMlfYc8Z0CGss4nfIHdZdDEWEHGgfTV+vqeMAnewSWtPPFOESqi4GuS0hQuEMi8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771841913; c=relaxed/simple; bh=XXoqmE35qtkAlI2h4Tu//Y2qwN9a2FDgPj3e+Ze5B8A=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=ElCFyRrhyEPPq4gtUlOkMFN4oglb9fIJiYKOknS9n+HbWjc0xpR0wjN8kbgWRwDqGZJSoQJUmIBVBhvaj0olIEV7UP591xdWd6tlxD5J7FO4OlH1veqiS2ZUKCXl/71M5ZlW1gSTwP4AD1GJwXTk47f7z5VkVYj8Nw6R5KjuxrA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=fail (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=runbox.com; dkim=pass (2048-bit key) header.d=runbox.com header.i=@runbox.com header.b=uIzEq4jI; arc=none smtp.client-ip=185.226.149.38 Authentication-Results: smtp.subspace.kernel.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=runbox.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=runbox.com header.i=@runbox.com header.b="uIzEq4jI" Received: from mailtransmit02.runbox ([10.9.9.162] helo=aibo.runbox.com) by mailtransmit05.runbox.com with esmtps (TLS1.2) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.93) (envelope-from ) id 1vuT1I-00GoEc-0P; Mon, 23 Feb 2026 11:18:28 +0100 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=runbox.com; s=selector2; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To :Message-Id:Date:Subject:Cc:To:From; bh=VFES34WMTKsm8PCwDyF9IMVHu9o0Lyg8e0g4OlrqmkU=; b=uIzEq4jIVijdHcy8nE+BK1LNRm mz9MKZ6BCBEjSld5wRs6cFp1iXntq08oTnCtZt3vmZxX1E6+5Z65amJq3CXn8EmCSSdwA8F8A1tl/ l8rKMo1qnjBB7YSi4hH6isCSfZhVdEAIbv0hofuU77RezO9CDOYk5VDjKAh0qXKBCMvJ/7wfD0A8t AysmsGdb4EyCeH+CzLWtnQ+d6Ms17nwtYC5+5rjrd3H8XMwYZ/3xtM9RaroA+ZfvSJWoyo0Etnnng 9gTwufF4GmfcKRPdzBNqZaC0WXoQKzeeIrqZge+ZlJo5859JVgcGb9/lYkzAv9wM+Q5Kh49HiS1+S y/qX3sXw==; Received: from [10.9.9.73] (helo=submission02.runbox) by mailtransmit02.runbox with esmtp (Exim 4.86_2) (envelope-from ) id 1vuT1H-0005Qu-Fw; Mon, 23 Feb 2026 11:18:27 +0100 Received: by submission02.runbox with esmtpsa [Authenticated ID (1493616)] (TLS1.2:ECDHE_SECP256R1__RSA_PSS_RSAE_SHA256__AES_256_GCM:256) (Exim 4.93) id 1vuT10-006AjD-Un; Mon, 23 Feb 2026 11:18:11 +0100 From: david.laight.linux@gmail.com To: Willy Tarreau , =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= , linux-kernel@vger.kernel.org, Cheng Li Cc: David Laight Subject: [PATCH v3 next 02/17] tools/nolibc: Optimise and common up the number to ascii functions Date: Mon, 23 Feb 2026 10:17:20 +0000 Message-Id: <20260223101735.2922-3-david.laight.linux@gmail.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20260223101735.2922-1-david.laight.linux@gmail.com> References: <20260223101735.2922-1-david.laight.linux@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: David Laight Implement u[64]to[ah]_r() using a common function that uses multiply by reciprocal to generate the least significant digit first and then reverses the string. On 32bit this is five multiplies (with 64bit product) for each output digit. I think the old utoa_r() always did 36 multiplies and a lot of subtracts - so this is likely faster even for 32bit values. Definitely better for 64bit values (especially small ones). Clearly shifts are faster for base 16, but reversing the output buffer makes a big difference. Sharing the code reduces the footprint (unless gcc decides to constant fold the functions). Definitely helps vfprintf() where the constants get loaded and a single call is done. Also makes it cheap to add octal support to vfprintf for completeness. Signed-off-by: David Laight --- This was previously a separate patch. Moved here since a major user in the printf code and the printf tests test this code. v3: Revert to the code from v1. But with a simple test not a loop. tools/include/nolibc/stdlib.h | 169 +++++++++++++++++----------------- 1 file changed, 82 insertions(+), 87 deletions(-) diff --git a/tools/include/nolibc/stdlib.h b/tools/include/nolibc/stdlib.h index f184e108ed0a..186635656c15 100644 --- a/tools/include/nolibc/stdlib.h +++ b/tools/include/nolibc/stdlib.h @@ -188,36 +188,91 @@ void *realloc(void *old_ptr, size_t new_size) return ret; } =20 -/* Converts the unsigned long integer to its hex representation into +/* Converts the unsigned 64bit integer to base ascii into * buffer , which must be long enough to store the number and the - * trailing zero (17 bytes for "ffffffffffffffff" or 9 for "ffffffff"). The - * buffer is filled from the first byte, and the number of characters emit= ted - * (not counting the trailing zero) is returned. The function is construct= ed - * in a way to optimize the code size and avoid any divide that could add a - * dependency on large external functions. + * trailing zero. The buffer is filled from the first byte, and the number + * of characters emitted (not counting the trailing zero) is returned. + * The function uses 'multiply by reciprocal' for the divisions and + * requires the caller pass the correct reciprocal. + * + * Note that unlike __div64_const32() in asm-generic/div64.h there isn't + * an extra shift done (by ___p), the reciprocal has to be lower resulting + * in a slightly low quotient. + * Keep things simple by correcting for the error. + * This also saves calculating the 'low * low' product (e2 below) which is + * very unlikely to be significant. + * + * Some maths: + * recip =3D p2 / base - e1; // With e1 < base. + * q =3D (recip * in - e2) / p2; // With e2 < p2. + * =3D base / in - (e1 * in + e2) / p2; + * > base / in - (e1 * p2 + p2) / p2; + * =3D base / in - ((e1 + 1) * p2) / p2; + * > base / in - base; + * So the maximum error is less than 'base'. + * Hence the largest possible digit is '2 * base - 1'. + * For base 10 e1 is 6 and you can get digits of 15 (eg from 2**64-1). + * Error e1 is largest for a base that is a factor of 2**64+1, the smalles= t is 274177 + * and converting 2**42-1 in base 274177 does generate a digit of 274177+2= 74175. + * This all means only a single correction is needed rather than a loop. + * + * __int128 isn't used for mips because gcc prior to 10.0 will call + * __multi3 for MIPS64r6. */ -static __attribute__((unused)) -int utoh_r(unsigned long in, char *buffer) +#define _NOLIBC_U64TOA_RECIP(base) ((base) & 1 ? ~0ull / (base) : (1ull <<= 63) / ((base) / 2)) +static __attribute__((unused, noinline)) +int _nolibc_u64toa_base(uint64_t in, char *buffer, unsigned int base, uint= 64_t recip) { - signed char pos =3D (~0UL > 0xfffffffful) ? 60 : 28; - int digits =3D 0; - int dig; + unsigned int digits =3D 0; + unsigned int dig; + uint64_t q; + char *p; =20 + /* Generate least significant digit first */ do { - dig =3D in >> pos; - in -=3D (uint64_t)dig << pos; - pos -=3D 4; - if (dig || digits || pos < 0) { - if (dig > 9) - dig +=3D 'a' - '0' - 10; - buffer[digits++] =3D '0' + dig; +#if defined(__SIZEOF_INT128__) && !defined(__mips__) + q =3D ((unsigned __int128)in * recip) >> 64; +#else + uint64_t p =3D (uint32_t)in * (recip >> 32); + q =3D (in >> 32) * (recip >> 32) + (p >> 32); + p =3D (uint32_t)p + (in >> 32) * (uint32_t)recip; + q +=3D p >> 32; +#endif + dig =3D in - q * base; + /* Correct for any rounding errors */ + if (dig >=3D base) { + dig -=3D base; + q++; } - } while (pos >=3D 0); + if (dig > 9) + dig +=3D 'a' - '0' - 10; + buffer[digits++] =3D '0' + dig; + } while ((in =3D q)); =20 buffer[digits] =3D 0; + + /* Order reverse to result */ + for (p =3D buffer + digits - 1; p > buffer; buffer++, p--) { + dig =3D *buffer; + *buffer =3D *p; + *p =3D dig; + } + return digits; } =20 +/* Converts the unsigned long integer to its hex representation into + * buffer , which must be long enough to store the number and the + * trailing zero (17 bytes for "ffffffffffffffff" or 9 for "ffffffff"). The + * buffer is filled from the first byte, and the number of characters emit= ted + * (not counting the trailing zero) is returned. + */ +static __inline__ __attribute__((unused)) +int utoh_r(unsigned long in, char *buffer) +{ + return _nolibc_u64toa_base(in, buffer, 16, _NOLIBC_U64TOA_RECIP(16)); +} + /* converts unsigned long to an hex string using the static itoa_buff= er * and returns the pointer to that string. */ @@ -233,30 +288,11 @@ char *utoh(unsigned long in) * trailing zero (21 bytes for 18446744073709551615 in 64-bit, 11 for * 4294967295 in 32-bit). The buffer is filled from the first byte, and the * number of characters emitted (not counting the trailing zero) is return= ed. - * The function is constructed in a way to optimize the code size and avoid - * any divide that could add a dependency on large external functions. */ -static __attribute__((unused)) +static __inline__ __attribute__((unused)) int utoa_r(unsigned long in, char *buffer) { - unsigned long lim; - int digits =3D 0; - int pos =3D (~0UL > 0xfffffffful) ? 19 : 9; - int dig; - - do { - for (dig =3D 0, lim =3D 1; dig < pos; dig++) - lim *=3D 10; - - if (digits || in >=3D lim || !pos) { - for (dig =3D 0; in >=3D lim; dig++) - in -=3D lim; - buffer[digits++] =3D '0' + dig; - } - } while (pos--); - - buffer[digits] =3D 0; - return digits; + return _nolibc_u64toa_base(in, buffer, 10, _NOLIBC_U64TOA_RECIP(10)); } =20 /* Converts the signed long integer to its string representation into @@ -324,34 +360,12 @@ char *utoa(unsigned long in) * buffer , which must be long enough to store the number and the * trailing zero (17 bytes for "ffffffffffffffff"). The buffer is filled f= rom * the first byte, and the number of characters emitted (not counting the - * trailing zero) is returned. The function is constructed in a way to opt= imize - * the code size and avoid any divide that could add a dependency on large - * external functions. + * trailing zero) is returned. */ -static __attribute__((unused)) +static __inline__ __attribute__((unused)) int u64toh_r(uint64_t in, char *buffer) { - signed char pos =3D 60; - int digits =3D 0; - int dig; - - do { - if (sizeof(long) >=3D 8) { - dig =3D (in >> pos) & 0xF; - } else { - /* 32-bit platforms: avoid a 64-bit shift */ - uint32_t d =3D (pos >=3D 32) ? (in >> 32) : in; - dig =3D (d >> (pos & 31)) & 0xF; - } - if (dig > 9) - dig +=3D 'a' - '0' - 10; - pos -=3D 4; - if (dig || digits || pos < 0) - buffer[digits++] =3D '0' + dig; - } while (pos >=3D 0); - - buffer[digits] =3D 0; - return digits; + return _nolibc_u64toa_base(in, buffer, 16, _NOLIBC_U64TOA_RECIP(16)); } =20 /* converts uint64_t to an hex string using the static itoa_buffer and @@ -368,31 +382,12 @@ char *u64toh(uint64_t in) * buffer , which must be long enough to store the number and the * trailing zero (21 bytes for 18446744073709551615). The buffer is filled= from * the first byte, and the number of characters emitted (not counting the - * trailing zero) is returned. The function is constructed in a way to opt= imize - * the code size and avoid any divide that could add a dependency on large - * external functions. + * trailing zero) is returned. */ -static __attribute__((unused)) +static __inline__ __attribute__((unused)) int u64toa_r(uint64_t in, char *buffer) { - unsigned long long lim; - int digits =3D 0; - int pos =3D 19; /* start with the highest possible digit */ - int dig; - - do { - for (dig =3D 0, lim =3D 1; dig < pos; dig++) - lim *=3D 10; - - if (digits || in >=3D lim || !pos) { - for (dig =3D 0; in >=3D lim; dig++) - in -=3D lim; - buffer[digits++] =3D '0' + dig; - } - } while (pos--); - - buffer[digits] =3D 0; - return digits; + return _nolibc_u64toa_base(in, buffer, 10, _NOLIBC_U64TOA_RECIP(10)); } =20 /* Converts the signed 64-bit integer to its string representation in= to --=20 2.39.5 From nobody Fri Apr 17 06:13:05 2026 Received: from mailtransmit04.runbox.com (mailtransmit04.runbox.com [185.226.149.37]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 14F831A9F94 for ; Mon, 23 Feb 2026 10:38:13 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.226.149.37 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771843095; cv=none; b=UI4kq5KEmcRrw3AxZQLsLLaJJd+wOqzVmzsq/c5rGXx3BLpG7V04gbLN2SDPR02DI5FlH/7xMnA+DPsUsm1CvGRApnbBYgnEJNirBxSnIqvYdCbkuzJ8VgLTiJqG2P//tmPzA7x4JfWJaCkJwa+Xe3Q+pAvLl4zy8SHwbU5hcy8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771843095; c=relaxed/simple; bh=/RLgX+p4F+wV7OtpiM7GLS3gMHCyw0csh3Xj+1QbdLc=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=kU6KO1f6hMDSbaVQgysjfdlgzfoRWPzNERz6w4K+vXIY0l/h64bjDcxZL4QzfBQWk4vj6kk3D6sKCbeZOIWIbvESTdYCT4KXhnAGFHPPMmPRuF+DjsGieh+yXoQUw0GARU8bGocjmi/BKyYYxgRq8rEz7VZiwgdViRt8nlbi1bo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=fail (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=runbox.com; dkim=pass (2048-bit key) header.d=runbox.com header.i=@runbox.com header.b=EE7uN/fS; arc=none smtp.client-ip=185.226.149.37 Authentication-Results: smtp.subspace.kernel.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=runbox.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=runbox.com header.i=@runbox.com header.b="EE7uN/fS" Received: from mailtransmit02.runbox ([10.9.9.162] helo=aibo.runbox.com) by mailtransmit04.runbox.com with esmtps (TLS1.2) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.93) (envelope-from ) id 1vuT1D-00GgpI-BH; Mon, 23 Feb 2026 11:18:23 +0100 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=runbox.com; s=selector2; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To :Message-Id:Date:Subject:Cc:To:From; bh=xR8vwnb7Qy5aGBmBq6caH16VdDDYRePCCUdIloUYHho=; b=EE7uN/fSj/P/yXvgIP2EjWNZ2L bDiwIkhhoM0zEiSlzKZQnKg7xt7K2ofL98PnkEQ+NCs44b/mYa4MQK3pWBhBU+ozrOHnu2zKsyb1V v/qLDK3l9VyGV2nmmi67C5yW8yAfPfm5u5UyuGjRznNGgZtvEZ2vzPNEtNTLCh0T2pkAW8x9VIvhB +XnbkO2UqxAb/bQL7Fam7XOor91MH0OCkMdON/upmYoiHOyPzgHlB7QSIq8CcJlHe0HA0VdMaTuQ9 0Akg1xOq3elPB+z5ttdxx3U3itKET12EBzifUyl/iJ6QIgYWoiW6zGDWhxYBS0+S9OXHfdXEYL/ym ZPDwdlWA==; Received: from [10.9.9.73] (helo=submission02.runbox) by mailtransmit02.runbox with esmtp (Exim 4.86_2) (envelope-from ) id 1vuT1D-0005Pu-1H; Mon, 23 Feb 2026 11:18:23 +0100 Received: by submission02.runbox with esmtpsa [Authenticated ID (1493616)] (TLS1.2:ECDHE_SECP256R1__RSA_PSS_RSAE_SHA256__AES_256_GCM:256) (Exim 4.93) id 1vuT11-006AjD-DX; Mon, 23 Feb 2026 11:18:11 +0100 From: david.laight.linux@gmail.com To: Willy Tarreau , =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= , linux-kernel@vger.kernel.org, Cheng Li Cc: David Laight Subject: [PATCH v3 next 03/17] selftests/nolibc: Fix build with host headers and libc Date: Mon, 23 Feb 2026 10:17:21 +0000 Message-Id: <20260223101735.2922-4-david.laight.linux@gmail.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20260223101735.2922-1-david.laight.linux@gmail.com> References: <20260223101735.2922-1-david.laight.linux@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: David Laight Many systems don't have strlcpy() or strlcat() and readdir_r() is deprecate= d. This makes the tests fail to build with the host headers. Disable the 'directories' test and define strlcpy(), strlcat() and readdir_r() using #defines so that the code compiles. Fixes: 6fe8360b16acb ("selftests/nolibc: also test libc-test through regul= ar selftest framework") Signed-off-by: David Laight --- For v3 (no v2): - Now part of the patch series that enhances _nolibc_printf(). - Disable the test that uses readdir_r() and #defines readdir_r() instead of disabling compiler warnings. - No need to change is_nolibc, doesn't help. Basically the nolibc-test program makefiles don't work without this change. tools/testing/selftests/nolibc/nolibc-test.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/s= elftests/nolibc/nolibc-test.c index 1b9d3b2e2491..0e8b3b9a86ef 100644 --- a/tools/testing/selftests/nolibc/nolibc-test.c +++ b/tools/testing/selftests/nolibc/nolibc-test.c @@ -74,6 +74,20 @@ static const int is_nolibc =3D #endif ; =20 +#if !defined(NOLIBC) +/* Some disabled tests may not compile. */ + +/* strlcat() and strlcpy() may not be in the system headers. */ +#undef strlcat +#undef strlcpy +#define strlcat(d, s, l) 0 +#define strlcpy(d, s, l) 0 + +/* readdir_r() is likely to be marked deprecated */ +#undef readdir_r +#define readdir_r(dir, dirent, result) ((errno =3D EINVAL), -1) +#endif + /* definition of a series of tests */ struct test { const char *name; /* test name */ @@ -1408,7 +1422,7 @@ int run_syscall(int min, int max) CASE_TEST(fork); EXPECT_SYSZR(1, test_fork(FORK_STANDARD));= break; CASE_TEST(getdents64_root); EXPECT_SYSNE(1, test_getdents64("/"), -1);= break; CASE_TEST(getdents64_null); EXPECT_SYSER(1, test_getdents64("/dev/null= "), -1, ENOTDIR); break; - CASE_TEST(directories); EXPECT_SYSZR(proc, test_dirent()); break; + CASE_TEST(directories); EXPECT_SYSZR(is_nolibc && proc, test_diren= t()); break; CASE_TEST(getrandom); EXPECT_SYSZR(1, test_getrandom()); break; CASE_TEST(gettimeofday_tv); EXPECT_SYSZR(1, gettimeofday(&tv, NULL)); = break; CASE_TEST(gettimeofday_tv_tz);EXPECT_SYSZR(1, gettimeofday(&tv, &tz)); b= reak; --=20 2.39.5 From nobody Fri Apr 17 06:13:05 2026 Received: from mailtransmit04.runbox.com (mailtransmit04.runbox.com [185.226.149.37]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A448634D4DE for ; Mon, 23 Feb 2026 11:10:41 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.226.149.37 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771845043; cv=none; b=ihfBk06hfGsWPOpB1wmCT0VJooVt08hYEPNc4eZSCEFH8N/wvvBWUA/l9fxUHkbn02xEbD75+EKsbB+2OVFt1QsDUlYvD3it4RBh71PijtHm0dKf0H5HbAH0lqbsKLIY8kWElr6XDWN5C6Ru9Z+DcKbp6Mx/V26xL3RE8LcRX84= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771845043; c=relaxed/simple; bh=u+6JTbvh2s4QJvC1bDHd0x+KO/CfIj8Uvl/clpJ2aLk=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=b7X+5J67ortUWOK22DhbEex3m9K3+GGpQl2FPfZJJw2m4QDRMilXhkTWTnCmpQT2fMiMFuq89KRFmavNTqZzC5aA5s0p3/vK97PB2u2h+l9/FL9JTRNMeMiFsfvmAWyWhcbjS0NkkmPvm1q7VCT44coGmbpkWIlojNmlWkwXZzc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=fail (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=runbox.com; dkim=pass (2048-bit key) header.d=runbox.com header.i=@runbox.com header.b=HmCyku0b; arc=none smtp.client-ip=185.226.149.37 Authentication-Results: smtp.subspace.kernel.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=runbox.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=runbox.com header.i=@runbox.com header.b="HmCyku0b" Received: from mailtransmit03.runbox ([10.9.9.163] helo=aibo.runbox.com) by mailtransmit04.runbox.com with esmtps (TLS1.2) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.93) (envelope-from ) id 1vuT1C-00GgpC-Uz; Mon, 23 Feb 2026 11:18:22 +0100 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=runbox.com; s=selector2; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To :Message-Id:Date:Subject:Cc:To:From; bh=fIcCZ7m9f/bsnL6B1y6KWqhrBG/OU5qL3DBfDr8Qszo=; b=HmCyku0bTGFz/deJejW8nFFLse DIsbTXXuE+O3JPFbNg2fDEElwFry4/OA8aWLA68TnNWLlpTrZ+CAOcPfvh+6pAxrl2G1X8480kfmg f7Iqyltiocag9bzpOdKnn8odsQZhEEidDl0alu1CdbxT4db1QMfDJAR0+BBhpnF0HOoAYd97yYuo/ 39AbMcddikMo4kNM/5Ez5YENZfir4KcXB6WAbxT7h81yhS2ivIsia3DckDMHDsq44xE/xH1+drHjV 89nqn+8H7nIoCieSBk1kPk3EIwBqxoCBiBsCvhPXpJfyr3cXJqrvAa5pdkTlezdiOvIFYRqB8ks/1 qqDSNHEQ==; Received: from [10.9.9.73] (helo=submission02.runbox) by mailtransmit03.runbox with esmtp (Exim 4.86_2) (envelope-from ) id 1vuT1C-0001Su-KY; Mon, 23 Feb 2026 11:18:22 +0100 Received: by submission02.runbox with esmtpsa [Authenticated ID (1493616)] (TLS1.2:ECDHE_SECP256R1__RSA_PSS_RSAE_SHA256__AES_256_GCM:256) (Exim 4.93) id 1vuT11-006AjD-PZ; Mon, 23 Feb 2026 11:18:11 +0100 From: david.laight.linux@gmail.com To: Willy Tarreau , =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= , linux-kernel@vger.kernel.org, Cheng Li Cc: David Laight Subject: [PATCH v3 next 04/17] selftests/nolibc: Improve reporting of vfprintf() errors Date: Mon, 23 Feb 2026 10:17:22 +0000 Message-Id: <20260223101735.2922-5-david.laight.linux@gmail.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20260223101735.2922-1-david.laight.linux@gmail.com> References: <20260223101735.2922-1-david.laight.linux@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: David Laight Check the string matches before checking the returned length. Only print the string once when it matches. Allow for skipping tests that check non-standard behaviour. Since the 'return value' from snprintf() is really the output buffer there is no reason for the test cases to specify the numeric return value (the length of the extected output) as well as the expected output. The expected output needs to be the 'untruncated' output for tests that generate output that gets truncatedb because expect_vfprintf() uses a short buffer. This puts all the knowledge of any truncation to expect_vfprintf(). It also easier to change the maximum size, and saves counting the length of the expected string by hand when adding tests. Increase the size limit from 20 to 25 characters, changing the tests to match. This is needed to test octal conversions later on. Append a '+' to the printed output (after the final ") when the output is truncated. Additionally check that nothing beyond the end is written. The "width_trunc" test (#14) now fails because e90ce42e81381 ("tools/nolibc: implement width padding in printf()") doesn't correctly update the space in the buffer when adding pad characters. This will be addressed in a later patch. Also correctly return 1 (the number of errors) when strcmp() fails rather than the return value from strncmp() which is the signed difference between the mismatching characters. Signed-off-by: David Laight --- For v3: - Patch 9 in v2, patch 5 in v1.. - Increase the size limit to 25 in preparation for testing octal. - Change the way truncated fprintf() are handled. - Allow for tests being skipped. - Use a fixed value (0xa5) for the canary when detecting overwrites. tools/testing/selftests/nolibc/nolibc-test.c | 91 ++++++++++++++------ 1 file changed, 64 insertions(+), 27 deletions(-) diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/s= elftests/nolibc/nolibc-test.c index 0e8b3b9a86ef..029ed63e1ae4 100644 --- a/tools/testing/selftests/nolibc/nolibc-test.c +++ b/tools/testing/selftests/nolibc/nolibc-test.c @@ -1660,33 +1660,70 @@ int run_stdlib(int min, int max) return ret; } =20 -#define EXPECT_VFPRINTF(c, expected, fmt, ...) \ - ret +=3D expect_vfprintf(llen, c, expected, fmt, ##__VA_ARGS__) +#define EXPECT_VFPRINTF(cond, expected, fmt, ...) \ + ret +=3D expect_vfprintf(llen, cond, expected, fmt, ##__VA_ARGS__) =20 -static int expect_vfprintf(int llen, int c, const char *expected, const ch= ar *fmt, ...) +#define VFPRINTF_LEN 25 + +static int expect_vfprintf(int llen, int cond, const char *expected, const= char *fmt, ...) { - char buf[100]; + ssize_t written, expected_len; + char buf[VFPRINTF_LEN + 80]; + unsigned int cmp_len; va_list args; - ssize_t w; - int ret; =20 + if (!cond) { + result(llen, SKIPPED); + return 0; + } + + /* Fill and terminate buffer to check for overlong/absent writes */ + memset(buf, 0xa5, sizeof(buf) - 1); + buf[sizeof(buf) - 1] =3D 0; =20 va_start(args, fmt); - /* Only allow writing 21 bytes, to test truncation */ - w =3D vsnprintf(buf, 21, fmt, args); + /* Limit the buffer length to test truncation */ + written =3D vsnprintf(buf, VFPRINTF_LEN + 1, fmt, args); va_end(args); =20 - if (w !=3D c) { - llen +=3D printf(" written(%d) !=3D %d", (int)w, c); + llen +=3D printf(" \"%s\"", buf); + + /* If the expected output is long it will have been truncated. */ + expected_len =3D strlen(expected); + if (expected_len > VFPRINTF_LEN) { + /* Indicate truncated in test output */ + llen +=3D printf("+"); + cmp_len =3D VFPRINTF_LEN; + } else { + cmp_len =3D expected_len; + } + + if (memcmp(expected, buf, cmp_len) || buf[cmp_len]) { + /* Copy and truncate until "%.*s" supported */ + memcpy(buf, expected, cmp_len); + buf[cmp_len] =3D 0; + llen +=3D printf(" should be \"%s\"", buf); result(llen, FAIL); return 1; } =20 - llen +=3D printf(" \"%s\" =3D \"%s\"", expected, buf); - ret =3D strncmp(expected, buf, c); + if (written !=3D expected_len) { + llen +=3D printf(" written(%d) !=3D %d", (int)written, (int)expected_len= ); + result(llen, FAIL); + return 1; + } =20 - result(llen, ret ? FAIL : OK); - return ret; + /* Check for any overwrites after the actual data. */ + while (++cmp_len < sizeof(buf) - 1) { + if ((unsigned char)buf[cmp_len] !=3D 0xa5) { + llen +=3D printf(" overwrote buf[%d] with 0x%x", cmp_len, buf[cmp_len]); + result(llen, FAIL); + return 1; + } + } + + result(llen, OK); + return 0; } =20 static int test_scanf(void) @@ -1807,21 +1844,21 @@ static int run_printf(int min, int max) * test numbers. */ switch (test + __LINE__ + 1) { - CASE_TEST(empty); EXPECT_VFPRINTF(0, "", ""); break; - CASE_TEST(simple); EXPECT_VFPRINTF(3, "foo", "foo"); break; - CASE_TEST(string); EXPECT_VFPRINTF(3, "foo", "%s", "foo"); break; - CASE_TEST(number); EXPECT_VFPRINTF(4, "1234", "%d", 1234); break; - CASE_TEST(negnumber); EXPECT_VFPRINTF(5, "-1234", "%d", -1234); break; - CASE_TEST(unsigned); EXPECT_VFPRINTF(5, "12345", "%u", 12345); break; + CASE_TEST(empty); EXPECT_VFPRINTF(1, "", ""); break; + CASE_TEST(simple); EXPECT_VFPRINTF(1, "foo", "foo"); break; + CASE_TEST(string); EXPECT_VFPRINTF(1, "foo", "%s", "foo"); break; + CASE_TEST(number); EXPECT_VFPRINTF(1, "1234", "%d", 1234); break; + CASE_TEST(negnumber); EXPECT_VFPRINTF(1, "-1234", "%d", -1234); break; + CASE_TEST(unsigned); EXPECT_VFPRINTF(1, "12345", "%u", 12345); break; CASE_TEST(char); EXPECT_VFPRINTF(1, "c", "%c", 'c'); break; CASE_TEST(hex); EXPECT_VFPRINTF(1, "f", "%x", 0xf); break; - CASE_TEST(pointer); EXPECT_VFPRINTF(3, "0x1", "%p", (void *) 0x1); = break; - CASE_TEST(uintmax_t); EXPECT_VFPRINTF(20, "18446744073709551615", "%j= u", 0xffffffffffffffffULL); break; - CASE_TEST(intmax_t); EXPECT_VFPRINTF(20, "-9223372036854775807", "%j= d", 0x8000000000000001LL); break; - CASE_TEST(truncation); EXPECT_VFPRINTF(25, "01234567890123456789", "%s= ", "0123456789012345678901234"); break; - CASE_TEST(string_width); EXPECT_VFPRINTF(10, " 1", "%10s", "1");= break; - CASE_TEST(number_width); EXPECT_VFPRINTF(10, " 1", "%10d", 1); b= reak; - CASE_TEST(width_trunc); EXPECT_VFPRINTF(25, " ", "%2= 5d", 1); break; + CASE_TEST(pointer); EXPECT_VFPRINTF(1, "0x1", "%p", (void *) 0x1); = break; + CASE_TEST(uintmax_t); EXPECT_VFPRINTF(1, "18446744073709551615", "%ju= ", 0xffffffffffffffffULL); break; + CASE_TEST(intmax_t); EXPECT_VFPRINTF(1, "-9223372036854775807", "%jd= ", 0x8000000000000001LL); break; + CASE_TEST(truncation); EXPECT_VFPRINTF(1, "012345678901234567890123456= 789", "%s", "012345678901234567890123456789"); break; + CASE_TEST(string_width); EXPECT_VFPRINTF(1, " 1", "%10s", "1"); = break; + CASE_TEST(number_width); EXPECT_VFPRINTF(1, " 1", "%10d", 1); br= eak; + CASE_TEST(width_trunc); EXPECT_VFPRINTF(1, " = 1", "%30d", 1); break; CASE_TEST(scanf); EXPECT_ZR(1, test_scanf()); break; CASE_TEST(strerror); EXPECT_ZR(1, test_strerror()); break; CASE_TEST(printf_error); EXPECT_ZR(1, test_printf_error()); break; --=20 2.39.5 From nobody Fri Apr 17 06:13:05 2026 Received: from mailtransmit05.runbox.com (mailtransmit05.runbox.com [185.226.149.38]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B1C4E2BEC43 for ; Mon, 23 Feb 2026 10:48:14 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.226.149.38 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771843697; cv=none; b=K/RGx1FuPei5m+6gJ6DfXzPfTWzR7QTqaMbo6oj5lcFkaJK8GPeMY0bsOppnx8ecNc+9Xbq/bDMMuGE24oz9oyWZo+lSDqwZZcDuaIWHU0v8nXya4Pe43vVG87WHDx1NUjeK6hIrw3r+T0lB2MC9vDYyH2BwF5Lupcu9MLRmTss= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771843697; c=relaxed/simple; bh=P6Ml2UaBtGH4hK3sQz7Sa5hALD1X44N559yIFJOsIxU=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=Ce0ZrQliLWLEu5lpPKRAz5fzVg15WiNp95vDoVF+0zkWnt/XQqopqE1l2/AbMF5xnsZIxI0DVf2r+MlHQ4bnUZknm2bhdmRSvQoMAyRrpzUkNLVuLSKmUwpBsWaElNvMZK+hkK210yq1VWlyIgVkJDWSWzdHzeqwAiT8o13BsJY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=fail (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=runbox.com; dkim=pass (2048-bit key) header.d=runbox.com header.i=@runbox.com header.b=WBeFtfaV; arc=none smtp.client-ip=185.226.149.38 Authentication-Results: smtp.subspace.kernel.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=runbox.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=runbox.com header.i=@runbox.com header.b="WBeFtfaV" Received: from mailtransmit03.runbox ([10.9.9.163] helo=aibo.runbox.com) by mailtransmit05.runbox.com with esmtps (TLS1.2) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.93) (envelope-from ) id 1vuT1D-00GoDa-NW; Mon, 23 Feb 2026 11:18:23 +0100 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=runbox.com; s=selector2; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To :Message-Id:Date:Subject:Cc:To:From; bh=dbanrDQfNAigkyuhDRj9Ra0Waug2sr0tPZBE+TFICq4=; b=WBeFtfaVq3P+i6KFoj+DhoCV7w E8JjDR+ZPSsh8omLKKInufJQUYxjkgMSo1oPUnbLWJBDpWbpWVJx3EWLZxEI75gm4mcGz5kn3u5P4 w4/kt+4+sEw0ICtm6LFjrCJhD13Fe3fQyOcLbTPr/HCYjcHsnutr1mUeP3olRoN+gjR2bH8kLMcqN UyB3qRFA4hOLouvnkf+/sFr77WpGuvr01ehuFQnpDaYETtNweCxATvt/c52efSJBVFU4LS4Aae82J sRC4P5xN6RTAc4fr6n+B05z2QikkcjzZoRaQ8GclCGpUobnjMjOh17l32bCfXjbvRSD25bMHcp9pI DlJFbudA==; Received: from [10.9.9.73] (helo=submission02.runbox) by mailtransmit03.runbox with esmtp (Exim 4.86_2) (envelope-from ) id 1vuT1D-0001T0-EE; Mon, 23 Feb 2026 11:18:23 +0100 Received: by submission02.runbox with esmtpsa [Authenticated ID (1493616)] (TLS1.2:ECDHE_SECP256R1__RSA_PSS_RSAE_SHA256__AES_256_GCM:256) (Exim 4.93) id 1vuT12-006AjD-6q; Mon, 23 Feb 2026 11:18:12 +0100 From: david.laight.linux@gmail.com To: Willy Tarreau , =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= , linux-kernel@vger.kernel.org, Cheng Li Cc: David Laight Subject: [PATCH v3 next 05/17] tools/nolibc: Implement strerror() in terms of strerror_r() Date: Mon, 23 Feb 2026 10:17:23 +0000 Message-Id: <20260223101735.2922-6-david.laight.linux@gmail.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20260223101735.2922-1-david.laight.linux@gmail.com> References: <20260223101735.2922-1-david.laight.linux@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: David Laight strerror() can be the only part of a program that has a .data section. This requres 4k in the program file. Add a simple implementation of strerror_r() (ignores the length) and use that in strerror() so that the "errno=3D" string is copied at run-time. Use __builtin_memcpy() because that optimises away the input string and just writes the required constants to the target buffer. Put the check for NOLIBC_IGNORE_ERRNO into strerror_r(). Always call strerror() from __nolibc_printf(). Code size change largely depends on whether the inlining decision for strerror() changes. Change the tests to use the normal EXPECT_VFPRINTF() when testing %m. Skip the tests when !is_nolibc. Signed-off-by: David Laight --- New patch for v3. tools/include/nolibc/stdio.h | 29 ++++++++++++++------ tools/testing/selftests/nolibc/nolibc-test.c | 20 ++------------ 2 files changed, 23 insertions(+), 26 deletions(-) diff --git a/tools/include/nolibc/stdio.h b/tools/include/nolibc/stdio.h index 233318b0d0f0..2267f50d03b4 100644 --- a/tools/include/nolibc/stdio.h +++ b/tools/include/nolibc/stdio.h @@ -373,11 +373,7 @@ int __nolibc_printf(__nolibc_printf_cb cb, intptr_t st= ate, size_t n, const char outstr=3D"(null)"; } else if (c =3D=3D 'm') { -#ifdef NOLIBC_IGNORE_ERRNO - outstr =3D "unknown error"; -#else outstr =3D strerror(errno); -#endif /* NOLIBC_IGNORE_ERRNO */ } else if (c =3D=3D '%') { /* queue it verbatim */ @@ -682,14 +678,31 @@ int setvbuf(FILE *stream __attribute__((unused)), return 0; } =20 +static __attribute__((unused,)) +int strerror_r(int errnum, char *buf, size_t buflen __attribute__((unused)= )) +{ +#ifdef NOLIBC_IGNORE_ERRNO + __builtin_memcpy(buf, "unknown error", 14); + return 13; +#else + __builtin_memcpy(buf, "errno=3D", 6); + return 6 + i64toa_r(errnum, buf + 6); +#endif +} + static __attribute__((unused)) -const char *strerror(int errno) +const char *strerror(int errnum) { - static char buf[18] =3D "errno=3D"; + static char buf[18]; + char *b =3D buf; + + /* Force gcc to use 'register offset' to access buf[]. */ + _NOLIBC_OPTIMIZER_HIDE_VAR(b); =20 - i64toa_r(errno, &buf[6]); + /* Use strerror_r() to avoid having the only .data in small programs. */ + strerror_r(errnum, b, sizeof(buf)); =20 - return buf; + return b; } =20 #endif /* _NOLIBC_STDIO_H */ diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/s= elftests/nolibc/nolibc-test.c index 029ed63e1ae4..61968fdfeec0 100644 --- a/tools/testing/selftests/nolibc/nolibc-test.c +++ b/tools/testing/selftests/nolibc/nolibc-test.c @@ -1793,23 +1793,6 @@ static int test_scanf(void) return 0; } =20 -int test_strerror(void) -{ - char buf[100]; - ssize_t ret; - - memset(buf, 'A', sizeof(buf)); - - errno =3D EINVAL; - ret =3D snprintf(buf, sizeof(buf), "%m"); - if (is_nolibc) { - if (ret < 6 || memcmp(buf, "errno=3D", 6)) - return 1; - } - - return 0; -} - static int test_printf_error(void) { int fd, ret, saved_errno; @@ -1859,8 +1842,9 @@ static int run_printf(int min, int max) CASE_TEST(string_width); EXPECT_VFPRINTF(1, " 1", "%10s", "1"); = break; CASE_TEST(number_width); EXPECT_VFPRINTF(1, " 1", "%10d", 1); br= eak; CASE_TEST(width_trunc); EXPECT_VFPRINTF(1, " = 1", "%30d", 1); break; + CASE_TEST(errno); EXPECT_VFPRINTF(is_nolibc, "22:errno=3D22", "%d= :%m", errno=3D22); break; + CASE_TEST(errno-neg); EXPECT_VFPRINTF(is_nolibc, "-22: errno=3D-22"= , "%d:%12m", errno=3D-22); break; CASE_TEST(scanf); EXPECT_ZR(1, test_scanf()); break; - CASE_TEST(strerror); EXPECT_ZR(1, test_strerror()); break; CASE_TEST(printf_error); EXPECT_ZR(1, test_printf_error()); break; case __LINE__: return ret; /* must be last */ --=20 2.39.5 From nobody Fri Apr 17 06:13:05 2026 Received: from mailtransmit04.runbox.com (mailtransmit04.runbox.com [185.226.149.37]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E81AB34D4DE for ; Mon, 23 Feb 2026 11:05:36 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.226.149.37 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771844739; cv=none; b=r9MsUxUeUuN7yBdc88fW66SwiYJ4lF2IsRUIqAe8SuV19wGWqSiAMiyAdMByzZ8ZF9NT+ywfIrKoRr0mj2Y/KALfmEEcYR2V1LsaZszFwPg1QxX3AoHsmxZOVz0YJTXwSY3WN7AhsLLU/SOHJhW2gHWz7U8M7wZxajxfxwVuxRE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771844739; c=relaxed/simple; bh=7rCgFHGHeBnnL5EmbW3ZyZcMUB0S/kE4Vgo6fObk1EI=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version:Content-Type; b=ZFnR6XUEE/4mJpgvIA7BAQf8irFh50r7BhPHdxjEPZaTwQ+c6mRRiRLgoXrctL/iImHY8rlIix1DnRUczAPWxk9u80JAzrfRzrqL1b1Fgob3KxkhviFelDJEzXLCWOlbZaEySAmMRrTCbzNT6nXd0tV9jZmrrrF+Uze/dqqZA2A= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=fail (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=runbox.com; dkim=pass (2048-bit key) header.d=runbox.com header.i=@runbox.com header.b=qcF7Y+K+; arc=none smtp.client-ip=185.226.149.37 Authentication-Results: smtp.subspace.kernel.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=runbox.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=runbox.com header.i=@runbox.com header.b="qcF7Y+K+" Received: from mailtransmit03.runbox ([10.9.9.163] helo=aibo.runbox.com) by mailtransmit04.runbox.com with esmtps (TLS1.2) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.93) (envelope-from ) id 1vuT1E-00GgpY-Hu; Mon, 23 Feb 2026 11:18:24 +0100 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=runbox.com; s=selector2; h=Content-Transfer-Encoding:Content-Type:MIME-Version: References:In-Reply-To:Message-Id:Date:Subject:Cc:To:From; bh=47fiGEaVSoRcZmUIB8aeXj5G9xiyWnMvUjhTlu18Wh4=; b=qcF7Y+K+aDiFMBGx+ffCyeahSL /amsIvDxDks4DobTlpqx9LVss8dP4zjB2UmzlzrTNL01kEXCthcuBt4hJ+nCgMsgarzJkZ9XCicxZ nCVpiG4Hojv2ZX+ix3HctcvWF9/dpO46FK5JzD8lOOAyH1ClZFSH3ei1giv5Aq/ZUyhA+fX/UAUJt Yo7ZWtQ2SL5/b+YDMMOLiFWOR9Anp8Cevavnh3QwXSbtNzLCe9YfYWl7iOWURaqSegk7B7fN0+tcE T1Rkxw0Uje9wRjuiI4pW3myiichbVsqed9Ci6n4a1LamnUoIguX8m6ZmLdypcv72zwowUWAZ+qqwG kFENTrNA==; Received: from [10.9.9.73] (helo=submission02.runbox) by mailtransmit03.runbox with esmtp (Exim 4.86_2) (envelope-from ) id 1vuT1E-0001T5-8H; Mon, 23 Feb 2026 11:18:24 +0100 Received: by submission02.runbox with esmtpsa [Authenticated ID (1493616)] (TLS1.2:ECDHE_SECP256R1__RSA_PSS_RSAE_SHA256__AES_256_GCM:256) (Exim 4.93) id 1vuT12-006AjD-Lt; Mon, 23 Feb 2026 11:18:12 +0100 From: david.laight.linux@gmail.com To: Willy Tarreau , =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= , linux-kernel@vger.kernel.org, Cheng Li Cc: David Laight Subject: [PATCH v3 next 06/17] tools/nolibc/printf: Change variables 'c' to 'ch' and 'tmpbuf[]' to 'outbuf[]' Date: Mon, 23 Feb 2026 10:17:24 +0000 Message-Id: <20260223101735.2922-7-david.laight.linux@gmail.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20260223101735.2922-1-david.laight.linux@gmail.com> References: <20260223101735.2922-1-david.laight.linux@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable From: David Laight Changing 'c' makes the code slightly easier to read because the variable stands out from the single character literals (especially 'c'). Change tmpbuf[] to outbuf[] because 'out' points into it. The following patches pretty much rewrite the function so the churn is limited. Acked-by: Willy Tarreau Acked-by: "Thomas Wei=C3=9Fschuh" Signed-off-by: David Laight --- Changes for v3: - Patch 1 in v2. - Rename tmpbuf[] to outbuf[] as well. tools/include/nolibc/stdio.h | 38 ++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/tools/include/nolibc/stdio.h b/tools/include/nolibc/stdio.h index 2267f50d03b4..a271059cf67a 100644 --- a/tools/include/nolibc/stdio.h +++ b/tools/include/nolibc/stdio.h @@ -301,16 +301,16 @@ typedef int (*__nolibc_printf_cb)(intptr_t state, con= st char *buf, size_t size); static __attribute__((unused, format(printf, 4, 0))) int __nolibc_printf(__nolibc_printf_cb cb, intptr_t state, size_t n, const= char *fmt, va_list args) { - char escape, lpref, c; + char escape, lpref, ch; unsigned long long v; unsigned int written, width; size_t len, ofs, w; - char tmpbuf[21]; + char outbuf[21]; const char *outstr; =20 written =3D ofs =3D escape =3D lpref =3D 0; while (1) { - c =3D fmt[ofs++]; + ch =3D fmt[ofs++]; width =3D 0; =20 if (escape) { @@ -318,17 +318,17 @@ int __nolibc_printf(__nolibc_printf_cb cb, intptr_t s= tate, size_t n, const char escape =3D 0; =20 /* width */ - while (c >=3D '0' && c <=3D '9') { + while (ch >=3D '0' && ch <=3D '9') { width *=3D 10; - width +=3D c - '0'; + width +=3D ch - '0'; =20 - c =3D fmt[ofs++]; + ch =3D fmt[ofs++]; } =20 - if (c =3D=3D 'c' || c =3D=3D 'd' || c =3D=3D 'u' || c =3D=3D 'x' || c = =3D=3D 'p') { - char *out =3D tmpbuf; + if (ch =3D=3D 'c' || ch =3D=3D 'd' || ch =3D=3D 'u' || ch =3D=3D 'x' ||= ch =3D=3D 'p') { + char *out =3D outbuf; =20 - if (c =3D=3D 'p') + if (ch =3D=3D 'p') v =3D va_arg(args, unsigned long); else if (lpref) { if (lpref > 1) @@ -338,7 +338,7 @@ int __nolibc_printf(__nolibc_printf_cb cb, intptr_t sta= te, size_t n, const char } else v =3D va_arg(args, unsigned int); =20 - if (c =3D=3D 'd') { + if (ch =3D=3D 'd') { /* sign-extend the value */ if (lpref =3D=3D 0) v =3D (long long)(int)v; @@ -346,7 +346,7 @@ int __nolibc_printf(__nolibc_printf_cb cb, intptr_t sta= te, size_t n, const char v =3D (long long)(long)v; } =20 - switch (c) { + switch (ch) { case 'c': out[0] =3D v; out[1] =3D 0; @@ -365,26 +365,26 @@ int __nolibc_printf(__nolibc_printf_cb cb, intptr_t s= tate, size_t n, const char u64toh_r(v, out); break; } - outstr =3D tmpbuf; + outstr =3D outbuf; } - else if (c =3D=3D 's') { + else if (ch =3D=3D 's') { outstr =3D va_arg(args, char *); if (!outstr) outstr=3D"(null)"; } - else if (c =3D=3D 'm') { + else if (ch =3D=3D 'm') { outstr =3D strerror(errno); } - else if (c =3D=3D '%') { + else if (ch =3D=3D '%') { /* queue it verbatim */ continue; } else { /* modifiers or final 0 */ - if (c =3D=3D 'l') { + if (ch =3D=3D 'l') { /* long format prefix, maintain the escape */ lpref++; - } else if (c =3D=3D 'j') { + } else if (ch =3D=3D 'j') { lpref =3D 2; } escape =3D 1; @@ -395,7 +395,7 @@ int __nolibc_printf(__nolibc_printf_cb cb, intptr_t sta= te, size_t n, const char } =20 /* not an escape sequence */ - if (c =3D=3D 0 || c =3D=3D '%') { + if (ch =3D=3D 0 || ch =3D=3D '%') { /* flush pending data on escape or end */ escape =3D 1; lpref =3D 0; @@ -416,7 +416,7 @@ int __nolibc_printf(__nolibc_printf_cb cb, intptr_t sta= te, size_t n, const char =20 written +=3D len; do_escape: - if (c =3D=3D 0) + if (ch =3D=3D 0) break; fmt +=3D ofs; ofs =3D 0; --=20 2.39.5 From nobody Fri Apr 17 06:13:05 2026 Received: from mailtransmit04.runbox.com (mailtransmit04.runbox.com [185.226.149.37]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8411834E74D for ; Mon, 23 Feb 2026 10:35:47 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.226.149.37 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771842949; cv=none; b=M5z47yU2rzHtMI5h5AW/YDzsJCAxi8aDuiYK90gUk5XWX6MqRghcMp035HgZzR57LQjTZuZcVHs/hVattJz7cH9aMs0C08uNXmmjPzBveIHFHHrmu4Hypl0xmHDvkz64OZGLhEo8BpiOBsg5fHx/vnesKYgoEFg1rdsokEYz4zc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771842949; c=relaxed/simple; bh=qrqPGlM8tyPpYDfD0bJpBrAl4QDU3AeNK95Bkvri0aI=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=ZJ3CR2Qz0CGVBPFh8XpMOpzLIrPCtqVJfiRmw5vVcZpFt5U/G3R7L0xq6QqGfK/RnEa0MzHugihDUpAg6gGUJllzAMkmacVIScgcj/IWvS/LW16i7ne8Wej9TbxmgvY4LDJeaZd/UqKLdT/gKXEam+F+7lAGlyLripI2+yOYxWA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=fail (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=runbox.com; dkim=pass (2048-bit key) header.d=runbox.com header.i=@runbox.com header.b=iIoslzm7; arc=none smtp.client-ip=185.226.149.37 Authentication-Results: smtp.subspace.kernel.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=runbox.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=runbox.com header.i=@runbox.com header.b="iIoslzm7" Received: from mailtransmit03.runbox ([10.9.9.163] helo=aibo.runbox.com) by mailtransmit04.runbox.com with esmtps (TLS1.2) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.93) (envelope-from ) id 1vuT1A-00GgoX-Jv; Mon, 23 Feb 2026 11:18:20 +0100 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=runbox.com; s=selector2; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To :Message-Id:Date:Subject:Cc:To:From; bh=9qWJcMC6R8wI95vbEiZTwzfoOGH5nPzcpuWTfNiHHs4=; b=iIoslzm7ms6yyRv2/eTVCKHY5S x0ke9FH5dhbl+Kl3IaEjpJtNF8u5/5lrvqo8jKzaXfhLiwyeEpip75KP1O7l6Jt+RegT1QDKUkeQJ 1hJsWVeohkaxbpLnib+8l3oT2qrOrmcM5wK7Vqk4YeM2ormU5j6/kbGVXcD0AZsR4GmOC5lttym7C M2nYkPSY9SR3QtiHKCRyOXrwD4Lv++nZAn0hQe+J/g/5k30xNBa8ZMIB8myRcCxjLl3VCH7mbWKh1 3rDPWsWdHRHqlRtcxfyOI6LI9e3fofbAEIhT3F2t0ZKOICC5wUGca92v6e4etigSiOYUyzlVpmUk0 1Z7apBvA==; Received: from [10.9.9.73] (helo=submission02.runbox) by mailtransmit03.runbox with esmtp (Exim 4.86_2) (envelope-from ) id 1vuT1A-0001Rs-5j; Mon, 23 Feb 2026 11:18:20 +0100 Received: by submission02.runbox with esmtpsa [Authenticated ID (1493616)] (TLS1.2:ECDHE_SECP256R1__RSA_PSS_RSAE_SHA256__AES_256_GCM:256) (Exim 4.93) id 1vuT13-006AjD-1a; Mon, 23 Feb 2026 11:18:13 +0100 From: david.laight.linux@gmail.com To: Willy Tarreau , =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= , linux-kernel@vger.kernel.org, Cheng Li Cc: David Laight Subject: [PATCH v3 next 07/17] tools/nolibc/printf: Move snprintf length check to callback Date: Mon, 23 Feb 2026 10:17:25 +0000 Message-Id: <20260223101735.2922-8-david.laight.linux@gmail.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20260223101735.2922-1-david.laight.linux@gmail.com> References: <20260223101735.2922-1-david.laight.linux@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: David Laight Move output truncation to the snprintf() callback. This simplifies the main code and fixes truncation of padded fields. Add a zero length callback to 'finalise' the buffer rather than doing it in snprintf() itself. Fixes e90ce42e81381 ("tools/nolibc: implement width padding in printf()") Signed-off-by: David Laight --- For v3: - Patch 2 in v2, patch 1 in v1. - Note that it fixes e90ce42e81381. - Update comments (again). - Rename size =3D> space in snprintf 'state. - Copy state->space to a local rather than relying on the compiler doing CSE, changes the code slightly for x86 (but not the size). - Unconditionally write a '\0' to terminate the old data before overwriting it with new data. Saves a few bytes of object code. tools/include/nolibc/stdio.h | 92 +++++++++++++++++++++++++----------- 1 file changed, 65 insertions(+), 27 deletions(-) diff --git a/tools/include/nolibc/stdio.h b/tools/include/nolibc/stdio.h index a271059cf67a..5c8f233336b9 100644 --- a/tools/include/nolibc/stdio.h +++ b/tools/include/nolibc/stdio.h @@ -295,16 +295,25 @@ int fseek(FILE *stream, long offset, int whence) * - %[l*]{d,u,c,x,p} * - %s * - unknown modifiers are ignored. + * + * Called by vfprintf() and snprintf() to do the actual formatting. + * The callers provide a callback function to save the formatted data. + * The callback function is called multiple times: + * - for each group of literal characters in the format string. + * - for field padding. + * - for each conversion specifier. + * - with (NULL, 0) at the end of the __nolibc_printf. + * If the callback returns non-zero __nolibc_printf() immediately returns = -1. */ -typedef int (*__nolibc_printf_cb)(intptr_t state, const char *buf, size_t = size); +typedef int (*__nolibc_printf_cb)(void *state, const char *buf, size_t siz= e); =20 -static __attribute__((unused, format(printf, 4, 0))) -int __nolibc_printf(__nolibc_printf_cb cb, intptr_t state, size_t n, const= char *fmt, va_list args) +static __attribute__((unused, format(printf, 3, 0))) +int __nolibc_printf(__nolibc_printf_cb cb, void *state, const char *fmt, v= a_list args) { char escape, lpref, ch; unsigned long long v; unsigned int written, width; - size_t len, ofs, w; + size_t len, ofs; char outbuf[21]; const char *outstr; =20 @@ -402,17 +411,13 @@ int __nolibc_printf(__nolibc_printf_cb cb, intptr_t s= tate, size_t n, const char outstr =3D fmt; len =3D ofs - 1; flush_str: - if (n) { - w =3D len < n ? len : n; - n -=3D w; - while (width-- > w) { - if (cb(state, " ", 1) !=3D 0) - return -1; - written +=3D 1; - } - if (cb(state, outstr, w) !=3D 0) + while (width-- > len) { + if (cb(state, " ", 1) !=3D 0) return -1; + written +=3D 1; } + if (cb(state, outstr, len) !=3D 0) + return -1; =20 written +=3D len; do_escape: @@ -425,18 +430,25 @@ int __nolibc_printf(__nolibc_printf_cb cb, intptr_t s= tate, size_t n, const char =20 /* literal char, just queue it */ } + + /* Request a final '\0' be added to the snprintf() output. + * This may be the only call of the cb() function. + */ + if (cb(state, NULL, 0) !=3D 0) + return -1; + return written; } =20 -static int __nolibc_fprintf_cb(intptr_t state, const char *buf, size_t siz= e) +static int __nolibc_fprintf_cb(void *stream, const char *buf, size_t size) { - return _fwrite(buf, size, (FILE *)state); + return _fwrite(buf, size, stream); } =20 static __attribute__((unused, format(printf, 2, 0))) int vfprintf(FILE *stream, const char *fmt, va_list args) { - return __nolibc_printf(__nolibc_fprintf_cb, (intptr_t)stream, SIZE_MAX, f= mt, args); + return __nolibc_printf(__nolibc_fprintf_cb, stream, fmt, args); } =20 static __attribute__((unused, format(printf, 1, 0))) @@ -494,26 +506,52 @@ int dprintf(int fd, const char *fmt, ...) return ret; } =20 -static int __nolibc_sprintf_cb(intptr_t _state, const char *buf, size_t si= ze) +struct __nolibc_sprintf_cb_state { + char *buf; + size_t space; +}; + +static int __nolibc_sprintf_cb(void *v_state, const char *buf, size_t size) { - char **state =3D (char **)_state; + struct __nolibc_sprintf_cb_state *state =3D v_state; + size_t space =3D state->space; + char *tgt; + + /* Truncate the request to fit in the output buffer space. + * The last byte is reserved for the terminating '\0'. + * state->space can only be zero for snprintf(NULL, 0, fmt, args) + * so this normally lets through calls with 'size =3D=3D 0'. + */ + if (size >=3D space) { + if (space <=3D 1) + return 0; + size =3D space - 1; + } + tgt =3D state->buf; + + /* __nolibc_printf() ends with cb(state, NULL, 0) to request the output + * buffer be '\0' terminated. + * That will be the only cb() call for, eg, snprintf(buf, sz, ""). + * Zero lengths can occur at other times (eg "%s" for an empty string). + * Unconditionally write the '\0' byte to reduce code size, it is + * normally overwritten by the data being output. + * There is no point adding a '\0' after copied data - there is always + * another call. + */ + *tgt =3D '\0'; + state->space =3D space - size; + state->buf =3D tgt + size; + memcpy(tgt, buf, size); =20 - memcpy(*state, buf, size); - *state +=3D size; return 0; } =20 static __attribute__((unused, format(printf, 3, 0))) int vsnprintf(char *buf, size_t size, const char *fmt, va_list args) { - char *state =3D buf; - int ret; + struct __nolibc_sprintf_cb_state state =3D { .buf =3D buf, .space =3D siz= e }; =20 - ret =3D __nolibc_printf(__nolibc_sprintf_cb, (intptr_t)&state, size, fmt,= args); - if (ret < 0) - return ret; - buf[(size_t)ret < size ? (size_t)ret : size - 1] =3D '\0'; - return ret; + return __nolibc_printf(__nolibc_sprintf_cb, &state, fmt, args); } =20 static __attribute__((unused, format(printf, 3, 4))) --=20 2.39.5 From nobody Fri Apr 17 06:13:05 2026 Received: from mailtransmit04.runbox.com (mailtransmit04.runbox.com [185.226.149.37]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B9ED335BDB7 for ; Mon, 23 Feb 2026 10:35:51 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.226.149.37 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771842953; cv=none; b=BfuiVNW6aoOCdckxHmIpM2Ke/23z9NXzU7Upfe5tlAoP+SlX/l/Kglsy9khQr9zqTxMv162N17MTlmv1X9WtNy6N5OrYRY4FNvA0ElWm8fFb2dpf9zZnLqRW/FA6QdAvAFW8+ps5EwPpmbrGi6hWlReYwOJ/hu6oBbgTzqDLilA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771842953; c=relaxed/simple; bh=lPKkgsVR52SCLMuLPByNe17cg+/mQgX0Z+Yvy+82umY=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=cyDXXRVeiLz5DdEOsiZGpCWQpWCePtmJTTmcjeChr4aiwBLuk2uXdajYGzmkPQ6RIP+49kFtxXgF6HVkoD9A56zHQjKNWMhxGYe0LZ+xcdv1SU7kI/sq7UnF5ri3L0+bZrtCaspAdKVYfif9TH4ouK4Z3gBpO19glHX6oZbEMOU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=fail (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=runbox.com; dkim=pass (2048-bit key) header.d=runbox.com header.i=@runbox.com header.b=LZXGzm8m; arc=none smtp.client-ip=185.226.149.37 Authentication-Results: smtp.subspace.kernel.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=runbox.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=runbox.com header.i=@runbox.com header.b="LZXGzm8m" Received: from mailtransmit03.runbox ([10.9.9.163] helo=aibo.runbox.com) by mailtransmit04.runbox.com with esmtps (TLS1.2) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.93) (envelope-from ) id 1vuT19-00GgoN-MN; Mon, 23 Feb 2026 11:18:19 +0100 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=runbox.com; s=selector2; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To :Message-Id:Date:Subject:Cc:To:From; bh=djFFzfzthcA5Yu/a7hS1HFU5pW171okYJFXqjYJQb9g=; b=LZXGzm8mOwpnIQfhGFPlPxCNto 3RZrkep07xphZ+b1iv1XTw0fShjl9XN9rGW7X+bgSHlT2YVGqu73sEUYG5IGrQ1t2xJa9LgqJBkAz nW7jdR0nzVfi0huRN5C7rFzCqGDi0hbym8jp4Xb8/FQPbVdsLZfQZ3Nruon7E8sw4mHT3ine0xYkv HtXVAL8J7R4jsuOJ8KNVo4kvHBdK6u0QfCuuNXB2Ku6JeQEpSK6T5gw/5e4eKhDRSdh82hcQ/e2tR 4iLomflGNIPz/H6p/lDxSjMXw1AC84rMuq5L5+bg5Q4Btsi4sb7ks36/kmO0N4EDxaa6go+rdQj1l d5jpJ/vQ==; Received: from [10.9.9.73] (helo=submission02.runbox) by mailtransmit03.runbox with esmtp (Exim 4.86_2) (envelope-from ) id 1vuT19-0001Rg-Ar; Mon, 23 Feb 2026 11:18:19 +0100 Received: by submission02.runbox with esmtpsa [Authenticated ID (1493616)] (TLS1.2:ECDHE_SECP256R1__RSA_PSS_RSAE_SHA256__AES_256_GCM:256) (Exim 4.93) id 1vuT13-006AjD-FC; Mon, 23 Feb 2026 11:18:13 +0100 From: david.laight.linux@gmail.com To: Willy Tarreau , =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= , linux-kernel@vger.kernel.org, Cheng Li Cc: David Laight Subject: [PATCH v3 next 08/17] tools/nolibc/printf: Output pad characters in 16 byte chunks Date: Mon, 23 Feb 2026 10:17:26 +0000 Message-Id: <20260223101735.2922-9-david.laight.linux@gmail.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20260223101735.2922-1-david.laight.linux@gmail.com> References: <20260223101735.2922-1-david.laight.linux@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: David Laight Simple to do and saves calls to the callback function. Change variables written, width and len to 'signed int' to get better code. Acked-by: Willy Tarreau Signed-off-by: David Laight --- For v3: - Change to signed variables here rather than a later patch. tools/include/nolibc/stdio.h | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/tools/include/nolibc/stdio.h b/tools/include/nolibc/stdio.h index 5c8f233336b9..ea6c514c4b7a 100644 --- a/tools/include/nolibc/stdio.h +++ b/tools/include/nolibc/stdio.h @@ -312,8 +312,8 @@ int __nolibc_printf(__nolibc_printf_cb cb, void *state,= const char *fmt, va_list { char escape, lpref, ch; unsigned long long v; - unsigned int written, width; - size_t len, ofs; + int written, width, len; + size_t ofs; char outbuf[21]; const char *outstr; =20 @@ -411,10 +411,14 @@ int __nolibc_printf(__nolibc_printf_cb cb, void *stat= e, const char *fmt, va_list outstr =3D fmt; len =3D ofs - 1; flush_str: - while (width-- > len) { - if (cb(state, " ", 1) !=3D 0) + width -=3D len; + while (width > 0) { + /* Output pad in 16 byte blocks with the small block first. */ + int pad_len =3D ((width - 1) & 15) + 1; + width -=3D pad_len; + written +=3D pad_len; + if (cb(state, " ", pad_len) !=3D 0) return -1; - written +=3D 1; } if (cb(state, outstr, len) !=3D 0) return -1; --=20 2.39.5 From nobody Fri Apr 17 06:13:05 2026 Received: from mailtransmit05.runbox.com (mailtransmit05.runbox.com [185.226.149.38]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8A3A035CB8F for ; Mon, 23 Feb 2026 11:09:42 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.226.149.38 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771844984; cv=none; b=nSsA71Z+q3ixsgik0ezQtfb7ZqEwg3d3e/AuWf4sgwJfNKH9AMpc7k1kX0YQCbFDM3rR2Akf3GXbtYO1O//JBrrDLHgsOQ3r9+JWcc3UXPtHoRviSa/pqil2RYGVVAYKouzKoYtcfBFEeBCsz3NpykX7HBLmlbtUab9M2RjhAco= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771844984; c=relaxed/simple; bh=hW5yZnI8l3t9iNmxsvr8a8yrvZbkSUFoZUt847CW9F0=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=XmCZq7Fby16WqwEwRzAGMXQOzP+fFIm2V7XDw+iBVCdBMIOwIc90uu4w4T3FTNSKfUm5Wh5/51FCUX4zXL4skTDMQP/K9Bukp747SFwvXg9zOUC2As1PoRLy5mJdcgQ7StNLi9f8F0TZicZIRW0jLvFpXsMUDUM0jO5oJYmz8BI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=fail (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=runbox.com; dkim=pass (2048-bit key) header.d=runbox.com header.i=@runbox.com header.b=pnB2jG/Q; arc=none smtp.client-ip=185.226.149.38 Authentication-Results: smtp.subspace.kernel.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=runbox.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=runbox.com header.i=@runbox.com header.b="pnB2jG/Q" Received: from mailtransmit03.runbox ([10.9.9.163] helo=aibo.runbox.com) by mailtransmit05.runbox.com with esmtps (TLS1.2) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.93) (envelope-from ) id 1vuT1A-00GoCq-2x; Mon, 23 Feb 2026 11:18:20 +0100 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=runbox.com; s=selector2; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To :Message-Id:Date:Subject:Cc:To:From; bh=lIqWK+ETxsAU8lN5Xp0EO5Isjnb5exAhcz1Iatt/SF4=; b=pnB2jG/QAlQM+f5XNx+TQiy1PR AoBoKTl7k5wE/GHiWvE0VJ+arvQzLAwM7mUaAvsZKqzJ3ogrwiamS6cihyqWoXjWbZz9RHraLyDSq xFlnv5pUI7wetvLyXlUiFLYbn62f+Rw1l4nmWOWVw8pJXIKg/zYiLBAIAb3N+cJ2KyiC55KdwsfyT fCuF85Jl1JA9M9kATQkXlcYbeTEBaYMZBo4IHGmKbKfz+hWbD8Kr/Ov5u8yg9zW5vHsf1+dHQzysd cpld55O05G74Dt2Slsb8VK45LClXTWD6Y1obnjOChvJhnnnOcFbyzy4Wq5dngmHItkTc75vSQFMk3 N+CoOkPA==; Received: from [10.9.9.73] (helo=submission02.runbox) by mailtransmit03.runbox with esmtp (Exim 4.86_2) (envelope-from ) id 1vuT19-0001Rm-OX; Mon, 23 Feb 2026 11:18:19 +0100 Received: by submission02.runbox with esmtpsa [Authenticated ID (1493616)] (TLS1.2:ECDHE_SECP256R1__RSA_PSS_RSAE_SHA256__AES_256_GCM:256) (Exim 4.93) id 1vuT13-006AjD-R7; Mon, 23 Feb 2026 11:18:13 +0100 From: david.laight.linux@gmail.com To: Willy Tarreau , =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= , linux-kernel@vger.kernel.org, Cheng Li Cc: David Laight Subject: [PATCH v3 next 09/17] tools/nolibc/printf: Simplify __nolibc_printf() Date: Mon, 23 Feb 2026 10:17:27 +0000 Message-Id: <20260223101735.2922-10-david.laight.linux@gmail.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20260223101735.2922-1-david.laight.linux@gmail.com> References: <20260223101735.2922-1-david.laight.linux@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: David Laight Move the check for the length modifiers into the format processing between the field width and conversion specifier. This lets the loop be simplified and a 'fast scan' for a format start used. If an error is detected (eg an invalid conversion specifier) then copy the invalid format to the output buffer. Reduces code size by about 10% on x86-64. Acked-By; Willy Tarreau Signed-off-by: David Laight Acked-By; Willy Tarreau --- Some versions of gcc bloat this version by generating a jump table. All goes away in the later patches. =20 No change for v3, patch 5 in v2. tools/include/nolibc/stdio.h | 104 ++++++++++++++++++----------------- 1 file changed, 53 insertions(+), 51 deletions(-) diff --git a/tools/include/nolibc/stdio.h b/tools/include/nolibc/stdio.h index ea6c514c4b7a..52c4c4476102 100644 --- a/tools/include/nolibc/stdio.h +++ b/tools/include/nolibc/stdio.h @@ -310,28 +310,52 @@ typedef int (*__nolibc_printf_cb)(void *state, const = char *buf, size_t size); static __attribute__((unused, format(printf, 3, 0))) int __nolibc_printf(__nolibc_printf_cb cb, void *state, const char *fmt, v= a_list args) { - char escape, lpref, ch; + char lpref, ch; unsigned long long v; int written, width, len; - size_t ofs; char outbuf[21]; const char *outstr; =20 - written =3D ofs =3D escape =3D lpref =3D 0; + written =3D 0; while (1) { - ch =3D fmt[ofs++]; + outstr =3D fmt; + ch =3D *fmt++; + if (!ch) + break; + width =3D 0; + if (ch !=3D '%') { + while (*fmt && *fmt !=3D '%') + fmt++; + /* Output characters from the format string. */ + len =3D fmt - outstr; + } else { + /* we're in a format sequence */ =20 - if (escape) { - /* we're in an escape sequence, ofs =3D=3D 1 */ - escape =3D 0; + ch =3D *fmt++; =20 /* width */ while (ch >=3D '0' && ch <=3D '9') { width *=3D 10; width +=3D ch - '0'; =20 - ch =3D fmt[ofs++]; + ch =3D *fmt++; + } + + /* Length modifiers */ + if (ch =3D=3D 'l') { + lpref =3D 1; + ch =3D *fmt++; + if (ch =3D=3D 'l') { + lpref =3D 2; + ch =3D *fmt++; + } + } else if (ch =3D=3D 'j') { + /* intmax_t is long long */ + lpref =3D 2; + ch =3D *fmt++; + } else { + lpref =3D 0; } =20 if (ch =3D=3D 'c' || ch =3D=3D 'd' || ch =3D=3D 'u' || ch =3D=3D 'x' ||= ch =3D=3D 'p') { @@ -383,56 +407,34 @@ int __nolibc_printf(__nolibc_printf_cb cb, void *stat= e, const char *fmt, va_list } else if (ch =3D=3D 'm') { outstr =3D strerror(errno); - } - else if (ch =3D=3D '%') { - /* queue it verbatim */ - continue; - } - else { - /* modifiers or final 0 */ - if (ch =3D=3D 'l') { - /* long format prefix, maintain the escape */ - lpref++; - } else if (ch =3D=3D 'j') { - lpref =3D 2; + } else { + if (ch !=3D '%') { + /* Invalid format: back up to output the format characters */ + fmt =3D outstr + 1; + /* and output a '%' now. */ } - escape =3D 1; - goto do_escape; + /* %% is documented as a 'conversion specifier'. + * Any flags, precision or length modifier are ignored. + */ + width =3D 0; + outstr =3D "%"; } len =3D strlen(outstr); - goto flush_str; } =20 - /* not an escape sequence */ - if (ch =3D=3D 0 || ch =3D=3D '%') { - /* flush pending data on escape or end */ - escape =3D 1; - lpref =3D 0; - outstr =3D fmt; - len =3D ofs - 1; - flush_str: - width -=3D len; - while (width > 0) { - /* Output pad in 16 byte blocks with the small block first. */ - int pad_len =3D ((width - 1) & 15) + 1; - width -=3D pad_len; - written +=3D pad_len; - if (cb(state, " ", pad_len) !=3D 0) - return -1; - } - if (cb(state, outstr, len) !=3D 0) - return -1; + written +=3D len; =20 - written +=3D len; - do_escape: - if (ch =3D=3D 0) - break; - fmt +=3D ofs; - ofs =3D 0; - continue; + width -=3D len; + while (width > 0) { + /* Output pad in 16 byte blocks with the small block first. */ + int pad_len =3D ((width - 1) & 15) + 1; + width -=3D pad_len; + written +=3D pad_len; + if (cb(state, " ", pad_len) !=3D 0) + return -1; } - - /* literal char, just queue it */ + if (cb(state, outstr, len) !=3D 0) + return -1; } =20 /* Request a final '\0' be added to the snprintf() output. --=20 2.39.5 From nobody Fri Apr 17 06:13:05 2026 Received: from mailtransmit05.runbox.com (mailtransmit05.runbox.com [185.226.149.38]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id DED2A1607A4 for ; Mon, 23 Feb 2026 11:15:53 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.226.149.38 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771845355; cv=none; b=YvXFKANYQ9SOVpTjerFohsUQSADkZ5VytmsI30/JHiZoeE4DbuM2CCjvhtNCmgPKeS5PWI2YKLcYNr9/oYhPElTfbu2hulNFCHMEZ+zLKhJonQoJrNCszmPN05eSv95ZLEfQfcWoEnXyOIwuhEqNcctuhdTeVCeWgAKrHmE3D4k= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771845355; c=relaxed/simple; bh=u3OLHosqu2zpuE6il2u2MRcXanbkLDAPbnt4iZKBGoU=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=GgTRRf1Mwa0tCKqINNMCprp6sC+KduaK7WvjdFdAsiRFPcS4qiPHRhl7aSOYIOqOfrbJLuMunAoiQTTK6RIUi26wKuEU3NigzRvsOXaw/3OSmX5OA0ZR+tuJkWhzkmSGzf/iV5/+ZG9643of7W/6tqE6bwlDigS/QasyiSLSyeI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=fail (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=runbox.com; dkim=pass (2048-bit key) header.d=runbox.com header.i=@runbox.com header.b=dy1zQSXq; arc=none smtp.client-ip=185.226.149.38 Authentication-Results: smtp.subspace.kernel.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=runbox.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=runbox.com header.i=@runbox.com header.b="dy1zQSXq" Received: from mailtransmit03.runbox ([10.9.9.163] helo=aibo.runbox.com) by mailtransmit05.runbox.com with esmtps (TLS1.2) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.93) (envelope-from ) id 1vuT1B-00GoDA-Q1; Mon, 23 Feb 2026 11:18:21 +0100 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=runbox.com; s=selector2; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To :Message-Id:Date:Subject:Cc:To:From; bh=GiyMWdU512Vq+XIQa8+2/zbtz5PJ/ZMLSRGbZrMZmjQ=; b=dy1zQSXqHD8+SiTPDheRu4ijAs dSaLB+f+olyJlm6Ye9VCVLF4FduPiwz1qte0S4qCwxAYGgWRGtO+rRiPRHjuq7hxomITezZji2okM JRMoz0yG/Gho4hIcnIl6o7PiaBw5PY7TXWC98FXeM5u6z7cfEphbJdKY3/e5vsdVEB0Ll+5BG0jYE L/fTyLOtZ2l8/mXLTyLIk6cPtbPDGAWAJEfm3iTv36Ho607B4oh81XJn1L2elZlsQDOZLITBEdzMA 5UFnD0DKxw/UgD3iSOj8BOuxlTkNrRdlT9fdwoqogOj/HslbZi8Uaf7rMtZs7t57ucqTsRRzfyefB xXZGEiLA==; Received: from [10.9.9.73] (helo=submission02.runbox) by mailtransmit03.runbox with esmtp (Exim 4.86_2) (envelope-from ) id 1vuT1B-0001Sh-9G; Mon, 23 Feb 2026 11:18:21 +0100 Received: by submission02.runbox with esmtpsa [Authenticated ID (1493616)] (TLS1.2:ECDHE_SECP256R1__RSA_PSS_RSAE_SHA256__AES_256_GCM:256) (Exim 4.93) id 1vuT14-006AjD-7l; Mon, 23 Feb 2026 11:18:14 +0100 From: david.laight.linux@gmail.com To: Willy Tarreau , =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= , linux-kernel@vger.kernel.org, Cheng Li Cc: David Laight Subject: [PATCH v3 next 10/17] tools/nolibc/printf: Use goto and reduce indentation Date: Mon, 23 Feb 2026 10:17:28 +0000 Message-Id: <20260223101735.2922-11-david.laight.linux@gmail.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20260223101735.2922-1-david.laight.linux@gmail.com> References: <20260223101735.2922-1-david.laight.linux@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: David Laight Upcoming changes will need to use goto to jump to the code that outputs characters. Use 'goto do_output' to output a known number of characters. Use 'goto do_strlen_output' to output a '\0' terminated string. Removes a level of indentation from the format processing code. Signed-off-by: David Laight --- New patch for v3. Makes the final code look better and there is less to change if done early. tools/include/nolibc/stdio.h | 168 +++++++++++++++++++---------------- 1 file changed, 91 insertions(+), 77 deletions(-) diff --git a/tools/include/nolibc/stdio.h b/tools/include/nolibc/stdio.h index 52c4c4476102..ae96b7bebbfe 100644 --- a/tools/include/nolibc/stdio.h +++ b/tools/include/nolibc/stdio.h @@ -329,99 +329,113 @@ int __nolibc_printf(__nolibc_printf_cb cb, void *sta= te, const char *fmt, va_list fmt++; /* Output characters from the format string. */ len =3D fmt - outstr; - } else { - /* we're in a format sequence */ + goto do_output; + } =20 - ch =3D *fmt++; + /* we're in a format sequence */ =20 - /* width */ - while (ch >=3D '0' && ch <=3D '9') { - width *=3D 10; - width +=3D ch - '0'; + ch =3D *fmt++; =20 - ch =3D *fmt++; - } + /* width */ + while (ch >=3D '0' && ch <=3D '9') { + width *=3D 10; + width +=3D ch - '0'; + + ch =3D *fmt++; + } =20 - /* Length modifiers */ + /* Length modifiers */ + if (ch =3D=3D 'l') { + lpref =3D 1; + ch =3D *fmt++; if (ch =3D=3D 'l') { - lpref =3D 1; - ch =3D *fmt++; - if (ch =3D=3D 'l') { - lpref =3D 2; - ch =3D *fmt++; - } - } else if (ch =3D=3D 'j') { - /* intmax_t is long long */ lpref =3D 2; ch =3D *fmt++; - } else { - lpref =3D 0; } + } else if (ch =3D=3D 'j') { + /* intmax_t is long long */ + lpref =3D 2; + ch =3D *fmt++; + } else { + lpref =3D 0; + } =20 - if (ch =3D=3D 'c' || ch =3D=3D 'd' || ch =3D=3D 'u' || ch =3D=3D 'x' ||= ch =3D=3D 'p') { - char *out =3D outbuf; + if (ch =3D=3D 'c' || ch =3D=3D 'd' || ch =3D=3D 'u' || ch =3D=3D 'x' || = ch =3D=3D 'p') { + char *out =3D outbuf; =20 - if (ch =3D=3D 'p') + if (ch =3D=3D 'p') + v =3D va_arg(args, unsigned long); + else if (lpref) { + if (lpref > 1) + v =3D va_arg(args, unsigned long long); + else v =3D va_arg(args, unsigned long); - else if (lpref) { - if (lpref > 1) - v =3D va_arg(args, unsigned long long); - else - v =3D va_arg(args, unsigned long); - } else - v =3D va_arg(args, unsigned int); - - if (ch =3D=3D 'd') { - /* sign-extend the value */ - if (lpref =3D=3D 0) - v =3D (long long)(int)v; - else if (lpref =3D=3D 1) - v =3D (long long)(long)v; - } + } else + v =3D va_arg(args, unsigned int); =20 - switch (ch) { - case 'c': - out[0] =3D v; - out[1] =3D 0; - break; - case 'd': - i64toa_r(v, out); - break; - case 'u': - u64toa_r(v, out); - break; - case 'p': - *(out++) =3D '0'; - *(out++) =3D 'x'; - __nolibc_fallthrough; - default: /* 'x' and 'p' above */ - u64toh_r(v, out); - break; - } - outstr =3D outbuf; - } - else if (ch =3D=3D 's') { - outstr =3D va_arg(args, char *); - if (!outstr) - outstr=3D"(null)"; + if (ch =3D=3D 'd') { + /* sign-extend the value */ + if (lpref =3D=3D 0) + v =3D (long long)(int)v; + else if (lpref =3D=3D 1) + v =3D (long long)(long)v; } - else if (ch =3D=3D 'm') { - outstr =3D strerror(errno); - } else { - if (ch !=3D '%') { - /* Invalid format: back up to output the format characters */ - fmt =3D outstr + 1; - /* and output a '%' now. */ - } - /* %% is documented as a 'conversion specifier'. - * Any flags, precision or length modifier are ignored. - */ - width =3D 0; - outstr =3D "%"; + + switch (ch) { + case 'c': + out[0] =3D v; + out[1] =3D 0; + break; + case 'd': + i64toa_r(v, out); + break; + case 'u': + u64toa_r(v, out); + break; + case 'p': + *(out++) =3D '0'; + *(out++) =3D 'x'; + __nolibc_fallthrough; + default: /* 'x' and 'p' above */ + u64toh_r(v, out); + break; } - len =3D strlen(outstr); + outstr =3D outbuf; + goto do_strlen_output; } =20 + if (ch =3D=3D 's') { + outstr =3D va_arg(args, char *); + if (!outstr) + outstr=3D"(null)"; + goto do_strlen_output; + } + + if (ch =3D=3D 'm') { + outstr =3D strerror(errno); + goto do_strlen_output; + } + + if (ch !=3D '%') { + /* Invalid format: back up to output the format characters */ + fmt =3D outstr + 1; + /* and output a '%' now. */ + } + /* %% is documented as a 'conversion specifier'. + * Any flags, precision or length modifier are ignored. + */ + len =3D 1; + width =3D 0; + outstr =3D fmt - 1; + goto do_output; + +do_strlen_output: + /* Open coded strlen() (slightly smaller). */ + for (len =3D 0;; len++) + if (!outstr[len]) + break; + +do_output: written +=3D len; =20 width -=3D len; --=20 2.39.5 From nobody Fri Apr 17 06:13:05 2026 Received: from mailtransmit05.runbox.com (mailtransmit05.runbox.com [185.226.149.38]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D595E2BEC43 for ; Mon, 23 Feb 2026 10:48:26 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.226.149.38 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771843708; cv=none; b=dPbQ9qsxRDxg2ljKj2yTTeXws8cWxYHymU9vnNjujCETbzUHqbrsp1GgQovd/dipoA1E4L7Dpu8HsmqhJTtAuNIxkrDj9f1KOOcYKzzy83L2WdbkbQmds2vqgrbI0ZKvlq5vT/4KRN5DT5zXcjfRLLD5fz0kGxDqpjRN/joMNcI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771843708; c=relaxed/simple; bh=ylIhlhaEo6yfElZksGDZcgztW4ZHLYQ2JL9UcCNZk1I=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=HjfjqaC8RtUhgKzwIiA70j4Y/IpRZtK1SWZzLlFuX6Z3AkFC9QceGO1MwbpBWY9szSvwuIdZVtGKw6BPtIE60dLnfRHX7SWi64WflERmC+C4IdB9/aD0O4gNCW+fILTI1n782jFtYGvHbysJtcqRkH1s/Z6Xxt8XjLzRMLRNgmo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=fail (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=runbox.com; dkim=pass (2048-bit key) header.d=runbox.com header.i=@runbox.com header.b=qq7fISL/; arc=none smtp.client-ip=185.226.149.38 Authentication-Results: smtp.subspace.kernel.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=runbox.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=runbox.com header.i=@runbox.com header.b="qq7fISL/" Received: from mailtransmit03.runbox ([10.9.9.163] helo=aibo.runbox.com) by mailtransmit05.runbox.com with esmtps (TLS1.2) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.93) (envelope-from ) id 1vuT1B-00GoD0-87; Mon, 23 Feb 2026 11:18:21 +0100 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=runbox.com; s=selector2; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To :Message-Id:Date:Subject:Cc:To:From; bh=SpPQYt5vTvFEukQyZBkE1bS2Fw53bct4O9KkTTpoVHM=; b=qq7fISL/6dtmC/FZtFLZxKGRnu lQgvaZo8/63WERHmicVZR6B8RvOhWKby5lw5XrfWm/TLQYJEzPCUO/zdF0ePjjoF6z70sstnAxP+A jnL1ymLo8mz6UuYyv0HIeUoVUD7Q3SV6ZTZv340mBHZhhO9Cdf0F78MaTFzQ7tgDGBs8ZebZlIdJT Dn4gqtqxAl6107kxx5EPQ3A6ycTVftGpJ7gxro2Jx7+7eHxVYBPU+HrvWJytk9flokDjL4AL27BI3 dc2ZJZzbxJI79Gsa8d2n5tKt45rgMJhtoAvmx5bvRrHXz3YK8o/Zdvcc6QEQJJtEj4dmSQS+0HT2L eJXjqoew==; Received: from [10.9.9.73] (helo=submission02.runbox) by mailtransmit03.runbox with esmtp (Exim 4.86_2) (envelope-from ) id 1vuT1A-0001S8-Jz; Mon, 23 Feb 2026 11:18:21 +0100 Received: by submission02.runbox with esmtpsa [Authenticated ID (1493616)] (TLS1.2:ECDHE_SECP256R1__RSA_PSS_RSAE_SHA256__AES_256_GCM:256) (Exim 4.93) id 1vuT14-006AjD-Jy; Mon, 23 Feb 2026 11:18:14 +0100 From: david.laight.linux@gmail.com To: Willy Tarreau , =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= , linux-kernel@vger.kernel.org, Cheng Li Cc: David Laight Subject: [PATCH v3 next 11/17] tools/nolibc/printf: Use bit-masks to hold requested flag, length and conversion chars Date: Mon, 23 Feb 2026 10:17:29 +0000 Message-Id: <20260223101735.2922-12-david.laight.linux@gmail.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20260223101735.2922-1-david.laight.linux@gmail.com> References: <20260223101735.2922-1-david.laight.linux@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: David Laight Use flags bits (1u << (ch & 31)) for the flags, length modifiers, and conversion specifiers. This makes it easy to test for multiple values at once. Detect the conversion flags " #+-0" although they are currently all ignored. Add support for length modifiers 't' and 'z' (both long) and 'q' and 'L' (both long long). Add support for "%i" (the same as %d") and "%X" (treated at "%x"). Unconditionally generate the signed values (for %d) to remove a second set of checks for the size. Separate out the formatting of single characters from numbers. Output the sign for negative values then negate and treat as unsigned. Change/add tests to use conversions i and X, and length modifiers L and ll. Use the correct minimum value for "%Li". Acked-by: Willy Tarreau Signed-off-by: David Laight --- =20 Changes for v3:=20 - Patch 6 in v2. =20 - Move all the variable definitions to the top of the function. The loop body is a bit long to hide definitions at its top. - Avoid -Wtype-limits validating format characters. - Include changes to the selftests. Changes for v2: - Use #defines to make the code a lot more readable. - Include the changes from the old patch 10 that used masks for the conversion specifiers. - Detect all the valid flag characters even though they are not implemented. - Support for left justifying field is moved to patch 7. =20 tools/include/nolibc/stdio.h | 162 +++++++++++++------ tools/testing/selftests/nolibc/nolibc-test.c | 14 +- 2 files changed, 124 insertions(+), 52 deletions(-) diff --git a/tools/include/nolibc/stdio.h b/tools/include/nolibc/stdio.h index ae96b7bebbfe..6cb106367e3b 100644 --- a/tools/include/nolibc/stdio.h +++ b/tools/include/nolibc/stdio.h @@ -291,10 +291,15 @@ int fseek(FILE *stream, long offset, int whence) } =20 =20 -/* minimal printf(). It supports the following formats: - * - %[l*]{d,u,c,x,p} - * - %s - * - unknown modifiers are ignored. +/* printf(). Supports most of the normal integer and string formats. + * - %[#-+ 0][width][{l,t,z,ll,L,j,q}]{c,d,i,u,x,X,p,s,m,%} + * - %% generates a single % + * - %m outputs strerror(errno). + * - %X outputs a..f the same as %x. + * - The modifiers [#-+ 0] are currently ignored. + * - No support for precision or variable widths. + * - No support for floating point or wide characters. + * - Invalid formats are copied to the output buffer. * * Called by vfprintf() and snprintf() to do the actual formatting. * The callers provide a callback function to save the formatted data. @@ -305,15 +310,43 @@ int fseek(FILE *stream, long offset, int whence) * - with (NULL, 0) at the end of the __nolibc_printf. * If the callback returns non-zero __nolibc_printf() immediately returns = -1. */ + typedef int (*__nolibc_printf_cb)(void *state, const char *buf, size_t siz= e); =20 +/* This code uses 'flag' variables that are indexed by the low 6 bits + * of characters to optimise checks for multiple characters. + * + * _NOLIBC_PF_FLAGS_CONTAIN(flags, 'a', 'b'. ...) + * returns non-zero if the bit for any of the specified characters is set. + * + * _NOLIBC_PF_CHAR_IS_ONE_OF(ch, 'a', 'b'. ...) + * returns the flag bit for ch if it is one of the specified characters. + * All the characters must be in the same 32 character block (non-alphabet= ic, + * upper case, or lower case) of the ASCII character set. + */ +#define _NOLIBC_PF_FLAG(ch) (1u << ((ch) & 0x1f)) +#define _NOLIBC_PF_FLAG_NZ(ch) ((ch) ? _NOLIBC_PF_FLAG(ch) : 0) +#define _NOLIBC_PF_FLAG8(cmp_1, cmp_2, cmp_3, cmp_4, cmp_5, cmp_6, cmp_7, = cmp_8, ...) \ + (_NOLIBC_PF_FLAG_NZ(cmp_1) | _NOLIBC_PF_FLAG_NZ(cmp_2) | \ + _NOLIBC_PF_FLAG_NZ(cmp_3) | _NOLIBC_PF_FLAG_NZ(cmp_4) | \ + _NOLIBC_PF_FLAG_NZ(cmp_5) | _NOLIBC_PF_FLAG_NZ(cmp_6) | \ + _NOLIBC_PF_FLAG_NZ(cmp_7) | _NOLIBC_PF_FLAG_NZ(cmp_8)) +#define _NOLIBC_PF_FLAGS_CONTAIN(flags, ...) \ + ((flags) & _NOLIBC_PF_FLAG8(__VA_ARGS__, 0, 0, 0, 0, 0, 0, 0)) +#define _NOLIBC_PF_CHAR_IS_ONE_OF(ch, cmp_1, ...) \ + ((unsigned int)(ch) - (cmp_1 & 0xe0) > 0x1f ? 0 : \ + _NOLIBC_PF_FLAGS_CONTAIN(_NOLIBC_PF_FLAG(ch), cmp_1, __VA_ARGS__)) + static __attribute__((unused, format(printf, 3, 0))) int __nolibc_printf(__nolibc_printf_cb cb, void *state, const char *fmt, v= a_list args) { - char lpref, ch; + char ch; unsigned long long v; + long long signed_v; int written, width, len; + unsigned int flags, ch_flag; char outbuf[21]; + char *out; const char *outstr; =20 written =3D 0; @@ -324,6 +357,7 @@ int __nolibc_printf(__nolibc_printf_cb cb, void *state,= const char *fmt, va_list break; =20 width =3D 0; + flags =3D 0; if (ch !=3D '%') { while (*fmt && *fmt !=3D '%') fmt++; @@ -334,7 +368,14 @@ int __nolibc_printf(__nolibc_printf_cb cb, void *state= , const char *fmt, va_list =20 /* we're in a format sequence */ =20 - ch =3D *fmt++; + /* Conversion flag characters */ + while (1) { + ch =3D *fmt++; + ch_flag =3D _NOLIBC_PF_CHAR_IS_ONE_OF(ch, ' ', '#', '+', '-', '0'); + if (!ch_flag) + break; + flags |=3D ch_flag; + } =20 /* width */ while (ch >=3D '0' && ch <=3D '9') { @@ -344,62 +385,82 @@ int __nolibc_printf(__nolibc_printf_cb cb, void *stat= e, const char *fmt, va_list ch =3D *fmt++; } =20 - /* Length modifiers */ - if (ch =3D=3D 'l') { - lpref =3D 1; - ch =3D *fmt++; - if (ch =3D=3D 'l') { - lpref =3D 2; - ch =3D *fmt++; + /* Length modifier. + * They miss the conversion flags characters " #+-0" so can go into flag= s. + * Change both L and ll to q. + */ + if (ch =3D=3D 'L') + ch =3D 'q'; + ch_flag =3D _NOLIBC_PF_CHAR_IS_ONE_OF(ch, 'l', 't', 'z', 'j', 'q'); + if (ch_flag !=3D 0) { + if (ch =3D=3D 'l' && fmt[0] =3D=3D 'l') { + fmt++; + ch_flag =3D _NOLIBC_PF_FLAG('q'); } - } else if (ch =3D=3D 'j') { - /* intmax_t is long long */ - lpref =3D 2; + flags |=3D ch_flag; ch =3D *fmt++; - } else { - lpref =3D 0; } =20 - if (ch =3D=3D 'c' || ch =3D=3D 'd' || ch =3D=3D 'u' || ch =3D=3D 'x' || = ch =3D=3D 'p') { - char *out =3D outbuf; + /* Conversion specifiers. */ =20 - if (ch =3D=3D 'p') + /* Numeric and pointer conversion specifiers. + * + * Use an explicit bound check (rather than _NOLIBC_PF_CHAR_IS_ONE_OF()) + * so that 'X' can be allowed through. + * 'X' gets treated and 'x' because _NOLIBC_PF_FLAG() returns the same + * value for both. + */ + ch_flag =3D _NOLIBC_PF_FLAG(ch); + if (((ch >=3D 'a' && ch <=3D 'z') || ch =3D=3D 'X') && + _NOLIBC_PF_FLAGS_CONTAIN(ch_flag, 'c', 'd', 'i', 'u', 'x', 'p')) { + /* 'long' is needed for pointer conversions and ltz lengths. + * A single test can be used provided 'p' (the same bit as '0') + * is masked from flags. + */ + if (_NOLIBC_PF_FLAGS_CONTAIN(ch_flag | (flags & ~_NOLIBC_PF_FLAG('p')), + 'p', 'l', 't', 'z')) { v =3D va_arg(args, unsigned long); - else if (lpref) { - if (lpref > 1) - v =3D va_arg(args, unsigned long long); - else - v =3D va_arg(args, unsigned long); - } else + signed_v =3D (long)v; + } else if (_NOLIBC_PF_FLAGS_CONTAIN(flags, 'j', 'q')) { + v =3D va_arg(args, unsigned long long); + signed_v =3D v; + } else { v =3D va_arg(args, unsigned int); + signed_v =3D (int)v; + } =20 - if (ch =3D=3D 'd') { - /* sign-extend the value */ - if (lpref =3D=3D 0) - v =3D (long long)(int)v; - else if (lpref =3D=3D 1) - v =3D (long long)(long)v; + if (ch =3D=3D 'c') { + /* "%c" - single character. */ + outbuf[0] =3D v; + len =3D 1; + outstr =3D outbuf; + goto do_output; } =20 - switch (ch) { - case 'c': - out[0] =3D v; - out[1] =3D 0; - break; - case 'd': - i64toa_r(v, out); - break; - case 'u': + out =3D outbuf; + + if (_NOLIBC_PF_FLAGS_CONTAIN(ch_flag, 'd', 'i')) { + /* "%d" and "%i" - signed decimal numbers. */ + if (signed_v < 0) { + *out++ =3D '-'; + v =3D -(signed_v + 1); + v++; + } + } + + /* Convert the number to ascii in the required base. */ + if (_NOLIBC_PF_FLAGS_CONTAIN(ch_flag, 'd', 'i', 'u')) { + /* Base 10 */ u64toa_r(v, out); - break; - case 'p': - *(out++) =3D '0'; - *(out++) =3D 'x'; - __nolibc_fallthrough; - default: /* 'x' and 'p' above */ + } else { + /* Base 16 */ + if (_NOLIBC_PF_FLAGS_CONTAIN(ch_flag, 'p')) { + *(out++) =3D '0'; + *(out++) =3D 'x'; + } u64toh_r(v, out); - break; } + outstr =3D outbuf; goto do_strlen_output; } @@ -438,6 +499,9 @@ int __nolibc_printf(__nolibc_printf_cb cb, void *state,= const char *fmt, va_list do_output: written +=3D len; =20 + /* Stop gcc back-merging this code into one of the conditionals above. */ + _NOLIBC_OPTIMIZER_HIDE_VAR(len); + width -=3D len; while (width > 0) { /* Output pad in 16 byte blocks with the small block first. */ diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/s= elftests/nolibc/nolibc-test.c index 61968fdfeec0..498d3125eb24 100644 --- a/tools/testing/selftests/nolibc/nolibc-test.c +++ b/tools/testing/selftests/nolibc/nolibc-test.c @@ -1833,11 +1833,19 @@ static int run_printf(int min, int max) CASE_TEST(number); EXPECT_VFPRINTF(1, "1234", "%d", 1234); break; CASE_TEST(negnumber); EXPECT_VFPRINTF(1, "-1234", "%d", -1234); break; CASE_TEST(unsigned); EXPECT_VFPRINTF(1, "12345", "%u", 12345); break; + CASE_TEST(signed_max); EXPECT_VFPRINTF(1, "2147483647", "%i", ~0u >> 1= ); break; + CASE_TEST(signed_min); EXPECT_VFPRINTF(1, "-2147483648", "%i", (~0u >>= 1) + 1); break; + CASE_TEST(unsigned_max); EXPECT_VFPRINTF(1, "4294967295", "%u", ~0u); br= eak; CASE_TEST(char); EXPECT_VFPRINTF(1, "c", "%c", 'c'); break; - CASE_TEST(hex); EXPECT_VFPRINTF(1, "f", "%x", 0xf); break; + CASE_TEST(hex_nolibc); EXPECT_VFPRINTF(is_nolibc, "|f|d|", "|%x|%X|", = 0xf, 0xd); break; + CASE_TEST(hex_libc); EXPECT_VFPRINTF(!is_nolibc, "|f|D|", "|%x|%X|",= 0xf, 0xd); break; CASE_TEST(pointer); EXPECT_VFPRINTF(1, "0x1", "%p", (void *) 0x1); = break; - CASE_TEST(uintmax_t); EXPECT_VFPRINTF(1, "18446744073709551615", "%ju= ", 0xffffffffffffffffULL); break; - CASE_TEST(intmax_t); EXPECT_VFPRINTF(1, "-9223372036854775807", "%jd= ", 0x8000000000000001LL); break; + CASE_TEST(percent); EXPECT_VFPRINTF(1, "a%d42%69%", "a%%d%d%%%d%%",= 42, 69); break; + CASE_TEST(perc_qual); EXPECT_VFPRINTF(1, "a%d2", "a%-14l%d%d", 2); br= eak; + CASE_TEST(invalid); EXPECT_VFPRINTF(1, "a%12yx3%y42%P", "a%12yx%d%y= %d%P", 3, 42); break; + CASE_TEST(intmax_max); EXPECT_VFPRINTF(1, "9223372036854775807", "%lld= ", ~0ULL >> 1); break; + CASE_TEST(intmax_min); EXPECT_VFPRINTF(1, "-9223372036854775808", "%Li= ", (~0ULL >> 1) + 1); break; + CASE_TEST(uintmax_max); EXPECT_VFPRINTF(1, "18446744073709551615", "%ju= ", ~0ULL); break; CASE_TEST(truncation); EXPECT_VFPRINTF(1, "012345678901234567890123456= 789", "%s", "012345678901234567890123456789"); break; CASE_TEST(string_width); EXPECT_VFPRINTF(1, " 1", "%10s", "1"); = break; CASE_TEST(number_width); EXPECT_VFPRINTF(1, " 1", "%10d", 1); br= eak; --=20 2.39.5 From nobody Fri Apr 17 06:13:05 2026 Received: from mailtransmit05.runbox.com (mailtransmit05.runbox.com [185.226.149.38]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 89DBF35D5F8 for ; Mon, 23 Feb 2026 11:00:02 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.226.149.38 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771844403; cv=none; b=hJ/mL6j7Eq4RE2UcMayl54sWTo0618/P9VIOmvLBIEQT22conQPy5Zqx7vlvx7U9dnsoqKGq7o0G91W+jm6VtseYX6hUHnhErZgP8RD07ZlIKqJjOtbDHLS0DQDJqn8H4aZ66MV3Jp1C7rNDVX1r+VZO/9GkfLN9Qg0ogm2JlxE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771844403; c=relaxed/simple; bh=aJYOgWMazHKXEzhQDu/0Z1bL2ooI7Z4gE6dbEfkjNRo=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=VR2fkYQebpdDKXcbbpWU2uslOxdBxlgIt2+v4uEcsiEfx+kySnwk8mKNlqAiEmDyrLu/lKq2My/xO3e7VU9t2n/DTbb9Rwlrp6nvwMjgIzN2f94ZkzdfI0ESbam2A+IaT6TbL8/piQw7HgRMnQH9eLTTn4BNMPjMXOf3HvrRkTw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=fail (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=runbox.com; dkim=pass (2048-bit key) header.d=runbox.com header.i=@runbox.com header.b=Q9iRcJGV; arc=none smtp.client-ip=185.226.149.38 Authentication-Results: smtp.subspace.kernel.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=runbox.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=runbox.com header.i=@runbox.com header.b="Q9iRcJGV" Received: from mailtransmit03.runbox ([10.9.9.163] helo=aibo.runbox.com) by mailtransmit05.runbox.com with esmtps (TLS1.2) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.93) (envelope-from ) id 1vuT1F-00GoE6-P8; Mon, 23 Feb 2026 11:18:25 +0100 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=runbox.com; s=selector2; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To :Message-Id:Date:Subject:Cc:To:From; bh=yTV4jgxjYuWLXjqEY/GNcTWw9BdfHU0teSATs59KDG4=; b=Q9iRcJGVG34A89lAcrjeYAaAf0 eCMaSCfkYK3Vpo0STxWmoXEEwNXfZL3fL8+tRQKETQt9BS2IjmAXAyzC+tNc95MQL2QHlAIZU+kqN 4veRha2FoqbZxvqGSK7EsvMpVtrnX3ypeaA8aZnq+7L4yStjZgvrorRgY2j78UuFb3IUmQTDxVT0T FGiGiM8ddCLl4Q0oITiEcK2apsccO6gZuYmS2qBxqS6tp8OHO5CmnPY3cCPo6mjax8kH0QTMFY7FE I+F4tCk2YX0xwPv5GTJEwmgU58iJ/TfTywNw74zkOf/yRPH0jErNK4IeEjY19p5cXSX1IPsy9NbK2 JuOvBMdw==; Received: from [10.9.9.73] (helo=submission02.runbox) by mailtransmit03.runbox with esmtp (Exim 4.86_2) (envelope-from ) id 1vuT1F-0001TB-En; Mon, 23 Feb 2026 11:18:25 +0100 Received: by submission02.runbox with esmtpsa [Authenticated ID (1493616)] (TLS1.2:ECDHE_SECP256R1__RSA_PSS_RSAE_SHA256__AES_256_GCM:256) (Exim 4.93) id 1vuT15-006AjD-4o; Mon, 23 Feb 2026 11:18:15 +0100 From: david.laight.linux@gmail.com To: Willy Tarreau , =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= , linux-kernel@vger.kernel.org, Cheng Li Cc: David Laight Subject: [PATCH v3 next 12/17] tools/nolibc/printf: Handle "%s" with the numeric formats Date: Mon, 23 Feb 2026 10:17:30 +0000 Message-Id: <20260223101735.2922-13-david.laight.linux@gmail.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20260223101735.2922-1-david.laight.linux@gmail.com> References: <20260223101735.2922-1-david.laight.linux@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: David Laight Avoids the extra va_arg() call with is non-trivial on a lot of modern ABI. Signed-off-by: David Laight --- Changes for v3:=20 - Moved to its own patch (part of patch 7 in v2).. - Fix 32bit compile. tools/include/nolibc/stdio.h | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/tools/include/nolibc/stdio.h b/tools/include/nolibc/stdio.h index 6cb106367e3b..6dd27473ab7f 100644 --- a/tools/include/nolibc/stdio.h +++ b/tools/include/nolibc/stdio.h @@ -412,13 +412,13 @@ int __nolibc_printf(__nolibc_printf_cb cb, void *stat= e, const char *fmt, va_list */ ch_flag =3D _NOLIBC_PF_FLAG(ch); if (((ch >=3D 'a' && ch <=3D 'z') || ch =3D=3D 'X') && - _NOLIBC_PF_FLAGS_CONTAIN(ch_flag, 'c', 'd', 'i', 'u', 'x', 'p')) { - /* 'long' is needed for pointer conversions and ltz lengths. + _NOLIBC_PF_FLAGS_CONTAIN(ch_flag, 'c', 'd', 'i', 'u', 'x', 'p', 's')= ) { + /* 'long' is needed for pointer/string conversions and ltz lengths. * A single test can be used provided 'p' (the same bit as '0') * is masked from flags. */ if (_NOLIBC_PF_FLAGS_CONTAIN(ch_flag | (flags & ~_NOLIBC_PF_FLAG('p')), - 'p', 'l', 't', 'z')) { + 'p', 's', 'l', 't', 'z')) { v =3D va_arg(args, unsigned long); signed_v =3D (long)v; } else if (_NOLIBC_PF_FLAGS_CONTAIN(flags, 'j', 'q')) { @@ -437,6 +437,15 @@ int __nolibc_printf(__nolibc_printf_cb cb, void *state= , const char *fmt, va_list goto do_output; } =20 + if (ch =3D=3D 's') { + /* "%s" - character string. */ + outstr =3D (const char *)(uintptr_t)v; + if (!outstr) { + outstr =3D "(null)"; + } + goto do_strlen_output; + } + out =3D outbuf; =20 if (_NOLIBC_PF_FLAGS_CONTAIN(ch_flag, 'd', 'i')) { @@ -465,13 +474,6 @@ int __nolibc_printf(__nolibc_printf_cb cb, void *state= , const char *fmt, va_list goto do_strlen_output; } =20 - if (ch =3D=3D 's') { - outstr =3D va_arg(args, char *); - if (!outstr) - outstr=3D"(null)"; - goto do_strlen_output; - } - if (ch =3D=3D 'm') { outstr =3D strerror(errno); goto do_strlen_output; --=20 2.39.5 From nobody Fri Apr 17 06:13:05 2026 Received: from mailtransmit04.runbox.com (mailtransmit04.runbox.com [185.226.149.37]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 72F961A9F94 for ; Mon, 23 Feb 2026 10:35:42 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.226.149.37 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771842945; cv=none; b=nckX7WTEHbZyv0EfJvMRmbaNqzR/yxZ+1GLJlIdY6YlkCtPYqaq6m2kgksR1MZFqE621IYKrpDJdkjVGFDMzd078TEqLp04rTURc+lZjJdGQQRLe0TE0wSCZyeMSwYewVHMEuH1abVKSMIEbgVfJiDJPK9iL1yed829qPeCXtJs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771842945; c=relaxed/simple; bh=axkRjll/NjKc2b5GtSz0IVv9kHyIHeKYwF9Y6Ttb2rQ=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=pHLTTxK8lYTpNAswMViEXr51UFnaGVc36Qlsl0Oq656osCsavpK7AMgNNaU16gGKuKoiPz7O9mermi1CX79QxniI4UYm4k7PZAi/qZSvXaGuWmkON2+XgOeLkOmxwCaDVx62j0TjO6x+dwtKtBekdJ2k+tzTNugk2YFF06wMnu4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=fail (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=runbox.com; dkim=pass (2048-bit key) header.d=runbox.com header.i=@runbox.com header.b=DEBvg5M6; arc=none smtp.client-ip=185.226.149.37 Authentication-Results: smtp.subspace.kernel.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=runbox.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=runbox.com header.i=@runbox.com header.b="DEBvg5M6" Received: from mailtransmit02.runbox ([10.9.9.162] helo=aibo.runbox.com) by mailtransmit04.runbox.com with esmtps (TLS1.2) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.93) (envelope-from ) id 1vuT1F-00Ggpk-DE; Mon, 23 Feb 2026 11:18:25 +0100 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=runbox.com; s=selector2; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To :Message-Id:Date:Subject:Cc:To:From; bh=23LayPfrort6veHcKA3M54nlZOnM5X+LunSZttk0Bk4=; b=DEBvg5M6uWEshpEiQDPn4+CWHK IgG/HN/YWsk6QUs3o+D89nKjM8z5dQlVBs56QsFsdimlfEtkZMIfc/7RPwHDjmu1Y9aNbjP1/cmL9 05vl24noeHl6fFJ4bLy/5SSTIMG630gnotm1yeR+CrGFI577womHLKIufdRs1dCb32jxgfXA7TMQr OKOIsEurngfjJJYDPrrW6EjVeqCsXRW5wZzE2UzVMG5f1rmSJ5siVXZIqAlQNtBDj76dabKgDf5gc 3jn9PNdIjAHU/sWzN+GJroMq82wSbKgaT1VxNFVrTIifP0koVmOYnzAUZ1A6NZ8M1yvW5wEkiO38O O5XUxDPQ==; Received: from [10.9.9.73] (helo=submission02.runbox) by mailtransmit02.runbox with esmtp (Exim 4.86_2) (envelope-from ) id 1vuT1F-0005QG-1Z; Mon, 23 Feb 2026 11:18:25 +0100 Received: by submission02.runbox with esmtpsa [Authenticated ID (1493616)] (TLS1.2:ECDHE_SECP256R1__RSA_PSS_RSAE_SHA256__AES_256_GCM:256) (Exim 4.93) id 1vuT15-006AjD-HU; Mon, 23 Feb 2026 11:18:15 +0100 From: david.laight.linux@gmail.com To: Willy Tarreau , =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= , linux-kernel@vger.kernel.org, Cheng Li Cc: David Laight Subject: [PATCH v3 next 13/17] tools/nolibc/printf: Add support for conversion flags Date: Mon, 23 Feb 2026 10:17:31 +0000 Message-Id: <20260223101735.2922-14-david.laight.linux@gmail.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20260223101735.2922-1-david.laight.linux@gmail.com> References: <20260223101735.2922-1-david.laight.linux@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: David Laight Add support for: '+' and ' ' Sign characters for non-negative numbers. '#' adds 0x to hex numbers. outputting "(nil)" for NULL pointers. Special code for 0 is needed because "%#x" needs to generate "0" not "0x0". The code is changed to use the length returned by u64toh_r() and prepend the sign (or "0x") after conversion to reduce the changes for the next patch that inserts zero padding between the sign and digits. Add tests for "%#x", "% d", "%+d" and passing NULL to "%p". Signed-off-by: David Laight --- Changes for v3: - Was patch 7 in v2. - Changes for left alignment moved to the next patch. - Rename 'sign' 'sign_prefix'. - Include tests=20 Changes for v2: =20 - Add support for left justifying fields (removed from patch 6). - Merge in the changes from the old patch 8. - Add support for "%#x" (formally part of patch 9). tools/include/nolibc/stdio.h | 68 +++++++++++++++----- tools/testing/selftests/nolibc/nolibc-test.c | 3 + 2 files changed, 55 insertions(+), 16 deletions(-) diff --git a/tools/include/nolibc/stdio.h b/tools/include/nolibc/stdio.h index 6dd27473ab7f..c21abe085fa6 100644 --- a/tools/include/nolibc/stdio.h +++ b/tools/include/nolibc/stdio.h @@ -296,7 +296,7 @@ int fseek(FILE *stream, long offset, int whence) * - %% generates a single % * - %m outputs strerror(errno). * - %X outputs a..f the same as %x. - * - The modifiers [#-+ 0] are currently ignored. + * - The modifiers [-0] are currently ignored. * - No support for precision or variable widths. * - No support for floating point or wide characters. * - Invalid formats are copied to the output buffer. @@ -345,9 +345,10 @@ int __nolibc_printf(__nolibc_printf_cb cb, void *state= , const char *fmt, va_list long long signed_v; int written, width, len; unsigned int flags, ch_flag; - char outbuf[21]; + char outbuf[2 + 22 + 1]; char *out; const char *outstr; + unsigned int sign_prefix; =20 written =3D 0; while (1) { @@ -409,8 +410,11 @@ int __nolibc_printf(__nolibc_printf_cb cb, void *state= , const char *fmt, va_list * so that 'X' can be allowed through. * 'X' gets treated and 'x' because _NOLIBC_PF_FLAG() returns the same * value for both. + * + * We need to check for "%p" or "%#x" later, merging here gives better c= ode. + * But '#' collides with 'c' so shift right. */ - ch_flag =3D _NOLIBC_PF_FLAG(ch); + ch_flag =3D _NOLIBC_PF_FLAG(ch) | (flags & _NOLIBC_PF_FLAG('#')) >> 1; if (((ch >=3D 'a' && ch <=3D 'z') || ch =3D=3D 'X') && _NOLIBC_PF_FLAGS_CONTAIN(ch_flag, 'c', 'd', 'i', 'u', 'x', 'p', 's')= ) { /* 'long' is needed for pointer/string conversions and ltz lengths. @@ -446,32 +450,64 @@ int __nolibc_printf(__nolibc_printf_cb cb, void *stat= e, const char *fmt, va_list goto do_strlen_output; } =20 - out =3D outbuf; + /* The 'sign_prefix' can be zero, one or two ("0x") characters. */ + sign_prefix =3D 0; =20 if (_NOLIBC_PF_FLAGS_CONTAIN(ch_flag, 'd', 'i')) { /* "%d" and "%i" - signed decimal numbers. */ if (signed_v < 0) { - *out++ =3D '-'; + sign_prefix =3D '-'; v =3D -(signed_v + 1); v++; + } else if (_NOLIBC_PF_FLAGS_CONTAIN(flags, '+')) { + sign_prefix =3D '+'; + } else if (_NOLIBC_PF_FLAGS_CONTAIN(flags, ' ')) { + sign_prefix =3D ' '; } } =20 - /* Convert the number to ascii in the required base. */ - if (_NOLIBC_PF_FLAGS_CONTAIN(ch_flag, 'd', 'i', 'u')) { - /* Base 10 */ - u64toa_r(v, out); - } else { - /* Base 16 */ + /* The value is converted offset into the buffer so that + * the sign/prefix can be added in front. + * The longest digit string is 22 + 1 for octal conversions, the + * space is reserved even though octal isn't currently supported. + */ + out =3D outbuf + 2; + + if (v =3D=3D 0) { + /* There are special rules for zero. */ if (_NOLIBC_PF_FLAGS_CONTAIN(ch_flag, 'p')) { - *(out++) =3D '0'; - *(out++) =3D 'x'; + /* "%p" match glibc, precision is ignored */ + outstr =3D "(nil)"; + len =3D 5; + goto do_output; + } + /* All other formats (including "%#x") just output "0". */ + out[0] =3D '0'; + len =3D 1; + } else { + /* Convert the number to ascii in the required base. */ + if (_NOLIBC_PF_FLAGS_CONTAIN(ch_flag, 'd', 'i', 'u')) { + /* Base 10 */ + len =3D u64toa_r(v, out); + } else { + /* Base 16 */ + if (_NOLIBC_PF_FLAGS_CONTAIN(ch_flag, 'p', '#' - 1)) { + /* "%p" and "%#x" need "0x" prepending. */ + sign_prefix =3D 'x' | '0' << 8; + } + len =3D u64toh_r(v, out); } - u64toh_r(v, out); } =20 - outstr =3D outbuf; - goto do_strlen_output; + /* Add the 0, 1 or 2 ("0x") sign/prefix characters at the front. */ + for (; sign_prefix; sign_prefix >>=3D 8) { + /* Force gcc to increment len inside the loop. */ + _NOLIBC_OPTIMIZER_HIDE_VAR(len); + len++; + *--out =3D sign_prefix; + } + outstr =3D out; + goto do_output; } =20 if (ch =3D=3D 'm') { diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/s= elftests/nolibc/nolibc-test.c index 498d3125eb24..f746711af777 100644 --- a/tools/testing/selftests/nolibc/nolibc-test.c +++ b/tools/testing/selftests/nolibc/nolibc-test.c @@ -1832,6 +1832,7 @@ static int run_printf(int min, int max) CASE_TEST(string); EXPECT_VFPRINTF(1, "foo", "%s", "foo"); break; CASE_TEST(number); EXPECT_VFPRINTF(1, "1234", "%d", 1234); break; CASE_TEST(negnumber); EXPECT_VFPRINTF(1, "-1234", "%d", -1234); break; + CASE_TEST(num_sign); EXPECT_VFPRINTF(1, "| 1|+2|+3|+4|5|", "|% d|%+d= |% +d|%+ d|%#d|", 1, 2, 3, 4, 5); break; CASE_TEST(unsigned); EXPECT_VFPRINTF(1, "12345", "%u", 12345); break; CASE_TEST(signed_max); EXPECT_VFPRINTF(1, "2147483647", "%i", ~0u >> 1= ); break; CASE_TEST(signed_min); EXPECT_VFPRINTF(1, "-2147483648", "%i", (~0u >>= 1) + 1); break; @@ -1840,6 +1841,7 @@ static int run_printf(int min, int max) CASE_TEST(hex_nolibc); EXPECT_VFPRINTF(is_nolibc, "|f|d|", "|%x|%X|", = 0xf, 0xd); break; CASE_TEST(hex_libc); EXPECT_VFPRINTF(!is_nolibc, "|f|D|", "|%x|%X|",= 0xf, 0xd); break; CASE_TEST(pointer); EXPECT_VFPRINTF(1, "0x1", "%p", (void *) 0x1); = break; + CASE_TEST(pointer_NULL); EXPECT_VFPRINTF(1, "(nil)", "%p", (void *)0); b= reak; CASE_TEST(percent); EXPECT_VFPRINTF(1, "a%d42%69%", "a%%d%d%%%d%%",= 42, 69); break; CASE_TEST(perc_qual); EXPECT_VFPRINTF(1, "a%d2", "a%-14l%d%d", 2); br= eak; CASE_TEST(invalid); EXPECT_VFPRINTF(1, "a%12yx3%y42%P", "a%12yx%d%y= %d%P", 3, 42); break; @@ -1850,6 +1852,7 @@ static int run_printf(int min, int max) CASE_TEST(string_width); EXPECT_VFPRINTF(1, " 1", "%10s", "1"); = break; CASE_TEST(number_width); EXPECT_VFPRINTF(1, " 1", "%10d", 1); br= eak; CASE_TEST(width_trunc); EXPECT_VFPRINTF(1, " = 1", "%30d", 1); break; + CASE_TEST(hex_alt); EXPECT_VFPRINTF(1, "|0x1| 0x2| 0|", "|%#x|%= #5x|%#5x|", 1, 2, 0); break; CASE_TEST(errno); EXPECT_VFPRINTF(is_nolibc, "22:errno=3D22", "%d= :%m", errno=3D22); break; CASE_TEST(errno-neg); EXPECT_VFPRINTF(is_nolibc, "-22: errno=3D-22"= , "%d:%12m", errno=3D-22); break; CASE_TEST(scanf); EXPECT_ZR(1, test_scanf()); break; --=20 2.39.5 From nobody Fri Apr 17 06:13:05 2026 Received: from mailtransmit04.runbox.com (mailtransmit04.runbox.com [185.226.149.37]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 85BE41A9F8C for ; Mon, 23 Feb 2026 10:40:48 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.226.149.37 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771843250; cv=none; b=oEOJgv0JJbD0Z4wFb/2Xzu45zCtGRpcgOT5ak+4DP4IjkM41F0X91B7EkxVxLVX8NXzExx50AN7iTXMiQgAuklZUxubD3drVj3bJ988AW8SU+pKklnsHkSxPqzIFeYwrGac1BtBaueX29a+FZfpYgs518JrVZkKbRajh7hVqu1E= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771843250; c=relaxed/simple; bh=I6UJp7IqGHwY3QNXGfGmqDI7gonDxC8mrBBYCsxzif8=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=oiNX5uqnKkKTip7T973tYDANNP2qzOcVetbNLu3MjgSpKOx9o05/rIxuh9WUDA6EqH2nM1WXJ4psdRWR16+/ZxvGic56dquEINF1bo6azuG2ZI/cR2yWpzrLuKWNcp29t/lYRN7zihOVoN1qa7pFYxkNFnsWvSHtosUNB+g4m/4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=fail (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=runbox.com; dkim=pass (2048-bit key) header.d=runbox.com header.i=@runbox.com header.b=RsbszwQ1; arc=none smtp.client-ip=185.226.149.37 Authentication-Results: smtp.subspace.kernel.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=runbox.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=runbox.com header.i=@runbox.com header.b="RsbszwQ1" Received: from mailtransmit02.runbox ([10.9.9.162] helo=aibo.runbox.com) by mailtransmit04.runbox.com with esmtps (TLS1.2) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.93) (envelope-from ) id 1vuT1E-00Ggpf-V9; Mon, 23 Feb 2026 11:18:24 +0100 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=runbox.com; s=selector2; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To :Message-Id:Date:Subject:Cc:To:From; bh=PfnSLSWmlNHpxKQJ2pLW1j5n0vlSVJPWVbwcSXAZnfI=; b=RsbszwQ1eNOepMsgjCk6uVITgk f2l31stxH8YJUFSIeldBJnTykWjpBJrNTwTGrpA8FE8ZQa2uYPq6vPeaxWgySaEhsKX4cFu/5o+zH HWSWLdZO3vtI8JCYBErCUjKXtpE3UF1C0Fc6xTNHwmr+MWeGKKOcmay1gcNC4vur/doTi5871q7e2 ZRkK8CwKFVDlFTZK0LZWheaOyDgqH9L//Cp500iY0z5ivGsmVcxXAe2bSzD2WCaj8wPIUB5yLvvev 0om7sm0t99ieKW3IoUhWSPocPBAY92iMqMApyjMMaHA03mc1/ek4hGr04gnpM7see9l8hn0pEt/ty FOEaWG1g==; Received: from [10.9.9.73] (helo=submission02.runbox) by mailtransmit02.runbox with esmtp (Exim 4.86_2) (envelope-from ) id 1vuT1E-0005QA-Ku; Mon, 23 Feb 2026 11:18:24 +0100 Received: by submission02.runbox with esmtpsa [Authenticated ID (1493616)] (TLS1.2:ECDHE_SECP256R1__RSA_PSS_RSAE_SHA256__AES_256_GCM:256) (Exim 4.93) id 1vuT15-006AjD-Ue; Mon, 23 Feb 2026 11:18:16 +0100 From: david.laight.linux@gmail.com To: Willy Tarreau , =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= , linux-kernel@vger.kernel.org, Cheng Li Cc: David Laight Subject: [PATCH v3 next 14/17] tools/nolibc/printf: Add support for left aligning fields Date: Mon, 23 Feb 2026 10:17:32 +0000 Message-Id: <20260223101735.2922-15-david.laight.linux@gmail.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20260223101735.2922-1-david.laight.linux@gmail.com> References: <20260223101735.2922-1-david.laight.linux@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: David Laight Output the characters before or after the pad - writing the pad takes more = code. Include additional/changed tests Signed-off-by: David Laight --- Changes for v3: - Formally part of patch 7. tools/include/nolibc/stdio.h | 6 +++++- tools/testing/selftests/nolibc/nolibc-test.c | 4 +++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/tools/include/nolibc/stdio.h b/tools/include/nolibc/stdio.h index c21abe085fa6..482e5b143c86 100644 --- a/tools/include/nolibc/stdio.h +++ b/tools/include/nolibc/stdio.h @@ -540,7 +540,11 @@ int __nolibc_printf(__nolibc_printf_cb cb, void *state= , const char *fmt, va_list /* Stop gcc back-merging this code into one of the conditionals above. */ _NOLIBC_OPTIMIZER_HIDE_VAR(len); =20 + /* Output the characters on the required side of any padding. */ width -=3D len; + flags =3D _NOLIBC_PF_FLAGS_CONTAIN(flags, '-'); + if (flags && cb(state, outstr, len) !=3D 0) + return -1; while (width > 0) { /* Output pad in 16 byte blocks with the small block first. */ int pad_len =3D ((width - 1) & 15) + 1; @@ -549,7 +553,7 @@ int __nolibc_printf(__nolibc_printf_cb cb, void *state,= const char *fmt, va_list if (cb(state, " ", pad_len) !=3D 0) return -1; } - if (cb(state, outstr, len) !=3D 0) + if (!flags && cb(state, outstr, len) !=3D 0) return -1; } =20 diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/s= elftests/nolibc/nolibc-test.c index f746711af777..cc59c0116855 100644 --- a/tools/testing/selftests/nolibc/nolibc-test.c +++ b/tools/testing/selftests/nolibc/nolibc-test.c @@ -1851,9 +1851,11 @@ static int run_printf(int min, int max) CASE_TEST(truncation); EXPECT_VFPRINTF(1, "012345678901234567890123456= 789", "%s", "012345678901234567890123456789"); break; CASE_TEST(string_width); EXPECT_VFPRINTF(1, " 1", "%10s", "1"); = break; CASE_TEST(number_width); EXPECT_VFPRINTF(1, " 1", "%10d", 1); br= eak; + CASE_TEST(number_left); EXPECT_VFPRINTF(1, "|-5 |", "|%-8d|", -5);= break; + CASE_TEST(string_align); EXPECT_VFPRINTF(1, "|foo |", "|%-8s|", "foo= "); break; CASE_TEST(width_trunc); EXPECT_VFPRINTF(1, " = 1", "%30d", 1); break; CASE_TEST(hex_alt); EXPECT_VFPRINTF(1, "|0x1| 0x2| 0|", "|%#x|%= #5x|%#5x|", 1, 2, 0); break; - CASE_TEST(errno); EXPECT_VFPRINTF(is_nolibc, "22:errno=3D22", "%d= :%m", errno=3D22); break; + CASE_TEST(errno); EXPECT_VFPRINTF(is_nolibc, "22:errno=3D22 ",= "%d:%-12m", errno=3D22); break; CASE_TEST(errno-neg); EXPECT_VFPRINTF(is_nolibc, "-22: errno=3D-22"= , "%d:%12m", errno=3D-22); break; CASE_TEST(scanf); EXPECT_ZR(1, test_scanf()); break; CASE_TEST(printf_error); EXPECT_ZR(1, test_printf_error()); break; --=20 2.39.5 From nobody Fri Apr 17 06:13:05 2026 Received: from mailtransmit05.runbox.com (mailtransmit05.runbox.com [185.226.149.38]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B868D35D604 for ; Mon, 23 Feb 2026 10:57:21 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.226.149.38 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771844244; cv=none; b=fHxKLY5oymTPL/eS+WAtmkWIf027YWrmOTweAkGrHEeCDNsDtO7a6zdcVifqn5uE6rH+zgVoOuHFsyi25ufgT8s6nggL3jY4xN0Sim6Cn4/hT/XwlA+xFoJqIUo74brlRK2rp9gTFdeUwOLe2+sTNq8J2bEBN/8QbnHQ4tnJ+7c= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771844244; c=relaxed/simple; bh=O/Ug/S8pvV7oBR2H35/0kPAl55qIrfjUCUyJuM1W6pw=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=uhJiPSvY6uVpaic5oDGs3EoHVuEC94T+mBw5H+pR1zYp+14AEu1nFGbvg2CecbPX8SlrcOB64W2Ue3LibTU05NVm4LDxsJhrwvVByCFWIGXaPOZekXy9+wtsPxIUarBk31qweVufMpbtnbcBwLmZ4YTkLCBHPI5Te7yUbT5/0Co= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=fail (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=runbox.com; dkim=pass (2048-bit key) header.d=runbox.com header.i=@runbox.com header.b=RNuVoWkz; arc=none smtp.client-ip=185.226.149.38 Authentication-Results: smtp.subspace.kernel.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=runbox.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=runbox.com header.i=@runbox.com header.b="RNuVoWkz" Received: from mailtransmit03.runbox ([10.9.9.163] helo=aibo.runbox.com) by mailtransmit05.runbox.com with esmtps (TLS1.2) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.93) (envelope-from ) id 1vuT18-00GoCZ-N1; Mon, 23 Feb 2026 11:18:18 +0100 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=runbox.com; s=selector2; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To :Message-Id:Date:Subject:Cc:To:From; bh=05o4Bvei+MQw/FOg09GlU5+BLLldcUJ5gWL25utryfM=; b=RNuVoWkzEd++8rfB6guNY3Osn1 XTLU08t+qoIcLykOs4vLZBCf/1WDcTc3tkRG0c2ZWAk0w6c+Vql4HqJMwibfFP4e17PHmBdnwq349 8YXqgz1U4tZ3PUqnSC9OWDszavAZ2sWLN4l50WyuCmJoJlzCChrE/+VUbYeQ80wqLvbP65z9ts5hS aTy5AjmMZXRnS1TT/tfEmHkfkwNcqPf2z3TQ0cMBmCT45dsCPv8/53US2PU50mfDK4j+26fUmW1uL bf0s3HT8zlyjRxVi862G0EB03nxvw6yCBq6W1QI+dY+E4BGzfj25KreUe+LsS/Eeja8svkrW/rtNr +0KDgFEg==; Received: from [10.9.9.73] (helo=submission02.runbox) by mailtransmit03.runbox with esmtp (Exim 4.86_2) (envelope-from ) id 1vuT18-0001Qw-8k; Mon, 23 Feb 2026 11:18:18 +0100 Received: by submission02.runbox with esmtpsa [Authenticated ID (1493616)] (TLS1.2:ECDHE_SECP256R1__RSA_PSS_RSAE_SHA256__AES_256_GCM:256) (Exim 4.93) id 1vuT16-006AjD-AT; Mon, 23 Feb 2026 11:18:16 +0100 From: david.laight.linux@gmail.com To: Willy Tarreau , =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= , linux-kernel@vger.kernel.org, Cheng Li Cc: David Laight Subject: [PATCH v3 next 15/17] tools/nolibc/printf: Add support for zero padding and field precision Date: Mon, 23 Feb 2026 10:17:33 +0000 Message-Id: <20260223101735.2922-16-david.laight.linux@gmail.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20260223101735.2922-1-david.laight.linux@gmail.com> References: <20260223101735.2922-1-david.laight.linux@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: David Laight Includes support for variable field widths (eg "%*.*d"). Zero padding is limited to 31 zero characters. This is wider than the largest numeric field so shouldn't be a problem. All the standard printf formats are now supported except octal and floating point. Add tests for new features Acked-by: Willy Tarreau Signed-off-by: David Laight --- Changes for v3: - Formerly patch 8 - Extra comments. - Adjust offsets in outbuf[], support adding 31 '0' digits while still allowing enough room for octal support to be added and keeping outbuf[] at a nice round 56 bytes. =20 Changes for v2: - These changes were previously in patch 9. However you need to apply the old patch 10 to get anything like the same source. The files then more of less match apart from 'c' being renamed 'ch' and the 'magic' #defines. tools/include/nolibc/stdio.h | 77 ++++++++++++++++---- tools/testing/selftests/nolibc/nolibc-test.c | 18 ++++- 2 files changed, 79 insertions(+), 16 deletions(-) diff --git a/tools/include/nolibc/stdio.h b/tools/include/nolibc/stdio.h index 482e5b143c86..cc2870a6a7cb 100644 --- a/tools/include/nolibc/stdio.h +++ b/tools/include/nolibc/stdio.h @@ -292,12 +292,10 @@ int fseek(FILE *stream, long offset, int whence) =20 =20 /* printf(). Supports most of the normal integer and string formats. - * - %[#-+ 0][width][{l,t,z,ll,L,j,q}]{c,d,i,u,x,X,p,s,m,%} + * - %[#0-+ ][width|*[.precision|*}][{l,t,z,ll,L,j,q}]{c,d,i,u,x,X,p,s,m,= %} * - %% generates a single % * - %m outputs strerror(errno). * - %X outputs a..f the same as %x. - * - The modifiers [-0] are currently ignored. - * - No support for precision or variable widths. * - No support for floating point or wide characters. * - Invalid formats are copied to the output buffer. * @@ -343,9 +341,9 @@ int __nolibc_printf(__nolibc_printf_cb cb, void *state,= const char *fmt, va_list char ch; unsigned long long v; long long signed_v; - int written, width, len; + int written, width, precision, len; unsigned int flags, ch_flag; - char outbuf[2 + 22 + 1]; + char outbuf[2 + 31 + 22 + 1]; char *out; const char *outstr; unsigned int sign_prefix; @@ -378,12 +376,24 @@ int __nolibc_printf(__nolibc_printf_cb cb, void *stat= e, const char *fmt, va_list flags |=3D ch_flag; } =20 - /* width */ - while (ch >=3D '0' && ch <=3D '9') { - width *=3D 10; - width +=3D ch - '0'; - - ch =3D *fmt++; + /* Width and precision */ + for (;; ch =3D *fmt++) { + if (ch =3D=3D '*') { + precision =3D va_arg(args, unsigned int); + ch =3D *fmt++; + } else { + for (precision =3D 0; ch >=3D '0' && ch <=3D '9'; ch =3D *fmt++) + precision =3D precision * 10 + (ch - '0'); + } + if (_NOLIBC_PF_FLAGS_CONTAIN(flags, '.')) + break; + width =3D precision; + if (ch !=3D '.') { + /* Default precision for strings */ + precision =3D INT_MAX; + break; + } + flags |=3D _NOLIBC_PF_FLAG('.'); } =20 /* Length modifier. @@ -446,6 +456,9 @@ int __nolibc_printf(__nolibc_printf_cb cb, void *state,= const char *fmt, va_list outstr =3D (const char *)(uintptr_t)v; if (!outstr) { outstr =3D "(null)"; + /* Match glibc, nothing output if precision too small */ + len =3D precision >=3D 6 ? 6 : 0; + goto do_output; } goto do_strlen_output; } @@ -467,11 +480,11 @@ int __nolibc_printf(__nolibc_printf_cb cb, void *stat= e, const char *fmt, va_list } =20 /* The value is converted offset into the buffer so that - * the sign/prefix can be added in front. + * 31 zero pad characters and the sign/prefix can be added in front. * The longest digit string is 22 + 1 for octal conversions, the * space is reserved even though octal isn't currently supported. */ - out =3D outbuf + 2; + out =3D outbuf + 2 + 31; =20 if (v =3D=3D 0) { /* There are special rules for zero. */ @@ -481,6 +494,11 @@ int __nolibc_printf(__nolibc_printf_cb cb, void *state= , const char *fmt, va_list len =3D 5; goto do_output; } + if (!precision) { + /* Explicit %nn.0d, no digits output */ + len =3D 0; + goto prepend_sign; + } /* All other formats (including "%#x") just output "0". */ out[0] =3D '0'; len =3D 1; @@ -499,6 +517,35 @@ int __nolibc_printf(__nolibc_printf_cb cb, void *state= , const char *fmt, va_list } } =20 + /* Add zero padding */ + if (_NOLIBC_PF_FLAGS_CONTAIN(flags, '0', '.')) { + if (!_NOLIBC_PF_FLAGS_CONTAIN(flags, '.')) { + if (_NOLIBC_PF_FLAGS_CONTAIN(flags, '-')) + /* Left justify overrides zero pad */ + goto prepend_sign; + /* eg "%05d", Zero pad to field width less sign. + * Note that precision can end up negative so all + * the variables have to be 'signed int'. + */ + precision =3D width; + if (sign_prefix) { + precision--; + if (sign_prefix >=3D 256) + precision--; + } + } + if (precision > 31) + /* Don't run off the start of outbuf[], arbitrary limit + * longer than the longest number field. */ + precision =3D 31; + for (; len < precision; len++) { + /* Stop gcc generating horrid code and memset(). */ + _NOLIBC_OPTIMIZER_HIDE_VAR(len); + *--out =3D '0'; + } + } + +prepend_sign: /* Add the 0, 1 or 2 ("0x") sign/prefix characters at the front. */ for (; sign_prefix; sign_prefix >>=3D 8) { /* Force gcc to increment len inside the loop. */ @@ -529,8 +576,8 @@ int __nolibc_printf(__nolibc_printf_cb cb, void *state,= const char *fmt, va_list goto do_output; =20 do_strlen_output: - /* Open coded strlen() (slightly smaller). */ - for (len =3D 0;; len++) + /* Open coded strnlen() (slightly smaller). */ + for (len =3D 0; len < precision; len++) if (!outstr[len]) break; =20 diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/s= elftests/nolibc/nolibc-test.c index cc59c0116855..e0cfe8d12ed1 100644 --- a/tools/testing/selftests/nolibc/nolibc-test.c +++ b/tools/testing/selftests/nolibc/nolibc-test.c @@ -1838,10 +1838,12 @@ static int run_printf(int min, int max) CASE_TEST(signed_min); EXPECT_VFPRINTF(1, "-2147483648", "%i", (~0u >>= 1) + 1); break; CASE_TEST(unsigned_max); EXPECT_VFPRINTF(1, "4294967295", "%u", ~0u); br= eak; CASE_TEST(char); EXPECT_VFPRINTF(1, "c", "%c", 'c'); break; + CASE_TEST(char); EXPECT_VFPRINTF(1, "|c|d| e|", "|%c|%.0c|%4c|= ", 'c', 'd', 'e'); break; CASE_TEST(hex_nolibc); EXPECT_VFPRINTF(is_nolibc, "|f|d|", "|%x|%X|", = 0xf, 0xd); break; CASE_TEST(hex_libc); EXPECT_VFPRINTF(!is_nolibc, "|f|D|", "|%x|%X|",= 0xf, 0xd); break; CASE_TEST(pointer); EXPECT_VFPRINTF(1, "0x1", "%p", (void *) 0x1); = break; - CASE_TEST(pointer_NULL); EXPECT_VFPRINTF(1, "(nil)", "%p", (void *)0); b= reak; + CASE_TEST(pointer_NULL); EXPECT_VFPRINTF(1, "|(nil)|(nil)|", "|%p|%.4p|"= , (void *)0, (void *)0); break; + CASE_TEST(string_NULL); EXPECT_VFPRINTF(1, "|(null)||(null)|", "|%s|%.5= s|%.6s|", (void *)0, (void *)0, (void *)0); break; CASE_TEST(percent); EXPECT_VFPRINTF(1, "a%d42%69%", "a%%d%d%%%d%%",= 42, 69); break; CASE_TEST(perc_qual); EXPECT_VFPRINTF(1, "a%d2", "a%-14l%d%d", 2); br= eak; CASE_TEST(invalid); EXPECT_VFPRINTF(1, "a%12yx3%y42%P", "a%12yx%d%y= %d%P", 3, 42); break; @@ -1850,11 +1852,25 @@ static int run_printf(int min, int max) CASE_TEST(uintmax_max); EXPECT_VFPRINTF(1, "18446744073709551615", "%ju= ", ~0ULL); break; CASE_TEST(truncation); EXPECT_VFPRINTF(1, "012345678901234567890123456= 789", "%s", "012345678901234567890123456789"); break; CASE_TEST(string_width); EXPECT_VFPRINTF(1, " 1", "%10s", "1"); = break; + CASE_TEST(string_trunc); EXPECT_VFPRINTF(1, " 12345", "%10.5s", "123= 4567890"); break; CASE_TEST(number_width); EXPECT_VFPRINTF(1, " 1", "%10d", 1); br= eak; CASE_TEST(number_left); EXPECT_VFPRINTF(1, "|-5 |", "|%-8d|", -5);= break; CASE_TEST(string_align); EXPECT_VFPRINTF(1, "|foo |", "|%-8s|", "foo= "); break; CASE_TEST(width_trunc); EXPECT_VFPRINTF(1, " = 1", "%30d", 1); break; + CASE_TEST(width_tr_lft); EXPECT_VFPRINTF(1, "1 = ", "%-30d", 1); break; + CASE_TEST(number_pad); EXPECT_VFPRINTF(1, "0000000005", "%010d", 5); b= reak; + CASE_TEST(number_pad); EXPECT_VFPRINTF(1, "|0000000005|0x1234|", "|%01= 0d|%#01x|", 5, 0x1234); break; + CASE_TEST(num_pad_neg); EXPECT_VFPRINTF(1, "-000000005", "%010d", -5); = break; + CASE_TEST(num_pad_hex); EXPECT_VFPRINTF(1, "00fffffffb", "%010x", -5); = break; + CASE_TEST(num_pad_trunc);EXPECT_VFPRINTF(is_nolibc, " 000000000000000= 0000000000000005", "%035d", 5); break; /* max 31 '0' can be added */ + CASE_TEST(num_p_tr_libc);EXPECT_VFPRINTF(!is_nolibc, "000000000000000000= 00000000000000005", "%035d", 5); break; + CASE_TEST(number_prec); EXPECT_VFPRINTF(1, " 00005", "%10.5d", 5); = break; + CASE_TEST(num_prec_neg); EXPECT_VFPRINTF(1, " -00005", "%10.5d", -5);= break; + CASE_TEST(num_prec_var); EXPECT_VFPRINTF(1, " -00005", "%*.*d", 10, 5= , -5); break; + CASE_TEST(num_0_prec_0); EXPECT_VFPRINTF(1, "|| |+||||", "|%.0d|% .0d|%+= .0d|%.0u|%.0x|%#.0x|", 0, 0, 0, 0, 0, 0); break; CASE_TEST(hex_alt); EXPECT_VFPRINTF(1, "|0x1| 0x2| 0|", "|%#x|%= #5x|%#5x|", 1, 2, 0); break; + CASE_TEST(hex_alt_prec); EXPECT_VFPRINTF(1, "| 0x02|0x03| 0x123|", "|%#5= .2x|%#04x|%#6.2x|", 2, 3, 0x123); break; + CASE_TEST(hex_0_alt); EXPECT_VFPRINTF(1, "|0|0000| 00|", "|%#x|%#04= x|%#5.2x|", 0, 0, 0); break; CASE_TEST(errno); EXPECT_VFPRINTF(is_nolibc, "22:errno=3D22 ",= "%d:%-12m", errno=3D22); break; CASE_TEST(errno-neg); EXPECT_VFPRINTF(is_nolibc, "-22: errno=3D-22"= , "%d:%12m", errno=3D-22); break; CASE_TEST(scanf); EXPECT_ZR(1, test_scanf()); break; --=20 2.39.5 From nobody Fri Apr 17 06:13:05 2026 Received: from mailtransmit05.runbox.com (mailtransmit05.runbox.com [185.226.149.38]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 021021607A4 for ; Mon, 23 Feb 2026 11:15:30 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.226.149.38 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771845332; cv=none; b=D1WzNDgNFjelb5AE904ItD0xTGpKbFqklmAEjrqIKB3e7Jcz8X5ydThVU0Vq83BsuXUVLu300PTbKs/rbldQ1iKbOWok5Dy42ZFJWmcKn5raITbw0u+5+OksRYAR738aUrUzb6/y/cbIc90kcARa6fGrdxLYtmMpl2M7m4WLW9g= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771845332; c=relaxed/simple; bh=QkmDvNK7zuUS0Ngq+JC24ZM4/OVeg48+MUQmJ17wLss=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=jGwPNw6RuHYPQRd/RTqk7PvRbg4T9jQccl2RgTQxvcAtoXErHyPTDH+Xp0X4YHqDjex+pue5dt1qRbyNnpdJwCV1j7k0e/OB3PuaDTuMeOVjKiC+KLtmTLW8ySJgQOm9QyB2VYfJKOv54ILJKOM7zwMaaU/gsyAGmldqZLEA/EE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=fail (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=runbox.com; dkim=pass (2048-bit key) header.d=runbox.com header.i=@runbox.com header.b=YwkfeaGc; arc=none smtp.client-ip=185.226.149.38 Authentication-Results: smtp.subspace.kernel.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=runbox.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=runbox.com header.i=@runbox.com header.b="YwkfeaGc" Received: from mailtransmit03.runbox ([10.9.9.163] helo=aibo.runbox.com) by mailtransmit05.runbox.com with esmtps (TLS1.2) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.93) (envelope-from ) id 1vuT18-00GoCW-BB; Mon, 23 Feb 2026 11:18:18 +0100 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=runbox.com; s=selector2; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To :Message-Id:Date:Subject:Cc:To:From; bh=351uFpNMb6F6EQ8Mfzu1N3JEHx/Kky76IuxraG6rJjc=; b=YwkfeaGc9nfz5W8Fs1mmyuA1EP bYmu2lhZY7nYuAt/1B3Tkoc5exwISI/O3BiUc+q8y4XKfMlYy9wxPpk58DJ3DVRKB29IynmHKqjsg K7yncWhJ/Sfbbh9SwS9VZH6o1XNQokd0x+IQ/5Ba7Q7fSvIR/AT+r9yhe3Z3RlcdVC3Z+mLuf0fT+ 7aZ1GMB82G8h4FV0x6VCqdqQnanVNHt/KgD+tCeQMkac07kte42M7HX+PhqDjX69WbOWepOmcb9os HISXvzWQMtb2+iy90Z4QiM+0NOCkon0obB11uIvSIVIAcY3Vww75Y389SxTsHEHJh+5od8jYV2WKm 5eqAoCFw==; Received: from [10.9.9.73] (helo=submission02.runbox) by mailtransmit03.runbox with esmtp (Exim 4.86_2) (envelope-from ) id 1vuT17-0001Qq-Qs; Mon, 23 Feb 2026 11:18:17 +0100 Received: by submission02.runbox with esmtpsa [Authenticated ID (1493616)] (TLS1.2:ECDHE_SECP256R1__RSA_PSS_RSAE_SHA256__AES_256_GCM:256) (Exim 4.93) id 1vuT16-006AjD-MR; Mon, 23 Feb 2026 11:18:16 +0100 From: david.laight.linux@gmail.com To: Willy Tarreau , =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= , linux-kernel@vger.kernel.org, Cheng Li Cc: David Laight Subject: [PATCH v3 next 16/17] tools/nolibc/printf: Add support for octal output Date: Mon, 23 Feb 2026 10:17:34 +0000 Message-Id: <20260223101735.2922-17-david.laight.linux@gmail.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20260223101735.2922-1-david.laight.linux@gmail.com> References: <20260223101735.2922-1-david.laight.linux@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: David Laight Octal output isn't often used, but adding it costs very little. Supporting "%#o" is mildly annoying, it has to add a leading '0' if there isn't one present. In simple cases this is the same as adding a sign of '0' - but that adds an extra '0' in a few places. So you need 3 tests, %o, # and no leading '0' (which can only be checked after the zero pad for precision). If all the test are deferred until after zero padding then too many values are 'live' across the call to _nolibc_u64toa_base() and get spilled to stac= k. Hence the check that ignores the 'sign' if it is the same as the first character of the output string. Add tests for octal output. Signed-off-by: David Laight --- New patch for v3. Relies on the changes to u64toa_r() etc in stdlib.h (patch 2).=20 tools/include/nolibc/stdio.h | 50 ++++++++++++++------ tools/testing/selftests/nolibc/nolibc-test.c | 3 ++ 2 files changed, 38 insertions(+), 15 deletions(-) diff --git a/tools/include/nolibc/stdio.h b/tools/include/nolibc/stdio.h index cc2870a6a7cb..e6aca894bf71 100644 --- a/tools/include/nolibc/stdio.h +++ b/tools/include/nolibc/stdio.h @@ -292,7 +292,7 @@ int fseek(FILE *stream, long offset, int whence) =20 =20 /* printf(). Supports most of the normal integer and string formats. - * - %[#0-+ ][width|*[.precision|*}][{l,t,z,ll,L,j,q}]{c,d,i,u,x,X,p,s,m,= %} + * - %[#0-+ ][width|*[.precision|*}][{l,t,z,ll,L,j,q}]{c,d,i,u,o,x,X,p,s,= m,%} * - %% generates a single % * - %m outputs strerror(errno). * - %X outputs a..f the same as %x. @@ -426,7 +426,7 @@ int __nolibc_printf(__nolibc_printf_cb cb, void *state,= const char *fmt, va_list */ ch_flag =3D _NOLIBC_PF_FLAG(ch) | (flags & _NOLIBC_PF_FLAG('#')) >> 1; if (((ch >=3D 'a' && ch <=3D 'z') || ch =3D=3D 'X') && - _NOLIBC_PF_FLAGS_CONTAIN(ch_flag, 'c', 'd', 'i', 'u', 'x', 'p', 's')= ) { + _NOLIBC_PF_FLAGS_CONTAIN(ch_flag, 'c', 'd', 'i', 'u', 'o', 'x', 'p',= 's')) { /* 'long' is needed for pointer/string conversions and ltz lengths. * A single test can be used provided 'p' (the same bit as '0') * is masked from flags. @@ -477,12 +477,19 @@ int __nolibc_printf(__nolibc_printf_cb cb, void *stat= e, const char *fmt, va_list } else if (_NOLIBC_PF_FLAGS_CONTAIN(flags, ' ')) { sign_prefix =3D ' '; } + } else { + /* "#o" requires that the output always starts with a '0'. + * This needs another check after any zero padding to avoid + * adding an extra leading '0'. + */ + if (_NOLIBC_PF_FLAGS_CONTAIN(ch_flag, 'o') && + _NOLIBC_PF_FLAGS_CONTAIN(ch_flag, '#' - 1)) + sign_prefix =3D '0'; } =20 /* The value is converted offset into the buffer so that * 31 zero pad characters and the sign/prefix can be added in front. - * The longest digit string is 22 + 1 for octal conversions, the - * space is reserved even though octal isn't currently supported. + * The longest digit string is 22 + 1 for octal conversions. */ out =3D outbuf + 2 + 31; =20 @@ -495,7 +502,7 @@ int __nolibc_printf(__nolibc_printf_cb cb, void *state,= const char *fmt, va_list goto do_output; } if (!precision) { - /* Explicit %nn.0d, no digits output */ + /* Explicit %nn.0d, no digits output (except for %#.0o) */ len =3D 0; goto prepend_sign; } @@ -504,17 +511,23 @@ int __nolibc_printf(__nolibc_printf_cb cb, void *stat= e, const char *fmt, va_list len =3D 1; } else { /* Convert the number to ascii in the required base. */ + unsigned long long recip; + unsigned int base; if (_NOLIBC_PF_FLAGS_CONTAIN(ch_flag, 'd', 'i', 'u')) { - /* Base 10 */ - len =3D u64toa_r(v, out); + base =3D 10; + recip =3D _NOLIBC_U64TOA_RECIP(10); + } else if (_NOLIBC_PF_FLAGS_CONTAIN(ch_flag, 'o')) { + base =3D 8; + recip =3D _NOLIBC_U64TOA_RECIP(8); } else { - /* Base 16 */ + base =3D 16; + recip =3D _NOLIBC_U64TOA_RECIP(16); if (_NOLIBC_PF_FLAGS_CONTAIN(ch_flag, 'p', '#' - 1)) { /* "%p" and "%#x" need "0x" prepending. */ sign_prefix =3D 'x' | '0' << 8; } - len =3D u64toh_r(v, out); } + len =3D _nolibc_u64toa_base(v, out, base, recip); } =20 /* Add zero padding */ @@ -545,13 +558,20 @@ int __nolibc_printf(__nolibc_printf_cb cb, void *stat= e, const char *fmt, va_list } } =20 + /* %#o has set sign_prefix to '0', but we don't want so add an extra + * leading zero here. + * Since the only other byte values of sign_prefix are ' ', '+' and '-' + * it is enough to check that out[] doesn't already start with sign_pre= fix. + */ + if (sign_prefix !=3D *out) { prepend_sign: - /* Add the 0, 1 or 2 ("0x") sign/prefix characters at the front. */ - for (; sign_prefix; sign_prefix >>=3D 8) { - /* Force gcc to increment len inside the loop. */ - _NOLIBC_OPTIMIZER_HIDE_VAR(len); - len++; - *--out =3D sign_prefix; + /* Add the 0, 1 or 2 ("0x") sign/prefix characters at the front. */ + for (; sign_prefix; sign_prefix >>=3D 8) { + /* Force gcc to increment len inside the loop. */ + _NOLIBC_OPTIMIZER_HIDE_VAR(len); + len++; + *--out =3D sign_prefix; + } } outstr =3D out; goto do_output; diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/s= elftests/nolibc/nolibc-test.c index e0cfe8d12ed1..b1f27fb1dd74 100644 --- a/tools/testing/selftests/nolibc/nolibc-test.c +++ b/tools/testing/selftests/nolibc/nolibc-test.c @@ -1839,6 +1839,9 @@ static int run_printf(int min, int max) CASE_TEST(unsigned_max); EXPECT_VFPRINTF(1, "4294967295", "%u", ~0u); br= eak; CASE_TEST(char); EXPECT_VFPRINTF(1, "c", "%c", 'c'); break; CASE_TEST(char); EXPECT_VFPRINTF(1, "|c|d| e|", "|%c|%.0c|%4c|= ", 'c', 'd', 'e'); break; + CASE_TEST(octal); EXPECT_VFPRINTF(1, "|17| 0033||", "|%o|%6.4o|%= .0o|", 017, 033, 0); break; + CASE_TEST(octal_max); EXPECT_VFPRINTF(1, "1777777777777777777777", "%= llo", ~0ULL); break; + CASE_TEST(octal_alt); EXPECT_VFPRINTF(1, "|0|01|02|034|0|", "|%#o|%#o= |%#02o|%#02o|%#.0o|", 0, 1, 2, 034, 0); break; CASE_TEST(hex_nolibc); EXPECT_VFPRINTF(is_nolibc, "|f|d|", "|%x|%X|", = 0xf, 0xd); break; CASE_TEST(hex_libc); EXPECT_VFPRINTF(!is_nolibc, "|f|D|", "|%x|%X|",= 0xf, 0xd); break; CASE_TEST(pointer); EXPECT_VFPRINTF(1, "0x1", "%p", (void *) 0x1); = break; --=20 2.39.5 From nobody Fri Apr 17 06:13:05 2026 Received: from mailtransmit05.runbox.com (mailtransmit05.runbox.com [185.226.149.38]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 19B36481DD for ; Mon, 23 Feb 2026 10:18:28 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.226.149.38 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771841912; cv=none; b=KYdyuG6WBm7cDQGGdzas4pAQYTf6cIHHP47nmjI4yDEY4eL8iAyRroYXwMlnr17wqPj+BcDNRUZsb0bhIv/jIcUE4ed1g+F575JO2YqvmsJITgZ2QNDKmEp+RE1ROJMPYEYxzduNaUqqYVgsPK6wcjr+hWsYxWPqOkVlhsEBEx4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771841912; c=relaxed/simple; bh=i52GsPClS/raYu7wS07JcgwRCb2w1MP6wWosENa1BwQ=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=P8Bs2hr4j+2J/H9hnZDGlKqE+zUp6VqutP+uoyZoTYfotc3aOUVN0qCO1LvGBao/51vRAR+zm4ZkjL2VhcCtve/8Ia84CrsOYJu2dOZzd+gWlqrhFmvWcN7H5EApPmRowOXGfNpq7v87s23khrwanOiNYamdNeumKExh/eEI4NU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=fail (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=runbox.com; dkim=pass (2048-bit key) header.d=runbox.com header.i=@runbox.com header.b=ntxVDmnS; arc=none smtp.client-ip=185.226.149.38 Authentication-Results: smtp.subspace.kernel.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=runbox.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=runbox.com header.i=@runbox.com header.b="ntxVDmnS" Received: from mailtransmit02.runbox ([10.9.9.162] helo=aibo.runbox.com) by mailtransmit05.runbox.com with esmtps (TLS1.2) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.93) (envelope-from ) id 1vuT1G-00GoEL-KX; Mon, 23 Feb 2026 11:18:26 +0100 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=runbox.com; s=selector2; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To :Message-Id:Date:Subject:Cc:To:From; bh=H03YtWQaaNeDoJiqRc1AtoHEopKeSBB0Vi+p+4SHhkE=; b=ntxVDmnSN8FQ6tTlwMw+bZSXq2 pYqAV0gTp7skKcni2YPpb0fkLL86D4SOV6GpvCQnFpLZ9yLkMKCEI+3/wzS5AUhiodmW3yPqtk8lO fLrKu94cJ6SCSL5EKkQFDvcUzQ/3z+BFG65hUA+6m/OT1st3J+5wsAGdmMyDQtPXQUo051OGVuWTt yaL4KYKaV3Q+7mF0/MI1emlEbtNQKAaYUTsRVnk5ZcLrn5ntBu3vyAl6foNGkrL3EtZvHN/JAwtc8 pURi/oljGraDaNe/aMCyGKV1wdiDP/hPudtoEwU9pF2baHW7gxH+ggKBBCURqD5Bvyzv55MG+c1F9 aEIWVQ+g==; Received: from [10.9.9.73] (helo=submission02.runbox) by mailtransmit02.runbox with esmtp (Exim 4.86_2) (envelope-from ) id 1vuT1G-0005Qb-AZ; Mon, 23 Feb 2026 11:18:26 +0100 Received: by submission02.runbox with esmtpsa [Authenticated ID (1493616)] (TLS1.2:ECDHE_SECP256R1__RSA_PSS_RSAE_SHA256__AES_256_GCM:256) (Exim 4.93) id 1vuT17-006AjD-5Z; Mon, 23 Feb 2026 11:18:17 +0100 From: david.laight.linux@gmail.com To: Willy Tarreau , =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= , linux-kernel@vger.kernel.org, Cheng Li Cc: David Laight Subject: [PATCH v3 next 17/17] selftests/nolibc: Use printf variable field widths and precisions Date: Mon, 23 Feb 2026 10:17:35 +0000 Message-Id: <20260223101735.2922-18-david.laight.linux@gmail.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20260223101735.2922-1-david.laight.linux@gmail.com> References: <20260223101735.2922-1-david.laight.linux@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: David Laight Now that printf supports '*' for field widths and precisions then can be used to simplify the test output. - aligning the "[OK]" strings. - reporting the expected sprintf() output when there is a mismatch. Acked-by: Willy Tarreau Signed-off-by: David Laight --- Changes for v3: - Change code in expect_vfprintf() (code added in v3). tools/testing/selftests/nolibc/nolibc-test.c | 26 ++++---------------- 1 file changed, 5 insertions(+), 21 deletions(-) diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/s= elftests/nolibc/nolibc-test.c index b1f27fb1dd74..6c043e5e1220 100644 --- a/tools/testing/selftests/nolibc/nolibc-test.c +++ b/tools/testing/selftests/nolibc/nolibc-test.c @@ -156,21 +156,6 @@ static const char *errorname(int err) } } =20 -static void align_result(size_t llen) -{ - const size_t align =3D 64; - char buf[align]; - size_t n; - - if (llen >=3D align) - return; - - n =3D align - llen; - memset(buf, ' ', n); - buf[n] =3D '\0'; - fputs(buf, stdout); -} - enum RESULT { OK, FAIL, @@ -188,8 +173,10 @@ static void result(int llen, enum RESULT r) else msg =3D " [FAIL]"; =20 - align_result(llen); - puts(msg); + llen =3D 64 - llen; + if (llen < 0) + llen =3D 0; + printf("%*s%s\n", llen, "", msg); } =20 /* The tests below are intended to be used by the macroes, which evaluate @@ -1699,10 +1686,7 @@ static int expect_vfprintf(int llen, int cond, const= char *expected, const char } =20 if (memcmp(expected, buf, cmp_len) || buf[cmp_len]) { - /* Copy and truncate until "%.*s" supported */ - memcpy(buf, expected, cmp_len); - buf[cmp_len] =3D 0; - llen +=3D printf(" should be \"%s\"", buf); + llen +=3D printf(" should be \"%.*s\"", VFPRINTF_LEN, expected); result(llen, FAIL); return 1; } --=20 2.39.5