fix(dlog): fix bug in handling of `%*`
The `dlog` function was not handling the `%*` format specifier
correctly.
The bug was caused by passing `va_list` by value instead of by
reference: In a call like `dlog("%*s", 10, "hello")`, the `va_list`
argument would be copied when passed to `parse_min_width`, and `10`
would be popped from the `va_list` to be used as the min_width. But then
`10` would be popped again and used as the `%s` argument, rather than
"hello".
Change-Id: I5c53465384196ee21d2a63167cf82e4f8ead9c3a
Signed-off-by: Karl Meakin <karl.meakin@arm.com>
diff --git a/inc/hf/dlog.h b/inc/hf/dlog.h
index 33f5a1e..992bad0 100644
--- a/inc/hf/dlog.h
+++ b/inc/hf/dlog.h
@@ -27,9 +27,40 @@
extern size_t dlog_buffer_offset;
extern char dlog_buffer[];
+/**
+ * This struct is a workaround to allow passing `va_list` by pointer on x86_64.
+ * On x86_64, `va_list` is an array, and in C arrays are weird.
+ *
+ * In particular, function parameters of array types are really pointers: the
+ * functions `void f(char[1])` and `void f(char*)` are identical (see
+ * https://en.cppreference.com/w/c/language/array). But this does not apply to
+ * function parameters of type pointer to array: the functions `void
+ * f(char(*)[1])` and `void f(char**)` are not identical.
+ *
+ * Therefore in the body of `by_value`, `args` has type `__va_list_tag *`.
+ * The call to `by_pointer` will cause a compile error, as `&args` has type
+ * `va_list_tag **` but `by_pointer` expects `va_list (*)[1]`.
+ *
+ * ```
+ * typedef va_list __va_list_tag[1];
+ *
+ * void by_pointer(va_list *args) {}
+ *
+ * void by_value(va_list args) {
+ * by_pointer(&args);
+ * }
+ * ```
+ *
+ * The workaround to prevent array to pointer decay is to wrap the array in a
+ * struct, since structs do not decay to pointers.
+ */
+struct va_list_wrapper {
+ va_list va;
+};
+
void dlog_enable_lock(void);
__attribute__((format(printf, 1, 2))) size_t dlog(const char *fmt, ...);
-size_t vdlog(const char *fmt, va_list args);
+size_t vdlog(const char *fmt, struct va_list_wrapper *args);
/*
* The do { ... } while (0) syntax is used to ensure that callers of