linux-user: fix preadv/pwritev offsets
PATCH v4 1/1 linux-user: fix preadv/pwritev offsets
preadv/pwritev accept low and high parts of file offset in two separate parameters. When host bitness doesn't match guest bitness these parts must be appropriately recombined.
Introduce target_to_host_low_high that does this recombination and use it in preadv/pwritev syscalls.
This fixes glibc testsuite test misc/tst-preadvwritev64.
---
Changes v3->v4:
- recombine offset into unsigned long long and split it into two longs.
Changes v2->v3:
- rename helper to target_to_host_low_high;
- handle cases TARGET_LONG_BITS * 2 > HOST_LONG_BITS and TARGET_LONG_BITS < 2 * HOST_LONG_BITS correctly.
Changes v1->v2:
- fix host high computation in TARGET_LONG_BITS > HOST_LONG_BITS case
linux-user/syscall.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-)
linux-user/syscall.c +22 -2
@@ -3386,6 +3386,20 @@ static abi_long do_getsockopt(int sockfd, int level, int optname,return ret; } +static void target_to_host_low_high (abi_ulong tlow, +abi_ulong thigh, +unsigned long *hlow, +unsigned long *hhigh) +{ +unsigned long long off = tlow | + ((unsigned long long )thigh <<TARGET_LONG_BITS /2 ) << +TARGET_LONG_BITS /2 ; + + *hlow = (unsigned long )off; + *hhigh = (unsigned long )((off >>HOST_LONG_BITS /2 ) >> +HOST_LONG_BITS /2 ); +} +static struct iovec *lock_iovec (int type,abi_ulong target_addr,abi_ulong count,int copy) {
@@ -10449,7 +10463,10 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, {struct iovec *vec =lock_iovec (VERIFY_WRITE , arg2, arg3,0 );if (vec !=NULL ) { - ret =get_errno (safe_preadv (arg1, vec, arg3, arg4, arg5)); +unsigned long low, high; + +target_to_host_low_high (arg4, arg5, &low, &high); + ret =get_errno (safe_preadv (arg1, vec, arg3, low, high));unlock_iovec (vec, arg2, arg3,1 ); }else { ret = -host_to_target_errno (errno);
@@ -10462,7 +10479,10 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, {struct iovec *vec =lock_iovec (VERIFY_READ , arg2, arg3,1 );if (vec !=NULL ) { - ret =get_errno (safe_pwritev (arg1, vec, arg3, arg4, arg5)); +unsigned long low, high; + +target_to_host_low_high (arg4, arg5, &low, &high); + ret =get_errno (safe_pwritev (arg1, vec, arg3, low, high));unlock_iovec (vec, arg2, arg3,0 ); }else { ret = -host_to_target_errno (errno);
Signature · 1 line
2.11.0
Le 06/04/2018 à 03:36, Max Filippov a écrit :
Quoted 18 lines
preadv/pwritev accept low and high parts of file offset in two separate parameters. When host bitness doesn't match guest bitness these parts must be appropriately recombined.
Introducetarget_to_host_low_highthat does this recombination and use it in preadv/pwritev syscalls.This fixes glibc testsuite test misc/tst-preadvwritev64.
Signed-off-by Max Filippov <[email protected]>---
Changesv3->v4:
- recombine offset into unsigned long long and split it into two longs.Changes
v2->v3:
- rename helper totarget_to_host_low_high;
- handle casesTARGET_LONG_BITS* 2 >HOST_LONG_BITSandTARGET_LONG_BITS< 2 *HOST_LONG_BITScorrectly.
Quoted 6 lines
Changes
v1->v2:
- fix host high computation inTARGET_LONG_BITS>HOST_LONG_BITScaselinux-user/syscall.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-)
linux-user/syscall.c static abi_long do_getsockopt(int sockfd, int level, int optname, · line 3396
Quoted 8 lines · Max Filippov
linux-user/syscall.c +8
-0+static void target_to_host_low_high (abi_ulong tlow, +abi_ulong thigh, +unsigned long *hlow, +unsigned long *hhigh) +{ +unsigned long long off = tlow | + ((unsigned long long )thigh <<TARGET_LONG_BITS /2 ) << +TARGET_LONG_BITS /2 ;
Use uint64_t instead of unsigned long long.
linux-user/syscall.c static abi_long do_getsockopt(int sockfd, int level, int optname, · line 3400
Quoted 4 lines
linux-user/syscall.c +4
-0+ + *hlow = (unsigned long )off; + *hhigh = (unsigned long )((off >>HOST_LONG_BITS /2 ) >> +HOST_LONG_BITS /2 );
The casts here are unnecessary and are implied by the assignment.
Otherwise,
r~
Quoted 11 lines · Richard Henderson
linux-user/syscall.c static abi_long do_getsockopt(int sockfd, int level, int optname, · line 3396
Quoted 8 lines · Max Filippov
linux-user/syscall.c +8
-0+static void target_to_host_low_high (abi_ulong tlow, +abi_ulong thigh, +unsigned long *hlow, +unsigned long *hhigh) +{ +unsigned long long off = tlow | + ((unsigned long long )thigh <<TARGET_LONG_BITS /2 ) << +TARGET_LONG_BITS /2 ;Use
uint64_tinstead of unsigned long long.
Ok.
Quoted 6 lines
linux-user/syscall.c static abi_long do_getsockopt(int sockfd, int level, int optname, · line 3400
Quoted 4 lines
linux-user/syscall.c +4
-0+ + *hlow = (unsigned long )off; + *hhigh = (unsigned long )((off >>HOST_LONG_BITS /2 ) >> +HOST_LONG_BITS /2 );The casts here are unnecessary and are implied by the assignment.
Did that to avoid value truncation warning. Will drop.
Quoted 2 lines
Otherwise,
Reviewed-by Richard Henderson <[email protected]>
Signature · 2 lines
Thanks. -- Max