#ifndef __SIM_SYSCALL_EMUL_HH__
#define __SIM_SYSCALL_EMUL_HH__
-#define NO_STAT64 (defined(__APPLE__) || defined(__OpenBSD__) || \
- defined(__FreeBSD__) || defined(__CYGWIN__) || \
- defined(__NetBSD__))
+#if (defined(__APPLE__) || defined(__OpenBSD__) || \
+ defined(__FreeBSD__) || defined(__CYGWIN__) || \
+ defined(__NetBSD__))
+#define NO_STAT64 1
+#else
+#define NO_STAT64 0
+#endif
-#define NO_STATFS (defined(__APPLE__) || defined(__OpenBSD__) || \
- defined(__FreeBSD__) || defined(__NetBSD__))
+#if (defined(__APPLE__) || defined(__OpenBSD__) || \
+ defined(__FreeBSD__) || defined(__NetBSD__))
+#define NO_STATFS 1
+#else
+#define NO_STATFS 0
+#endif
-#define NO_FALLOCATE (defined(__APPLE__) || defined(__OpenBSD__) || \
- defined(__FreeBSD__) || defined(__NetBSD__))
+#if (defined(__APPLE__) || defined(__OpenBSD__) || \
+ defined(__FreeBSD__) || defined(__NetBSD__))
+#define NO_FALLOCATE 1
+#else
+#define NO_FALLOCATE 0
+#endif
///
/// @file syscall_emul.hh
#include "mem/page_table.hh"
#include "params/Process.hh"
#include "sim/emul_driver.hh"
+#include "sim/futex_map.hh"
#include "sim/process.hh"
#include "sim/syscall_debug_macros.hh"
#include "sim/syscall_desc.hh"
SyscallReturn dupFunc(SyscallDesc *desc, int num,
Process *process, ThreadContext *tc);
+/// Target dup2() handler.
+SyscallReturn dup2Func(SyscallDesc *desc, int num,
+ Process *process, ThreadContext *tc);
+
/// Target fcntl() handler.
SyscallReturn fcntlFunc(SyscallDesc *desc, int num,
Process *process, ThreadContext *tc);
SyscallReturn setuidFunc(SyscallDesc *desc, int num,
Process *p, ThreadContext *tc);
+/// Target pipe() handler.
+SyscallReturn pipeFunc(SyscallDesc *desc, int num,
+ Process *p, ThreadContext *tc);
+
+/// Internal pipe() handler.
+SyscallReturn pipeImpl(SyscallDesc *desc, int num, Process *p,
+ ThreadContext *tc, bool pseudoPipe);
+
/// Target getpid() handler.
SyscallReturn getpidFunc(SyscallDesc *desc, int num,
Process *p, ThreadContext *tc);
int index);
/// Futex system call
-/// Implemented by Daniel Sanchez
-/// Used by printf's in multi-threaded apps
+/// Implemented by Daniel Sanchez
+/// Used by printf's in multi-threaded apps
template <class OS>
SyscallReturn
futexFunc(SyscallDesc *desc, int callnum, Process *process,
ThreadContext *tc)
{
- int index_uaddr = 0;
- int index_op = 1;
- int index_val = 2;
- int index_timeout = 3;
-
- uint64_t uaddr = process->getSyscallArg(tc, index_uaddr);
- int op = process->getSyscallArg(tc, index_op);
- int val = process->getSyscallArg(tc, index_val);
- uint64_t timeout = process->getSyscallArg(tc, index_timeout);
-
- std::map<uint64_t, std::list<ThreadContext *> * >
- &futex_map = tc->getSystemPtr()->futexMap;
+ using namespace std;
- DPRINTF(SyscallVerbose, "futex: Address=%llx, op=%d, val=%d\n",
- uaddr, op, val);
+ int index = 0;
+ Addr uaddr = process->getSyscallArg(tc, index);
+ int op = process->getSyscallArg(tc, index);
+ int val = process->getSyscallArg(tc, index);
+ /*
+ * Unsupported option that does not affect the correctness of the
+ * application. This is a performance optimization utilized by Linux.
+ */
op &= ~OS::TGT_FUTEX_PRIVATE_FLAG;
- if (op == OS::TGT_FUTEX_WAIT) {
- if (timeout != 0) {
- warn("futex: FUTEX_WAIT with non-null timeout unimplemented;"
- "we'll wait indefinitely");
- }
+ FutexMap &futex_map = tc->getSystemPtr()->futexMap;
- uint8_t *buf = new uint8_t[sizeof(int)];
- tc->getMemProxy().readBlob((Addr)uaddr, buf, (int)sizeof(int));
- int mem_val = *((int *)buf);
- delete[] buf;
+ if (OS::TGT_FUTEX_WAIT == op) {
+ // Ensure futex system call accessed atomically.
+ BufferArg buf(uaddr, sizeof(int));
+ buf.copyIn(tc->getMemProxy());
+ int mem_val = *(int*)buf.bufferPtr();
- if (val != mem_val) {
- DPRINTF(SyscallVerbose, "futex: FUTEX_WAKE, read: %d, "
- "expected: %d\n", mem_val, val);
+ /*
+ * The value in memory at uaddr is not equal with the expected val
+ * (a different thread must have changed it before the system call was
+ * invoked). In this case, we need to throw an error.
+ */
+ if (val != mem_val)
return -OS::TGT_EWOULDBLOCK;
- }
- // Queue the thread context
- std::list<ThreadContext *> * tcWaitList;
- if (futex_map.count(uaddr)) {
- tcWaitList = futex_map.find(uaddr)->second;
- } else {
- tcWaitList = new std::list<ThreadContext *>();
- futex_map.insert(std::pair< uint64_t,
- std::list<ThreadContext *> * >(uaddr, tcWaitList));
- }
- tcWaitList->push_back(tc);
- DPRINTF(SyscallVerbose, "futex: FUTEX_WAIT, suspending calling thread "
- "context on address 0x%lx\n", uaddr);
- tc->suspend();
- return 0;
- } else if (op == OS::TGT_FUTEX_WAKE){
- int wokenUp = 0;
- std::list<ThreadContext *> * tcWaitList;
- if (futex_map.count(uaddr)) {
- tcWaitList = futex_map.find(uaddr)->second;
- while (tcWaitList->size() > 0 && wokenUp < val) {
- tcWaitList->front()->activate();
- tcWaitList->pop_front();
- wokenUp++;
- }
- if (tcWaitList->empty()) {
- futex_map.erase(uaddr);
- delete tcWaitList;
- }
- }
- DPRINTF(SyscallVerbose, "futex: FUTEX_WAKE, activated %d waiting "
- "thread context on address 0x%lx\n",
- wokenUp, uaddr);
- return wokenUp;
- } else {
- warn("futex: op %d is not implemented, just returning...", op);
+ futex_map.suspend(uaddr, process->tgid(), tc);
+
return 0;
+ } else if (OS::TGT_FUTEX_WAKE == op) {
+ return futex_map.wakeup(uaddr, process->tgid(), val);
}
+ warn("futex: op %d not implemented; ignoring.", op);
+ return -ENOSYS;
}
//// memory space. Used by stat(), fstat(), and lstat().
template <typename target_stat, typename host_stat>
-static void
+void
convertStatBuf(target_stat &tgt, host_stat *host, bool fakeTTY = false)
{
using namespace TheISA;
tgt->st_mtimeX = TheISA::htog(tgt->st_mtimeX);
tgt->st_ctimeX = host->st_ctime;
tgt->st_ctimeX = TheISA::htog(tgt->st_ctimeX);
- // Force the block size to be 8k. This helps to ensure buffered io works
+ // Force the block size to be 8KB. This helps to ensure buffered io works
// consistently across different hosts.
tgt->st_blksize = 0x2000;
tgt->st_blksize = TheISA::htog(tgt->st_blksize);
// Same for stat64
template <typename target_stat, typename host_stat64>
-static void
+void
convertStat64Buf(target_stat &tgt, host_stat64 *host, bool fakeTTY = false)
{
using namespace TheISA;
#endif
}
-//Here are a couple convenience functions
+// Here are a couple of convenience functions
template<class OS>
-static void
+void
copyOutStatBuf(SETranslatingPortProxy &mem, Addr addr,
- hst_stat *host, bool fakeTTY = false)
+ hst_stat *host, bool fakeTTY = false)
{
typedef TypedBufferArg<typename OS::tgt_stat> tgt_stat_buf;
tgt_stat_buf tgt(addr);
}
template<class OS>
-static void
+void
copyOutStat64Buf(SETranslatingPortProxy &mem, Addr addr,
- hst_stat64 *host, bool fakeTTY = false)
+ hst_stat64 *host, bool fakeTTY = false)
{
typedef TypedBufferArg<typename OS::tgt_stat64> tgt_stat_buf;
tgt_stat_buf tgt(addr);
}
template <class OS>
-static void
+void
copyOutStatfsBuf(SETranslatingPortProxy &mem, Addr addr,
hst_statfs *host)
{
}
template <class OS>
-static SyscallReturn
-openFunc(SyscallDesc *desc, int callnum, Process *process,
- ThreadContext *tc, int index)
+SyscallReturn
+openImpl(SyscallDesc *desc, int callnum, Process *p, ThreadContext *tc,
+ bool isopenat)
{
- std::string path;
+ int index = 0;
+ int tgt_dirfd = -1;
- if (!tc->getMemProxy().tryReadString(path,
- process->getSyscallArg(tc, index)))
- return -EFAULT;
+ /**
+ * If using the openat variant, read in the target directory file
+ * descriptor from the simulated process.
+ */
+ if (isopenat)
+ tgt_dirfd = p->getSyscallArg(tc, index);
- int tgtFlags = process->getSyscallArg(tc, index);
- int mode = process->getSyscallArg(tc, index);
- int hostFlags = 0;
+ /**
+ * Retrieve the simulated process' memory proxy and then read in the path
+ * string from that memory space into the host's working memory space.
+ */
+ std::string path;
+ if (!tc->getMemProxy().tryReadString(path, p->getSyscallArg(tc, index)))
+ return -EFAULT;
- // translate open flags
+#ifdef __CYGWIN32__
+ int host_flags = O_BINARY;
+#else
+ int host_flags = 0;
+#endif
+ /**
+ * Translate target flags into host flags. Flags exist which are not
+ * ported between architectures which can cause check failures.
+ */
+ int tgt_flags = p->getSyscallArg(tc, index);
for (int i = 0; i < OS::NUM_OPEN_FLAGS; i++) {
- if (tgtFlags & OS::openFlagTable[i].tgtFlag) {
- tgtFlags &= ~OS::openFlagTable[i].tgtFlag;
- hostFlags |= OS::openFlagTable[i].hostFlag;
+ if (tgt_flags & OS::openFlagTable[i].tgtFlag) {
+ tgt_flags &= ~OS::openFlagTable[i].tgtFlag;
+ host_flags |= OS::openFlagTable[i].hostFlag;
}
}
-
- // any target flags left?
- if (tgtFlags != 0)
- warn("Syscall: open: cannot decode flags 0x%x", tgtFlags);
-
+ if (tgt_flags) {
+ warn("open%s: cannot decode flags 0x%x",
+ isopenat ? "at" : "", tgt_flags);
+ }
#ifdef __CYGWIN32__
- hostFlags |= O_BINARY;
+ host_flags |= O_BINARY;
#endif
- // Adjust path for current working directory
- path = process->fullPath(path);
+ int mode = p->getSyscallArg(tc, index);
- DPRINTF(SyscallVerbose, "opening file %s\n", path.c_str());
+ /**
+ * If the simulated process called open or openat with AT_FDCWD specified,
+ * take the current working directory value which was passed into the
+ * process class as a Python parameter and append the current path to
+ * create a full path.
+ * Otherwise, openat with a valid target directory file descriptor has
+ * been called. If the path option, which was passed in as a parameter,
+ * is not absolute, retrieve the directory file descriptor's path and
+ * prepend it to the path passed in as a parameter.
+ * In every case, we should have a full path (which is relevant to the
+ * host) to work with after this block has been passed.
+ */
+ if (!isopenat || (isopenat && tgt_dirfd == OS::TGT_AT_FDCWD)) {
+ path = p->fullPath(path);
+ } else if (!startswith(path, "/")) {
+ std::shared_ptr<FDEntry> fdep = ((*p->fds)[tgt_dirfd]);
+ auto ffdp = std::dynamic_pointer_cast<FileFDEntry>(fdep);
+ if (!ffdp)
+ return -EBADF;
+ path.insert(0, ffdp->getFileName());
+ }
+ /**
+ * Since this is an emulated environment, we create pseudo file
+ * descriptors for device requests that have been registered with
+ * the process class through Python; this allows us to create a file
+ * descriptor for subsequent ioctl or mmap calls.
+ */
if (startswith(path, "/dev/")) {
std::string filename = path.substr(strlen("/dev/"));
- if (filename == "sysdev0") {
- // This is a memory-mapped high-resolution timer device on Alpha.
- // We don't support it, so just punt.
- warn("Ignoring open(%s, ...)\n", path);
- return -ENOENT;
- }
-
- EmulatedDriver *drv = process->findDriver(filename);
+ EmulatedDriver *drv = p->findDriver(filename);
if (drv) {
- // the driver's open method will allocate a fd from the
- // process if necessary.
- return drv->open(process, tc, mode, hostFlags);
+ DPRINTF_SYSCALL(Verbose, "open%s: passing call to "
+ "driver open with path[%s]\n",
+ isopenat ? "at" : "", path.c_str());
+ return drv->open(p, tc, mode, host_flags);
}
+ /**
+ * Fall through here for pass through to host devices, such
+ * as /dev/zero
+ */
+ }
- // fall through here for pass through to host devices, such as
- // /dev/zero
+ /**
+ * Some special paths and files cannot be called on the host and need
+ * to be handled as special cases inside the simulator.
+ * If the full path that was created above does not match any of the
+ * special cases, pass it through to the open call on the host to let
+ * the host open the file on our behalf.
+ * If the host cannot open the file, return the host's error code back
+ * through the system call to the simulated process.
+ */
+ int sim_fd = -1;
+ std::vector<std::string> special_paths =
+ { "/proc/", "/system/", "/sys/", "/platform/", "/etc/passwd" };
+ for (auto entry : special_paths) {
+ if (startswith(path, entry))
+ sim_fd = OS::openSpecialFile(path, p, tc);
+ }
+ if (sim_fd == -1) {
+ sim_fd = open(path.c_str(), host_flags, mode);
+ }
+ if (sim_fd == -1) {
+ int local = -errno;
+ DPRINTF_SYSCALL(Verbose, "open%s: failed -> path:%s\n",
+ isopenat ? "at" : "", path.c_str());
+ return local;
}
- int fd;
- int local_errno;
- if (startswith(path, "/proc/") || startswith(path, "/system/") ||
- startswith(path, "/platform/") || startswith(path, "/sys/")) {
- // It's a proc/sys entry and requires special handling
- fd = OS::openSpecialFile(path, process, tc);
- local_errno = ENOENT;
- } else {
- // open the file
- fd = open(path.c_str(), hostFlags, mode);
- local_errno = errno;
- }
-
- if (fd == -1)
- return -local_errno;
-
- std::shared_ptr<FileFDEntry> ffdp =
- std::make_shared<FileFDEntry>(fd, hostFlags, path.c_str(), false);
- return process->fds->allocFD(ffdp);
+ /**
+ * The file was opened successfully and needs to be recorded in the
+ * process' file descriptor array so that it can be retrieved later.
+ * The target file descriptor that is chosen will be the lowest unused
+ * file descriptor.
+ * Return the indirect target file descriptor back to the simulated
+ * process to act as a handle for the opened file.
+ */
+ auto ffdp = std::make_shared<FileFDEntry>(sim_fd, host_flags, path, 0);
+ int tgt_fd = p->fds->allocFD(ffdp);
+ DPRINTF_SYSCALL(Verbose, "open%s: sim_fd[%d], target_fd[%d] -> path:%s\n",
+ isopenat ? "at" : "", sim_fd, tgt_fd, path.c_str());
+ return tgt_fd;
}
/// Target open() handler.
openFunc(SyscallDesc *desc, int callnum, Process *process,
ThreadContext *tc)
{
- return openFunc<OS>(desc, callnum, process, tc, 0);
+ return openImpl<OS>(desc, callnum, process, tc, false);
}
/// Target openat() handler.
openatFunc(SyscallDesc *desc, int callnum, Process *process,
ThreadContext *tc)
{
- int index = 0;
- int dirfd = process->getSyscallArg(tc, index);
- if (dirfd != OS::TGT_AT_FDCWD)
- warn("openat: first argument not AT_FDCWD; unlikely to work");
- return openFunc<OS>(desc, callnum, process, tc, 1);
+ return openImpl<OS>(desc, callnum, process, tc, true);
}
/// Target unlinkat() handler.
if ((start + old_length) == mmap_end &&
(!use_provided_address || provided_address == start)) {
+ // This case cannot occur when growing downward, as
+ // start is greater than or equal to mmap_end.
uint64_t diff = new_length - old_length;
process->allocateMem(mmap_end, diff);
mem_state->setMmapEnd(mmap_end + diff);
warn("can't remap here and MREMAP_MAYMOVE flag not set\n");
return -ENOMEM;
} else {
- uint64_t new_start = use_provided_address ?
- provided_address : mmap_end;
+ uint64_t new_start = provided_address;
+ if (!use_provided_address) {
+ new_start = process->mmapGrowsDown() ?
+ mmap_end - new_length : mmap_end;
+ mmap_end = process->mmapGrowsDown() ?
+ new_start : mmap_end + new_length;
+ mem_state->setMmapEnd(mmap_end);
+ }
+
process->pTable->remap(start, old_length, new_start);
warn("mremapping to new vaddr %08p-%08p, adding %d\n",
new_start, new_start + new_length,
process->allocateMem(new_start + old_length,
new_length - old_length,
use_provided_address /* clobber */);
- if (!use_provided_address)
- mem_state->setMmapEnd(mmap_end + new_length);
if (use_provided_address &&
- new_start + new_length > mem_state->getMmapEnd()) {
+ ((new_start + new_length > mem_state->getMmapEnd() &&
+ !process->mmapGrowsDown()) ||
+ (new_start < mem_state->getMmapEnd() &&
+ process->mmapGrowsDown()))) {
// something fishy going on here, at least notify the user
// @todo: increase mmap_end?
warn("mmap region limit exceeded with MREMAP_FIXED\n");
cp->initState();
p->clone(tc, ctc, cp, flags);
+ if (flags & OS::TGT_CLONE_THREAD) {
+ delete cp->sigchld;
+ cp->sigchld = p->sigchld;
+ } else if (flags & OS::TGT_SIGCHLD) {
+ *cp->sigchld = true;
+ }
+
if (flags & OS::TGT_CLONE_CHILD_SETTID) {
BufferArg ctidBuf(ctidPtr, sizeof(long));
long *ctid = (long *)ctidBuf.bufferPtr();
return -EBADF;
sim_fd = ffdp->getSimFD();
- pmap = (decltype(pmap))mmap(NULL, length, PROT_READ, MAP_PRIVATE,
+ pmap = (decltype(pmap))mmap(nullptr, length, PROT_READ, MAP_PRIVATE,
sim_fd, offset);
if (pmap == (decltype(pmap))-1) {
TypedBufferArg<typename OS::rlimit> rlp(process->getSyscallArg(tc, index));
switch (resource) {
- case OS::TGT_RLIMIT_STACK:
+ case OS::TGT_RLIMIT_STACK:
+ // max stack size in bytes: make up a number (8MB for now)
+ rlp->rlim_cur = rlp->rlim_max = 8 * 1024 * 1024;
+ rlp->rlim_cur = TheISA::htog(rlp->rlim_cur);
+ rlp->rlim_max = TheISA::htog(rlp->rlim_max);
+ break;
+
+ case OS::TGT_RLIMIT_DATA:
+ // max data segment size in bytes: make up a number
+ rlp->rlim_cur = rlp->rlim_max = 256 * 1024 * 1024;
+ rlp->rlim_cur = TheISA::htog(rlp->rlim_cur);
+ rlp->rlim_max = TheISA::htog(rlp->rlim_max);
+ break;
+
+ default:
+ warn("getrlimit: unimplemented resource %d", resource);
+ return -EINVAL;
+ break;
+ }
+
+ rlp.copyOut(tc->getMemProxy());
+ return 0;
+}
+
+template <class OS>
+SyscallReturn
+prlimitFunc(SyscallDesc *desc, int callnum, Process *process,
+ ThreadContext *tc)
+{
+ int index = 0;
+ if (process->getSyscallArg(tc, index) != 0)
+ {
+ warn("prlimit: ignoring rlimits for nonzero pid");
+ return -EPERM;
+ }
+ int resource = process->getSyscallArg(tc, index);
+ Addr n = process->getSyscallArg(tc, index);
+ if (n != 0)
+ warn("prlimit: ignoring new rlimit");
+ Addr o = process->getSyscallArg(tc, index);
+ if (o != 0)
+ {
+ TypedBufferArg<typename OS::rlimit> rlp(
+ process->getSyscallArg(tc, index));
+ switch (resource) {
+ case OS::TGT_RLIMIT_STACK:
// max stack size in bytes: make up a number (8MB for now)
rlp->rlim_cur = rlp->rlim_max = 8 * 1024 * 1024;
rlp->rlim_cur = TheISA::htog(rlp->rlim_cur);
rlp->rlim_max = TheISA::htog(rlp->rlim_max);
break;
-
- case OS::TGT_RLIMIT_DATA:
+ case OS::TGT_RLIMIT_DATA:
// max data segment size in bytes: make up a number
- rlp->rlim_cur = rlp->rlim_max = 256 * 1024 * 1024;
+ rlp->rlim_cur = rlp->rlim_max = 256*1024*1024;
rlp->rlim_cur = TheISA::htog(rlp->rlim_cur);
rlp->rlim_max = TheISA::htog(rlp->rlim_max);
- break;
-
- default:
- warn("getrlimit: unimplemented resource %d", resource);
+ default:
+ warn("prlimit: unimplemented resource %d", resource);
return -EINVAL;
break;
+ }
+ rlp.copyOut(tc->getMemProxy());
}
-
- rlp.copyOut(tc->getMemProxy());
return 0;
}
tp.copyIn(tc->getMemProxy());
struct timeval hostTimeval[2];
- for (int i = 0; i < 2; ++i)
- {
+ for (int i = 0; i < 2; ++i) {
hostTimeval[i].tv_sec = TheISA::gtoh((*tp)[i].tv_sec);
hostTimeval[i].tv_usec = TheISA::gtoh((*tp)[i].tv_usec);
}
return sec;
}
+template <class OS>
+SyscallReturn
+tgkillFunc(SyscallDesc *desc, int num, Process *process, ThreadContext *tc)
+{
+ int index = 0;
+ int tgid = process->getSyscallArg(tc, index);
+ int tid = process->getSyscallArg(tc, index);
+ int sig = process->getSyscallArg(tc, index);
+
+ /**
+ * This system call is intended to allow killing a specific thread
+ * within an arbitrary thread group if sanctioned with permission checks.
+ * It's usually true that threads share the termination signal as pointed
+ * out by the pthread_kill man page and this seems to be the intended
+ * usage. Due to this being an emulated environment, assume the following:
+ * Threads are allowed to call tgkill because the EUID for all threads
+ * should be the same. There is no signal handling mechanism for kernel
+ * registration of signal handlers since signals are poorly supported in
+ * emulation mode. Since signal handlers cannot be registered, all
+ * threads within in a thread group must share the termination signal.
+ * We never exhaust PIDs so there's no chance of finding the wrong one
+ * due to PID rollover.
+ */
+
+ System *sys = tc->getSystemPtr();
+ Process *tgt_proc = nullptr;
+ for (int i = 0; i < sys->numContexts(); i++) {
+ Process *temp = sys->threadContexts[i]->getProcessPtr();
+ if (temp->pid() == tid) {
+ tgt_proc = temp;
+ break;
+ }
+ }
+
+ if (sig != 0 || sig != OS::TGT_SIGABRT)
+ return -EINVAL;
+
+ if (tgt_proc == nullptr)
+ return -ESRCH;
+
+ if (tgid != -1 && tgt_proc->tgid() != tgid)
+ return -ESRCH;
+
+ if (sig == OS::TGT_SIGABRT)
+ exitGroupFunc(desc, 252, process, tc);
+
+ return 0;
+}
+
#endif // __SIM_SYSCALL_EMUL_HH__