[uClibc]printf %c handling on big-endian platform

Ilguiz Latypov ilatypov at superbt.com
Mon Apr 1 22:48:35 UTC 2002


Hello,

Is there any chance that printf.c could process the %c format specifier 
incorrectly?  I traced that problem from telnetd daemon.

The attached test program printf_test.c gave expected results after
applying the patch.  The output from the earlier version of the program is
shown below.

I don't know if there are other endian conversion issues besides the one 
with %c specifier.

Ilguiz

Before the fix in libc/stdio/printf.c:
=============================================================================
# ./printf_test 
i %08x 61626364, (char) i %08x 00000064, bytes[0] 61, bytes[1] 62, bytes[2] 63,
bytes[3] 64
len 28, snprintf "     (char)(bytes[0]) %c '"
len 28, snprintf "      (int)(bytes[0]) %c '"
len 28, snprintf "(int)(bytes[0] << 24) %c 'a'"
# 

After the fix in libc/stdio/printf.c:
=============================================================================
# ./printf_test 
i %08x 61626364, (char) i %08x 00000064, bytes[0] 61, bytes[1] 62, bytes[2] 63,
bytes[3] 64
len 28, snprintf "     (char)(bytes[0]) %c 'a'"
len 28, snprintf "      (int)(bytes[0]) %c 'a'"
len 28, snprintf "(int)(bytes[0] << 24) %c '"
# 
=============================================================================

-------------- next part --------------
#include <sys/types.h>
#include <stdio.h>

typedef union int_bytes_s {
	int i;
	u_int8_t bytes[4];
} int_bytes_t __attribute__ ((packed));

int main(int argc, char **argv) {
	char s[81];
	int_bytes_t opt;
	int len;
	char ch;

	opt.bytes[0] = 'a';	/* 0x61 */
	opt.bytes[1] = 'b';	/* 0x62 */
	opt.bytes[2] = 'c';	/* 0x63 */
	opt.bytes[3] = 'd';	/* 0x64 */

	printf("i %%08x %08x, (char) i %%08x %08x, "
		"bytes[0] %02x, bytes[1] %02x, bytes[2] %02x, bytes[3] %02x\n",
		opt.i, (int)((char)opt.i),
		opt.bytes[0], opt.bytes[1], opt.bytes[2], opt.bytes[3]);

	memset(s, 0, sizeof(s));
	len = snprintf(s, sizeof(s), "     (char)(bytes[0]) %%c '%c'", 
		(char)(opt.bytes[0]));
	printf("len %d, snprintf \"%s\"\n", len, s);

	memset(s, 0, sizeof(s));
	len = snprintf(s, sizeof(s), "      (int)(bytes[0]) %%c '%c'", 
		(int)(opt.bytes[0]));
	printf("len %d, snprintf \"%s\"\n", len, s);

	memset(s, 0, sizeof(s));
	len = snprintf(s, sizeof(s), "(int)(bytes[0] << 24) %%c '%c'", 
		(int)(opt.bytes[0] << 24));
	printf("len %d, snprintf \"%s\"\n", len, s);


	ch = 'a';
	printf("        (signed char)ch         %%02x '%02x'\n", 
			(signed char)ch);
	printf("      (unsigned char)ch         %%02x '%02x'\n", 
			(unsigned char)ch);
	printf("(int)((unsigned char)ch)        %%02x '%02x'\n", 
			(int)((unsigned char)ch));
	printf("(int)((unsigned char)ch) & 0xff %%02x '%02x'\n", 
			(int)((unsigned char)ch) & 0xff);

	return 0;
}
-------------- next part --------------
Index: libc/stdio/printf.c
===================================================================
RCS file: /usr/local/cvsroot/uClibc/libc/stdio/printf.c,v
retrieving revision 1.1.1.3
retrieving revision 1.2
diff -u -r1.1.1.3 -r1.2
--- libc/stdio/printf.c	1 Apr 2002 17:16:43 -0000	1.1.1.3
+++ libc/stdio/printf.c	1 Apr 2002 22:14:52 -0000	1.2
@@ -1172,7 +1172,14 @@
 				}
 			} else {			/* char */
 				s = (char *) buf;
-				*s = *((const char *) *argptr);
+				/*
+				 * For the little-endian platform, the compiler
+				 * will take the byte off *argptr.
+				 * On big-endian platform, the compiler will
+				 * take the byte at (*argptr + sizeof(int) - 1).
+				 * --ilatypov
+				 */
+				*s = (char)(*((const int *) *argptr));
 				s[1] = 0;
 				slen = 1;
 			}



More information about the uClibc mailing list