
The host syscall callback mechanism should take care of updating the errcode within the CB_SYSCALL struct, and we should not be adjusting the error code once the syscall has completed. We especially, should not be rewriting the syscall errcode based on the value of errno some time after running the host syscall, as there is no guarantee that errno has not be overwritten. To perform a syscall we call cb_syscall (in syscall.c). To return from cb_syscall control passes through one of two exit paths these are labeled FinishSyscall and ErrorFinish and are reached using goto statements scattered throughout the cb_syscall function. In FinishSyscall we store the syscall result in 'sc->result', and the error code is transated to target encoding, and stored in 'sc->errcode'. In ErrorFinish, we again store the syscall result in 'sc->result', and fill in 'sc->errcode' by fetching the actual errno from the host with the 'cb->get_errno' callback. In both cases 'sc->errcode' will have been filled in with an appropriate value. Further, if we look at a specific syscall example, CB_SYS_open, in this case the first thing we do is fetch the path to open from the target with 'get_path', if this fails then the errcode is returned, and we jump to FinishSyscall. Notice that in this case, no host syscall may have been performed, for example a failure to read the path to open out of simulated memory can return EINVAL without performing any host syscall. Given that no host syscall has been performed, reading the host errno makes absolutely no sense. This commit removes from sim_syscall_multi the rewriting of sc->errcode based on the value of errno, and instead relies on the value stored in the cb_syscall. sim/common/ChangeLog: * sim-syscall.c (sim_syscall_multi): Don't update sc->errcode at this point, it should have already been set in cb_syscall.
118 lines
3.3 KiB
C
118 lines
3.3 KiB
C
/* Simulator system call support.
|
||
|
||
Copyright 2002-2018 Free Software Foundation, Inc.
|
||
|
||
This file is part of simulators.
|
||
|
||
This program is free software; you can redistribute it and/or modify
|
||
it under the terms of the GNU General Public License as published by
|
||
the Free Software Foundation; either version 3 of the License, or
|
||
(at your option) any later version.
|
||
|
||
This program is distributed in the hope that it will be useful,
|
||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
GNU General Public License for more details.
|
||
|
||
You should have received a copy of the GNU General Public License
|
||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||
|
||
#include "config.h"
|
||
|
||
#include <errno.h>
|
||
|
||
#include "sim-main.h"
|
||
#include "sim-syscall.h"
|
||
#include "targ-vals.h"
|
||
|
||
/* Read/write functions for system call interface. */
|
||
|
||
int
|
||
sim_syscall_read_mem (host_callback *cb ATTRIBUTE_UNUSED, struct cb_syscall *sc,
|
||
unsigned long taddr, char *buf, int bytes)
|
||
{
|
||
SIM_DESC sd = (SIM_DESC) sc->p1;
|
||
SIM_CPU *cpu = (SIM_CPU *) sc->p2;
|
||
|
||
TRACE_MEMORY (cpu, "READ (syscall) %i bytes @ 0x%08lx", bytes, taddr);
|
||
|
||
return sim_core_read_buffer (sd, cpu, read_map, buf, taddr, bytes);
|
||
}
|
||
|
||
int
|
||
sim_syscall_write_mem (host_callback *cb ATTRIBUTE_UNUSED, struct cb_syscall *sc,
|
||
unsigned long taddr, const char *buf, int bytes)
|
||
{
|
||
SIM_DESC sd = (SIM_DESC) sc->p1;
|
||
SIM_CPU *cpu = (SIM_CPU *) sc->p2;
|
||
|
||
TRACE_MEMORY (cpu, "WRITE (syscall) %i bytes @ 0x%08lx", bytes, taddr);
|
||
|
||
return sim_core_write_buffer (sd, cpu, write_map, buf, taddr, bytes);
|
||
}
|
||
|
||
/* Main syscall callback for simulators. */
|
||
|
||
void
|
||
sim_syscall_multi (SIM_CPU *cpu, int func, long arg1, long arg2, long arg3,
|
||
long arg4, long *result, long *result2, int *errcode)
|
||
{
|
||
SIM_DESC sd = CPU_STATE (cpu);
|
||
host_callback *cb = STATE_CALLBACK (sd);
|
||
CB_SYSCALL sc;
|
||
const char unknown_syscall[] = "<UNKNOWN SYSCALL>";
|
||
const char *syscall;
|
||
|
||
CB_SYSCALL_INIT (&sc);
|
||
|
||
sc.func = func;
|
||
sc.arg1 = arg1;
|
||
sc.arg2 = arg2;
|
||
sc.arg3 = arg3;
|
||
sc.arg4 = arg4;
|
||
|
||
sc.p1 = (PTR) sd;
|
||
sc.p2 = (PTR) cpu;
|
||
sc.read_mem = sim_syscall_read_mem;
|
||
sc.write_mem = sim_syscall_write_mem;
|
||
|
||
if (cb_syscall (cb, &sc) != CB_RC_OK)
|
||
{
|
||
/* The cb_syscall func never returns an error, so this is more of a
|
||
sanity check. */
|
||
sim_engine_abort (sd, cpu, sim_pc_get (cpu), "cb_syscall failed");
|
||
}
|
||
|
||
syscall = cb_target_str_syscall (cb, func);
|
||
if (!syscall)
|
||
syscall = unknown_syscall;
|
||
|
||
if (sc.result == -1)
|
||
TRACE_SYSCALL (cpu, "%s[%i](%#lx, %#lx, %#lx) = %li (error = %s[%i])",
|
||
syscall, func, arg1, arg2, arg3, sc.result,
|
||
cb_target_str_errno (cb, sc.errcode), sc.errcode);
|
||
else
|
||
TRACE_SYSCALL (cpu, "%s[%i](%#lx, %#lx, %#lx) = %li",
|
||
syscall, func, arg1, arg2, arg3, sc.result);
|
||
|
||
if (cb_target_to_host_syscall (cb, func) == CB_SYS_exit)
|
||
sim_engine_halt (sd, cpu, NULL, sim_pc_get (cpu), sim_exited, arg1);
|
||
|
||
*result = sc.result;
|
||
*result2 = sc.result2;
|
||
*errcode = sc.errcode;
|
||
}
|
||
|
||
long
|
||
sim_syscall (SIM_CPU *cpu, int func, long arg1, long arg2, long arg3, long arg4)
|
||
{
|
||
long result, result2;
|
||
int errcode;
|
||
|
||
sim_syscall_multi (cpu, func, arg1, arg2, arg3, arg4, &result, &result2,
|
||
&errcode);
|
||
if (result == -1)
|
||
return -errcode;
|
||
else
|
||
return result;
|
||
}
|