131-vararg-mixed.c (3970B)
1 /* Variadic recv across mixed types and printf-style forwarding. 2 * 3 * Existing vararg coverage (015/067/076/079/097/116) only pulls `int` 4 * via va_arg from the same function that called va_start. 5 * Real callers (printf-shaped) interleave pointers and 64-bit ints, 6 * AND forward `va_list` across function boundaries 7 * (printf -> vprintf -> vfprintf). cg-va-arg always loads 8 bytes 8 * and lets the caller cast, and doesn't require the enclosing 9 * function to be variadic — but that path was untested. 10 * 11 * This test pins down: 12 * - va_arg(ap, long) — full 64-bit value survives (past INT_MAX) 13 * - va_arg(ap, char *) — pointer round-trips intact 14 * - alternating types in one call 15 * - va_list passed as a parameter to a non-variadic helper which 16 * then calls va_arg (the vfprintf shape) 17 * - two-level forwarding: variadic wrapper -> v-helper -> reader 18 * (the printf -> vprintf -> vfprintf shape) 19 */ 20 21 #ifndef CCSCM 22 #include <stdarg.h> 23 #else 24 typedef char *va_list; 25 #define va_start(ap, n) __builtin_va_start(ap, n) 26 #define va_arg(ap, t) __builtin_va_arg(ap, t) 27 #define va_end(ap) __builtin_va_end(ap) 28 #endif 29 30 static int streq(char *a, char *b) { 31 while (*a && *b) { 32 if (*a != *b) return 0; 33 a = a + 1; 34 b = b + 1; 35 } 36 return *a == *b; 37 } 38 39 /* Each "record" is (long, char *, int). */ 40 static long check(int n, ...) { 41 va_list ap; 42 long sum = 0; 43 int i = 0; 44 long lv; 45 char *sv; 46 int iv; 47 va_start(ap, n); 48 while (i < n) { 49 lv = va_arg(ap, long); 50 sv = va_arg(ap, char *); 51 iv = va_arg(ap, int); 52 if (!streq(sv, "ok")) return -1; 53 sum = sum + lv + iv; 54 i = i + 1; 55 } 56 va_end(ap); 57 return sum; 58 } 59 60 /* Non-variadic reader. Receives an already-initialized va_list and a 61 * format-shaped char* + count. Mirrors vfprintf: ap was set up in a 62 * caller's frame, this function only consumes it. */ 63 static long vsum(char *tag, int n, va_list ap) { 64 long total = 0; 65 int i = 0; 66 if (!streq(tag, "go")) return -1; 67 while (i < n) { 68 total = total + va_arg(ap, int); 69 i = i + 1; 70 } 71 return total; 72 } 73 74 /* Mid-layer forwarder. Non-variadic; takes ap by value and hands it 75 * down. Mirrors vprintf -> vfprintf. */ 76 static long vsum_fwd(char *tag, int n, va_list ap) { 77 return vsum(tag, n, ap); 78 } 79 80 /* Top-level variadic wrapper: va_start then forward through two 81 * non-variadic frames before any va_arg runs. Mirrors printf. */ 82 static long psum(char *tag, int n, ...) { 83 va_list ap; 84 long r; 85 va_start(ap, n); 86 r = vsum_fwd(tag, n, ap); 87 va_end(ap); 88 return r; 89 } 90 91 int main(int argc, char **argv) { 92 /* --- Mixed-type direct va_arg (long / char* / int) ----------- */ 93 /* Two records. Longs chosen so the sum exceeds INT_MAX 94 * (2^31 - 1 = 2147483647); a 32-bit truncation in va_arg would 95 * corrupt the total. */ 96 long r = check(2, 97 1000000000L, "ok", 5, 98 2000000000L, "ok", 7); 99 if (r == -1) return 1; /* pointer arg lost or corrupted */ 100 if (r != 3000000012L) return 2; /* long arg truncated */ 101 102 /* Single record, distinct pointer to confirm we don't latch the 103 * literal across calls. */ 104 long r2 = check(1, 4000000000L, "ok", 1); 105 if (r2 == -1) return 3; 106 if (r2 != 4000000001L) return 4; 107 108 /* --- printf-style two-level va_list forwarding --------------- */ 109 /* psum -> vsum_fwd -> vsum. va_arg only runs in vsum, on an ap 110 * that was initialized two frames up. The single-int case 111 * matches printf("got %d\n", 42) at the ABI level. */ 112 long s1 = psum("go", 1, 42); 113 if (s1 != 42) return 5; 114 115 /* Multi-arg forwarding to confirm ap advances correctly across 116 * frames (each va_arg increments the local copy; the originator's 117 * frame is irrelevant once values are read). */ 118 long s2 = psum("go", 4, 10, 20, 30, 40); 119 if (s2 != 100) return 6; 120 121 return 0; 122 }