From d371cf3336df96407e974f34e39451790a97809a Mon Sep 17 00:00:00 2001 From: Julius Werner Date: Fri, 22 May 2015 18:09:48 -0700 Subject: Make common macros double-evaluation safe I just got hit by a double-evaluation bug again, it's time to attempt to fix this once more. Unfortunately there are several issues that don't make this easy: - bitfield variables don't support typeof() - local macro variables that shadow others trigger -Werror=shadow - sign warnings with integer literal and unsigned var in typeof-MIN() - ({ statement expressions }) can not be used outside functions - romcc doesn't support any of the fancy GCC/clang extensions This patch tries to address all of them as far as possible with macro magic. We don't have the technology to solve the bitfield and non-function context issues yet (__builtin_choose_expr() still throws a "no statement expression outside a function" error if it's only in the branch that's not chosen, unfortunately), so we'll have to provide alternative macros for use in those cases (and we'll avoid making __ALIGN_MASK() double-evaluation safe for now, since it would be annoying to do that there and having an alignment mask with side effects seems very unlikely). romcc can continue using unsafe versions since we're hopefully not writing a lot of new code for it. Sign warnings can be avoided in literal/variable comparisons by always using the type of the variable there. Shadowing is avoided by picking very explicit local variable names and using a special __COUNTER__ solution for MIN() and MAX() (the only ones of these you're likely to nest). Also add DIV_ROUND_UP() to libpayload since it's a generally quite useful thing to have. Change-Id: Iea35156c9aa9f6f2c7b8f00991418b746f44315d Signed-off-by: Julius Werner Reviewed-on: https://review.coreboot.org/c/coreboot/+/32027 Tested-by: build bot (Jenkins) Reviewed-by: Paul Menzel Reviewed-by: Patrick Georgi --- payloads/libpayload/include/compiler.h | 15 +++++++++++++++ payloads/libpayload/include/libpayload.h | 26 ++++++++++++++++++++++++-- 2 files changed, 39 insertions(+), 2 deletions(-) (limited to 'payloads') diff --git a/payloads/libpayload/include/compiler.h b/payloads/libpayload/include/compiler.h index a830239009..fa9c78bd36 100644 --- a/payloads/libpayload/include/compiler.h +++ b/payloads/libpayload/include/compiler.h @@ -26,4 +26,19 @@ #define __always_unused __attribute__((unused)) #define __must_check __attribute__((warn_unused_result)) +/* This evaluates to the type of the first expression, unless that is constant + in which case it evalutates to the type of the second. This is useful when + assigning macro parameters to temporary variables, because that would + normally circumvent the special loosened type promotion rules for integer + literals. By using this macro, the promotion can happen at the time the + literal is assigned to the temporary variable. If the literal doesn't fit in + the chosen type, -Werror=overflow will catch it, so this should be safe. */ +#define __TYPEOF_UNLESS_CONST(expr, fallback_expr) typeof( \ + __builtin_choose_expr(__builtin_constant_p(expr), fallback_expr, expr)) + +/* This creates a unique local variable name for use in macros. */ +#define __TMPNAME_3(i) __tmpname_##i +#define __TMPNAME_2(i) __TMPNAME_3(i) +#define __TMPNAME __TMPNAME_2(__COUNTER__) + #endif diff --git a/payloads/libpayload/include/libpayload.h b/payloads/libpayload/include/libpayload.h index 0b9ab0dcd5..3a84b3b038 100644 --- a/payloads/libpayload/include/libpayload.h +++ b/payloads/libpayload/include/libpayload.h @@ -66,10 +66,32 @@ #include #include -#define MIN(a,b) ((a) < (b) ? (a) : (b)) -#define MAX(a,b) ((a) > (b) ? (a) : (b)) +/* Double-evaluation unsafe min/max, for bitfields and outside of functions */ +#define __CMP_UNSAFE(a, b, op) ((a) op (b) ? (a) : (b)) +#define MIN_UNSAFE(a, b) __CMP_UNSAFE(a, b, <) +#define MAX_UNSAFE(a, b) __CMP_UNSAFE(a, b, >) + +#define __CMP_SAFE(a, b, op, var_a, var_b) ({ \ + __TYPEOF_UNLESS_CONST(a, b) var_a = (a); \ + __TYPEOF_UNLESS_CONST(b, a) var_b = (b); \ + var_a op var_b ? var_a : var_b; \ +}) + +#define __CMP(a, b, op) __builtin_choose_expr( \ + __builtin_constant_p(a) && __builtin_constant_p(b), \ + __CMP_UNSAFE(a, b, op), __CMP_SAFE(a, b, op, __TMPNAME, __TMPNAME)) + +#define MIN(a, b) __CMP(a, b, <) +#define MAX(a, b) __CMP(a, b, >) + #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) +#define DIV_ROUND_UP(x, y) ({ \ + typeof(x) _div_local_x = (x); \ + typeof(y) _div_local_y = (y); \ + (_div_local_x + _div_local_y - 1) / _div_local_y; \ +}) + static inline u32 div_round_up(u32 n, u32 d) { return (n + d - 1) / d; } #define LITTLE_ENDIAN 1234 -- cgit v1.2.3