linux-user: Rewrite __get_user/__put_user with __builtin_choose_expr

The previous formuation with multiple assignments to __typeof(*hptr) falls
down when hptr is qualified const.  E.g. with const struct S *p, p->f is
also qualified const.

With this formulation, there's no assignment to any local variable.

Signed-off-by: Richard Henderson <rth@twiddle.net>
Signed-off-by: Blue Swirl <blauwirbel@gmail.com>
This commit is contained in:
Richard Henderson 2013-01-04 16:39:31 -08:00 committed by Blue Swirl
parent c732a52d3e
commit 658f2dc970

View file

@ -287,36 +287,39 @@ static inline int access_ok(int type, abi_ulong addr, abi_ulong size)
(type == VERIFY_READ) ? PAGE_READ : (PAGE_READ | PAGE_WRITE)) == 0;
}
/* NOTE __get_user and __put_user use host pointers and don't check access. */
/* These are usually used to access struct data members once the
* struct has been locked - usually with lock_user_struct().
*/
#define __put_user(x, hptr)\
({ __typeof(*hptr) pu_ = (x);\
switch(sizeof(*hptr)) {\
case 1: break;\
case 2: pu_ = tswap16(pu_); break; \
case 4: pu_ = tswap32(pu_); break; \
case 8: pu_ = tswap64(pu_); break; \
default: abort();\
}\
memcpy(hptr, &pu_, sizeof(pu_)); \
0;\
})
/* NOTE __get_user and __put_user use host pointers and don't check access.
These are usually used to access struct data members once the struct has
been locked - usually with lock_user_struct. */
#define __get_user(x, hptr) \
({ __typeof(*hptr) gu_; \
memcpy(&gu_, hptr, sizeof(gu_)); \
switch(sizeof(*hptr)) {\
case 1: break; \
case 2: gu_ = tswap16(gu_); break; \
case 4: gu_ = tswap32(gu_); break; \
case 8: gu_ = tswap64(gu_); break; \
default: abort();\
}\
(x) = gu_; \
0;\
})
/* Tricky points:
- Use __builtin_choose_expr to avoid type promotion from ?:,
- Invalid sizes result in a compile time error stemming from
the fact that abort has no parameters.
- It's easier to use the endian-specific unaligned load/store
functions than host-endian unaligned load/store plus tswapN. */
#define __put_user_e(x, hptr, e) \
(__builtin_choose_expr(sizeof(*(hptr)) == 1, stb_p, \
__builtin_choose_expr(sizeof(*(hptr)) == 2, stw_##e##_p, \
__builtin_choose_expr(sizeof(*(hptr)) == 4, stl_##e##_p, \
__builtin_choose_expr(sizeof(*(hptr)) == 8, stq_##e##_p, abort)))) \
((hptr), (x)), 0)
#define __get_user_e(x, hptr, e) \
((x) = \
__builtin_choose_expr(sizeof(*(hptr)) == 1, ldub_p, \
__builtin_choose_expr(sizeof(*(hptr)) == 2, lduw_##e##_p, \
__builtin_choose_expr(sizeof(*(hptr)) == 4, ldl_##e##_p, \
__builtin_choose_expr(sizeof(*(hptr)) == 8, ldq_##e##_p, abort)))) \
(hptr), 0)
#ifdef TARGET_WORDS_BIGENDIAN
# define __put_user(x, hptr) __put_user_e(x, hptr, be)
# define __get_user(x, hptr) __get_user_e(x, hptr, be)
#else
# define __put_user(x, hptr) __put_user_e(x, hptr, le)
# define __get_user(x, hptr) __get_user_e(x, hptr, le)
#endif
/* put_user()/get_user() take a guest address and check access */
/* These are usually used to access an atomic data type, such as an int,