boot2

Playing with the boostrap
git clone https://git.ryansepassi.com/git/boot2.git
Log | Files | Refs | README

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 }