Skip to content

Commit

Permalink
i#58 MacOS: client syscall results
Browse files Browse the repository at this point in the history
Add dr_syscall_get_result_ex() and dr_syscall_set_result_ex() to support
properly getting and setting the return value of system calls in MacOS,
where CF indicates success and positive errno is used.

These routines both take in a new data structure that includes support for
32-bit system calls that return 64-bit values, as 32-bit return values do
not have the upper bits cleared, making a syscall-wide 64-bit return value
not feasible.  It is up to the user to know which syscalls have which
return types, though (they can use drsyscall if they want automated result
types).

The data structure includes errno support, allowing for cross-platform
errno reading and writing without worrying about whether it should be
negated or not.

Added a test to client.strace.

SVN-Revision: 2502
  • Loading branch information
derekbruening committed Jan 28, 2014
1 parent 3fc5973 commit 1a9abc3
Show file tree
Hide file tree
Showing 9 changed files with 332 additions and 31 deletions.
16 changes: 10 additions & 6 deletions api/samples/strace.c
Expand Up @@ -243,9 +243,12 @@ event_pre_syscall(void *drcontext, int sysnum)
/* pretend it succeeded */
#ifdef UNIX
/* return the #bytes == 3rd param */
dr_syscall_set_result(drcontext, dr_syscall_get_param(drcontext, 2));
dr_syscall_result_info_t info = { sizeof(info), };
info.succeeded = true;
info.value = dr_syscall_get_param(drcontext, 2);
dr_syscall_set_result_ex(drcontext, &info);
#else
/* we should also set the IO_STATUS_BLOCK.Information field */
/* XXX: we should also set the IO_STATUS_BLOCK.Information field */
dr_syscall_set_result(drcontext, 0);
#endif
#ifdef SHOW_RESULTS
Expand All @@ -271,10 +274,11 @@ static void
event_post_syscall(void *drcontext, int sysnum)
{
#ifdef SHOW_RESULTS
dr_fprintf(STDERR, " [%d] => "PFX" ("SZFMT")\n",
sysnum,
dr_syscall_get_result(drcontext),
(ptr_int_t)dr_syscall_get_result(drcontext));
dr_syscall_result_info_t info = { sizeof(info), };
dr_syscall_get_result_ex(drcontext, &info);
dr_fprintf(STDERR, " [%d] => "PFX" ("SZFMT")%s\n",
sysnum, info.value, (ptr_int_t)info.value,
info.succeeded ? "" : " (failed)");
#endif
if (sysnum == write_sysnum) {
per_thread_t *data = (per_thread_t *) drmgr_get_cls_field(drcontext, tcls_idx);
Expand Down
92 changes: 84 additions & 8 deletions core/unix/os.c
Expand Up @@ -4124,6 +4124,16 @@ sys_param(dcontext_t *dcontext, int num)
return *sys_param_addr(dcontext, num);
}

static inline bool
syscall_successful(priv_mcontext_t *mc)
{
#ifdef MACOS
return !TEST(EFLAGS_CF, mc->eflags);
#else
return ((ptr_int_t)mc->xax >= 0);
#endif
}

/* For non-Mac, this does nothing to indicate "success": you can pass -errno.
* For Mac, this clears CF and just sets xax. To return a 64-bit value in
* 32-bit mode, the caller must explicitly set xdx as well (we don't always
Expand Down Expand Up @@ -4187,6 +4197,41 @@ dr_syscall_get_result(void *drcontext)
return get_mcontext(dcontext)->xax;
}

DR_API
bool
dr_syscall_get_result_ex(void *drcontext, dr_syscall_result_info_t *info INOUT)
{
dcontext_t *dcontext = (dcontext_t *) drcontext;
priv_mcontext_t *mc = get_mcontext(dcontext);
CLIENT_ASSERT(dcontext->client_data->in_post_syscall,
"only call dr_syscall_get_param_ex() from post-syscall event");
CLIENT_ASSERT(info != NULL, "invalid parameter");
CLIENT_ASSERT(info->size == sizeof(*info), "invalid dr_syscall_result_info_t size");
if (info->size != sizeof(*info))
return false;
info->value = mc->xax;
info->succeeded = syscall_successful(mc);
if (info->use_high) {
/* MacOS has some 32-bit syscalls that return 64-bit values in
* xdx:xax, but the other syscalls don't clear xdx, so we can't easily
* return a 64-bit value all the time.
*/
info->high = mc->xdx;
}
if (info->use_errno) {
if (info->succeeded)
info->errno_value = 0;
else {
#ifdef LINUX
info->errno_value = (uint)-(int)mc->xax;
#else
info->errno_value = (uint)mc->xax;
#endif
}
}
return true;
}

DR_API
void
dr_syscall_set_result(void *drcontext, reg_t value)
Expand All @@ -4195,12 +4240,47 @@ dr_syscall_set_result(void *drcontext, reg_t value)
CLIENT_ASSERT(dcontext->client_data->in_pre_syscall ||
dcontext->client_data->in_post_syscall,
"dr_syscall_set_result() can only be called from a syscall event");
/* For non-Mac, the caller can still pass -errno and this will work.
* XXX: we need dr_syscall_set_result_ex() for Mac.
*/
/* For non-Mac, the caller can still pass -errno and this will work */
set_success_return_val(dcontext, value);
}

DR_API
bool
dr_syscall_set_result_ex(void *drcontext, dr_syscall_result_info_t *info)
{
dcontext_t *dcontext = (dcontext_t *) drcontext;
priv_mcontext_t *mc = get_mcontext(dcontext);
CLIENT_ASSERT(dcontext->client_data->in_pre_syscall ||
dcontext->client_data->in_post_syscall,
"dr_syscall_set_result() can only be called from a syscall event");
CLIENT_ASSERT(info->size == sizeof(*info), "invalid dr_syscall_result_info_t size");
if (info->size != sizeof(*info))
return false;
if (info->use_errno) {
if (info->succeeded) {
/* a weird case but we let the user combine these */
set_success_return_val(dcontext, info->errno_value);
} else
set_failure_return_val(dcontext, info->errno_value);
} else {
if (info->succeeded)
set_success_return_val(dcontext, info->value);
else {
/* use this to set CF, even though it might negate the value */
set_failure_return_val(dcontext, (uint)info->value);
/* now set the value, overriding set_failure_return_val() */
mc->xax = info->value;
}
if (info->use_high) {
/* MacOS has some 32-bit syscalls that return 64-bit values in
* xdx:xax.
*/
mc->xdx = info->high;
}
}
return true;
}

DR_API
void
dr_syscall_set_sysnum(void *drcontext, int new_num)
Expand Down Expand Up @@ -6184,11 +6264,7 @@ post_system_call(dcontext_t *dcontext)
* case-by-case basis in the switch statement below.
*/
ptr_int_t result = (ptr_int_t) mc->xax; /* signed */
#ifdef MACOS
bool success = !TEST(EFLAGS_CF, mc->eflags);
#else
bool success = (result >= 0);
#endif
bool success = syscall_successful(mc);
app_pc base;
size_t size;
uint prot;
Expand Down
47 changes: 45 additions & 2 deletions core/win32/syscall.c
@@ -1,5 +1,5 @@
/* **********************************************************
* Copyright (c) 2011-2013 Google, Inc. All rights reserved.
* Copyright (c) 2011-2014 Google, Inc. All rights reserved.
* Copyright (c) 2006-2010 VMware, Inc. All rights reserved.
* **********************************************************/

Expand Down Expand Up @@ -880,7 +880,7 @@ syscall_uses_edx_param_base()

/* since always coming from dispatch now, only need to set mcontext */
#define SET_RETURN_VAL(dc, val) \
get_mcontext(dc)->xax = (reg_t) (val);
get_mcontext(dc)->xax = (reg_t) (val)

/***************************************************************************
* PRE SYSTEM CALL
Expand Down Expand Up @@ -4029,6 +4029,27 @@ dr_syscall_get_result(void *drcontext)
return get_mcontext(dcontext)->xax;
}

DR_API
bool
dr_syscall_get_result_ex(void *drcontext, dr_syscall_result_info_t *info INOUT)
{
dcontext_t *dcontext = (dcontext_t *) drcontext;
CLIENT_ASSERT(dcontext->client_data->in_post_syscall,
"only call dr_syscall_get_result_ex() from post-syscall event");
CLIENT_ASSERT(info != NULL, "invalid parameter");
CLIENT_ASSERT(info->size == sizeof(*info), "invalid dr_syscall_result_info_t size");
if (info->size != sizeof(*info))
return false;
info->value = dr_syscall_get_result(drcontext);
/* We document not to rely on this for non-ntoskrnl syscalls */
info->succeeded = NT_SUCCESS(info->value);
if (info->use_high)
info->high = 0;
if (info->use_errno)
info->errno_value = (uint) info->value;
return true;
}

DR_API
void
dr_syscall_set_result(void *drcontext, reg_t value)
Expand All @@ -4040,6 +4061,28 @@ dr_syscall_set_result(void *drcontext, reg_t value)
SET_RETURN_VAL(dcontext, value);
}

DR_API
bool
dr_syscall_set_result_ex(void *drcontext, dr_syscall_result_info_t *info)
{
dcontext_t *dcontext = (dcontext_t *) drcontext;
CLIENT_ASSERT(dcontext->client_data->in_pre_syscall ||
dcontext->client_data->in_post_syscall,
"only call dr_syscall_set_result_ex() from a syscall event");
CLIENT_ASSERT(info != NULL, "invalid parameter");
CLIENT_ASSERT(info->size == sizeof(*info), "invalid dr_syscall_result_info_t size");
if (info->size != sizeof(*info))
return false;
if (info->use_high)
return false; /* not supported */
/* we ignore info->succeeded */
if (info->use_errno)
SET_RETURN_VAL(dcontext, info->errno_value);
else
SET_RETURN_VAL(dcontext, info->value);
return true;
}

DR_API
void
dr_syscall_set_sysnum(void *drcontext, int new_num)
Expand Down

0 comments on commit 1a9abc3

Please sign in to comment.