Commit 658f2dc9 authored by Richard Henderson's avatar Richard Henderson Committed by Blue Swirl
Browse files

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: default avatarRichard Henderson <rth@twiddle.net>
Signed-off-by: default avatarBlue Swirl <blauwirbel@gmail.com>
parent c732a52d
Loading
Loading
Loading
Loading
+33 −30
Original line number Diff line number Diff line
@@ -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;\
})

#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;\
})
/* 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.  */

/* 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,