018-printf-int-promo.c (1375B)
1 /* printf %d / %c / %u with plain `int` args. amd64 SysV stores int 2 * varargs as a 32-bit write into an 8-byte reg-save slot — upper bits 3 * are unspecified (tcc does not sign-extend ints into the slot). 4 * mes-libc's vfprintf used to read every integer spec via 5 * `va_arg(ap, long)`, so the upper bits leaked: -42 printed as 6 * 4294967254 (= 0x00000000_FFFFFFD6). The fix tracks the `l` length 7 * modifier and reads with the right width. Other arches passed by 8 * accident (their ABIs / va_list lowerings extend int slots), but 9 * the fix is correct everywhere. */ 10 extern int atoi (char const *s); 11 extern int printf (char const *fmt, ...); 12 13 int 14 main (void) 15 { 16 printf ("%d\n", atoi ("-42")); /* -42 */ 17 printf ("%d\n", -2147483647 - 1); /* INT_MIN: -2147483648 */ 18 printf ("%d\n", 2147483647); /* INT_MAX: 2147483647 */ 19 printf ("%u\n", (unsigned int)-1); /* 4294967295 */ 20 printf ("%x\n", (unsigned int)-1); /* ffffffff */ 21 printf ("%c\n", 'Z'); /* Z */ 22 23 /* %ld must still read a full long. Pass an intentionally wide 24 value so a buggy "always int" fix would fail this case. */ 25 long big = 4294967296L + 7L; /* 0x100000007 */ 26 printf ("%ld\n", big); /* 4294967303 */ 27 return 0; 28 }