// for _Exit() #include #include #include #include // for fork() and execve() #include // for sched_yield() #include #include // for getrusage() #include // for waitpid() #include #include #include #include #include #include #include #include #include #include namespace mlibc { int sys_futex_tid() { HelWord tid = 0; HEL_CHECK(helSyscall0_1(kHelCallSuper + posix::superGetTid, &tid)); return tid; } int sys_futex_wait(int *pointer, int expected, const struct timespec *time) { // This implementation is inherently signal-safe. if (time) { if (helFutexWait(pointer, expected, time->tv_nsec + time->tv_sec * 1000000000)) return -1; return 0; } if (helFutexWait(pointer, expected, -1)) return -1; return 0; } int sys_futex_wake(int *pointer) { // This implementation is inherently signal-safe. if (helFutexWake(pointer)) return -1; return 0; } int sys_waitpid(pid_t pid, int *status, int flags, struct rusage *ru, pid_t *ret_pid) { SignalGuard sguard; if (ru) { mlibc::infoLogger() << "mlibc: struct rusage in sys_waitpid is unsupported" << frg::endlog; return ENOSYS; } managarm::posix::CntRequest req(getSysdepsAllocator()); req.set_request_type(managarm::posix::CntReqType::WAIT); req.set_pid(pid); req.set_flags(flags); req.set_cancellation_id(allocateCancellationId()); auto [offer, send_head, recv_resp] = exchangeMsgsSyncCancellable( getPosixLane(), req.cancellation_id(), -1, helix_ng::offer( helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()), helix_ng::recvInline() ) ); HEL_CHECK(offer.error()); HEL_CHECK(send_head.error()); HEL_CHECK(recv_resp.error()); managarm::posix::SvrResponse resp(getSysdepsAllocator()); resp.ParseFromArray(recv_resp.data(), recv_resp.length()); if (resp.error() != managarm::posix::Errors::SUCCESS) return resp.error() | toErrno; *ret_pid = resp.pid(); if (*ret_pid == 0) return 0; if (status) *status = resp.mode(); if (ru != nullptr) { ru->ru_utime.tv_sec = resp.ru_user_time() / 1'000'000'000; ru->ru_utime.tv_usec = (resp.ru_user_time() % 1'000'000'000) / 1'000; } return 0; } int sys_waitid(idtype_t idtype, id_t id, siginfo_t *info, int options) { SignalGuard sguard; managarm::posix::WaitIdRequest req(getSysdepsAllocator()); req.set_idtype(idtype); req.set_id(id); req.set_flags(options); auto [offer, send_head, recv_resp] = exchangeMsgsSync( getPosixLane(), helix_ng::offer( helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()), helix_ng::recvInline() ) ); HEL_CHECK(offer.error()); HEL_CHECK(send_head.error()); HEL_CHECK(recv_resp.error()); managarm::posix::WaitIdResponse resp(getSysdepsAllocator()); resp.ParseFromArray(recv_resp.data(), recv_resp.length()); if (resp.error() != managarm::posix::Errors::SUCCESS) return resp.error() | toErrno; info->si_pid = resp.pid(); info->si_uid = resp.uid(); info->si_code = resp.sig_code(); switch (info->si_code) { case CLD_EXITED: info->si_status = WEXITSTATUS(resp.sig_status()); break; case CLD_KILLED: case CLD_DUMPED: case CLD_STOPPED: info->si_signo = WSTOPSIG(resp.sig_status()); break; case CLD_CONTINUED: info->si_signo = SIGCHLD; break; } return 0; } void sys_exit(int status) { // This implementation is inherently signal-safe. HEL_CHECK(helSyscall1(kHelCallSuper + posix::superExit, status)); __builtin_trap(); } void sys_yield() { // This implementation is inherently signal-safe. HEL_CHECK(helYield()); } int sys_sleep(time_t *secs, long *nanos) { SignalGuard sguard; globalQueue.trim(); uint64_t now; HEL_CHECK(helGetClock(&now)); uint64_t async_id; HEL_CHECK(helSubmitAwaitClock( now + uint64_t(*secs) * 1000000000 + uint64_t(*nanos), globalQueue.getQueue(), 0, &async_id )); auto element = globalQueue.dequeueSingle(); auto result = parseSimple(element); HEL_CHECK(result->error); *secs = 0; *nanos = 0; return 0; } int sys_fork(pid_t *child) { // This implementation is inherently signal-safe. int res; sigset_t full_sigset; res = sigfillset(&full_sigset); __ensure(!res); sigset_t former_sigset; res = sigprocmask(SIG_SETMASK, &full_sigset, &former_sigset); __ensure(!res); HelWord out; HEL_CHECK(helSyscall0_1(kHelCallSuper + posix::superFork, &out)); *child = out; if (!out) { clearCachedInfos(); globalQueue.recreateQueue(); } res = sigprocmask(SIG_SETMASK, &former_sigset, nullptr); __ensure(!res); return 0; } int sys_execve(const char *path, char *const argv[], char *const envp[]) { // TODO: Make this function signal-safe! frg::string args_area(getSysdepsAllocator()); for (auto it = argv; *it; ++it) args_area += frg::string_view{*it, strlen(*it) + 1}; frg::string env_area(getSysdepsAllocator()); for (auto it = envp; *it; ++it) env_area += frg::string_view{*it, strlen(*it) + 1}; uintptr_t out; HEL_CHECK(helSyscall6_1( kHelCallSuper + posix::superExecve, reinterpret_cast(path), strlen(path), reinterpret_cast(args_area.data()), args_area.size(), reinterpret_cast(env_area.data()), env_area.size(), &out )); return out; } gid_t sys_getgid() { SignalGuard sguard; managarm::posix::GetGidRequest req(getSysdepsAllocator()); auto [offer, send_head, recv_resp] = exchangeMsgsSync( getPosixLane(), helix_ng::offer( helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()), helix_ng::recvInline() ) ); HEL_CHECK(offer.error()); HEL_CHECK(send_head.error()); HEL_CHECK(recv_resp.error()); managarm::posix::SvrResponse resp(getSysdepsAllocator()); resp.ParseFromArray(recv_resp.data(), recv_resp.length()); __ensure(resp.error() == managarm::posix::Errors::SUCCESS); return resp.uid(); } int sys_setgid(gid_t gid) { SignalGuard sguard; managarm::posix::SetGidRequest req(getSysdepsAllocator()); req.set_uid(gid); auto [offer, send_head, recv_resp] = exchangeMsgsSync( getPosixLane(), helix_ng::offer( helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()), helix_ng::recvInline() ) ); HEL_CHECK(offer.error()); HEL_CHECK(send_head.error()); HEL_CHECK(recv_resp.error()); managarm::posix::SvrResponse resp(getSysdepsAllocator()); resp.ParseFromArray(recv_resp.data(), recv_resp.length()); if (resp.error() != managarm::posix::Errors::SUCCESS) return resp.error() | toErrno; return 0; } gid_t sys_getegid() { SignalGuard sguard; managarm::posix::GetEgidRequest req(getSysdepsAllocator()); auto [offer, send_head, recv_resp] = exchangeMsgsSync( getPosixLane(), helix_ng::offer( helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()), helix_ng::recvInline() ) ); HEL_CHECK(offer.error()); HEL_CHECK(send_head.error()); HEL_CHECK(recv_resp.error()); managarm::posix::SvrResponse resp(getSysdepsAllocator()); resp.ParseFromArray(recv_resp.data(), recv_resp.length()); __ensure(resp.error() == managarm::posix::Errors::SUCCESS); return resp.uid(); } int sys_setegid(gid_t egid) { SignalGuard sguard; managarm::posix::SetEgidRequest req(getSysdepsAllocator()); req.set_uid(egid); auto [offer, send_head, recv_resp] = exchangeMsgsSync( getPosixLane(), helix_ng::offer( helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()), helix_ng::recvInline() ) ); HEL_CHECK(offer.error()); HEL_CHECK(send_head.error()); HEL_CHECK(recv_resp.error()); managarm::posix::SvrResponse resp(getSysdepsAllocator()); resp.ParseFromArray(recv_resp.data(), recv_resp.length()); if (resp.error() != managarm::posix::Errors::SUCCESS) return resp.error() | toErrno; return 0; } int sys_setresgid(gid_t rgid, gid_t egid, gid_t sgid) { // TODO: handle saved set-user-ID (void)sgid; int real = sys_setgid(rgid); if (real) return real; int effective = sys_setegid(egid); if (effective) return effective; return 0; } uid_t sys_getuid() { SignalGuard sguard; managarm::posix::GetUidRequest req(getSysdepsAllocator()); auto [offer, send_head, recv_resp] = exchangeMsgsSync( getPosixLane(), helix_ng::offer( helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()), helix_ng::recvInline() ) ); HEL_CHECK(offer.error()); HEL_CHECK(send_head.error()); HEL_CHECK(recv_resp.error()); managarm::posix::SvrResponse resp(getSysdepsAllocator()); resp.ParseFromArray(recv_resp.data(), recv_resp.length()); __ensure(resp.error() == managarm::posix::Errors::SUCCESS); return resp.uid(); } int sys_setuid(uid_t uid) { SignalGuard sguard; managarm::posix::SetUidRequest req(getSysdepsAllocator()); req.set_uid(uid); auto [offer, send_head, recv_resp] = exchangeMsgsSync( getPosixLane(), helix_ng::offer( helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()), helix_ng::recvInline() ) ); HEL_CHECK(offer.error()); HEL_CHECK(send_head.error()); HEL_CHECK(recv_resp.error()); managarm::posix::SvrResponse resp(getSysdepsAllocator()); resp.ParseFromArray(recv_resp.data(), recv_resp.length()); if (resp.error() != managarm::posix::Errors::SUCCESS) return resp.error() | toErrno; return 0; } uid_t sys_geteuid() { SignalGuard sguard; managarm::posix::GetEuidRequest req(getSysdepsAllocator()); auto [offer, send_head, recv_resp] = exchangeMsgsSync( getPosixLane(), helix_ng::offer( helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()), helix_ng::recvInline() ) ); HEL_CHECK(offer.error()); HEL_CHECK(send_head.error()); HEL_CHECK(recv_resp.error()); managarm::posix::SvrResponse resp(getSysdepsAllocator()); resp.ParseFromArray(recv_resp.data(), recv_resp.length()); __ensure(resp.error() == managarm::posix::Errors::SUCCESS); return resp.uid(); } int sys_seteuid(uid_t euid) { SignalGuard sguard; managarm::posix::SetEuidRequest req(getSysdepsAllocator()); req.set_uid(euid); auto [offer, send_head, recv_resp] = exchangeMsgsSync( getPosixLane(), helix_ng::offer( helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()), helix_ng::recvInline() ) ); HEL_CHECK(offer.error()); HEL_CHECK(send_head.error()); HEL_CHECK(recv_resp.error()); managarm::posix::SvrResponse resp(getSysdepsAllocator()); resp.ParseFromArray(recv_resp.data(), recv_resp.length()); if (resp.error() != managarm::posix::Errors::SUCCESS) return resp.error() | toErrno; return 0; } int sys_setresuid(uid_t ruid, uid_t euid, uid_t suid) { // TODO: handle saved set-user-ID (void)suid; int real = sys_setuid(ruid); if (real) return real; int effective = sys_seteuid(euid); if (effective) return effective; return 0; } int sys_setreuid(uid_t ruid, uid_t euid) { int real = sys_setuid(ruid); if (real) return real; int effective = sys_seteuid(euid); if (effective) return effective; return 0; } int sys_setregid(gid_t rgid, gid_t egid) { int real = sys_setgid(rgid); if (real) return real; int effective = sys_setegid(egid); if (effective) return effective; return 0; } pid_t sys_gettid() { HelWord tid = 0; HEL_CHECK(helSyscall0_1(kHelCallSuper + posix::superGetTid, &tid)); return tid; } pid_t sys_getpid() { SignalGuard sguard; managarm::posix::GetPidRequest req(getSysdepsAllocator()); auto [offer, send_head, recv_resp] = exchangeMsgsSync( getPosixLane(), helix_ng::offer( helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()), helix_ng::recvInline() ) ); HEL_CHECK(offer.error()); HEL_CHECK(send_head.error()); HEL_CHECK(recv_resp.error()); managarm::posix::SvrResponse resp(getSysdepsAllocator()); resp.ParseFromArray(recv_resp.data(), recv_resp.length()); __ensure(resp.error() == managarm::posix::Errors::SUCCESS); return resp.pid(); } pid_t sys_getppid() { SignalGuard sguard; managarm::posix::GetPpidRequest req(getSysdepsAllocator()); auto [offer, send_head, recv_resp] = exchangeMsgsSync( getPosixLane(), helix_ng::offer( helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()), helix_ng::recvInline() ) ); HEL_CHECK(offer.error()); HEL_CHECK(send_head.error()); HEL_CHECK(recv_resp.error()); managarm::posix::SvrResponse resp(getSysdepsAllocator()); resp.ParseFromArray(recv_resp.data(), recv_resp.length()); __ensure(resp.error() == managarm::posix::Errors::SUCCESS); return resp.pid(); } int sys_getsid(pid_t pid, pid_t *sid) { SignalGuard sguard; managarm::posix::GetSidRequest req(getSysdepsAllocator()); req.set_pid(pid); auto [offer, send_head, recv_resp] = exchangeMsgsSync( getPosixLane(), helix_ng::offer( helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()), helix_ng::recvInline() ) ); HEL_CHECK(offer.error()); HEL_CHECK(send_head.error()); HEL_CHECK(recv_resp.error()); managarm::posix::SvrResponse resp(getSysdepsAllocator()); resp.ParseFromArray(recv_resp.data(), recv_resp.length()); if (resp.error() == managarm::posix::Errors::NO_SUCH_RESOURCE) { *sid = 0; return ESRCH; } else if (resp.error() != managarm::posix::Errors::SUCCESS) { return resp.error() | toErrno; } *sid = resp.pid(); return 0; } int sys_getpgid(pid_t pid, pid_t *pgid) { SignalGuard sguard; managarm::posix::GetPgidRequest req(getSysdepsAllocator()); req.set_pid(pid); auto [offer, send_head, recv_resp] = exchangeMsgsSync( getPosixLane(), helix_ng::offer( helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()), helix_ng::recvInline() ) ); HEL_CHECK(offer.error()); HEL_CHECK(send_head.error()); HEL_CHECK(recv_resp.error()); managarm::posix::SvrResponse resp(getSysdepsAllocator()); resp.ParseFromArray(recv_resp.data(), recv_resp.length()); if (resp.error() == managarm::posix::Errors::NO_SUCH_RESOURCE) { *pgid = 0; return ESRCH; } else if (resp.error() != managarm::posix::Errors::SUCCESS) { return resp.error() | toErrno; } *pgid = resp.pid(); return 0; } int sys_setpgid(pid_t pid, pid_t pgid) { SignalGuard sguard; managarm::posix::SetPgidRequest req(getSysdepsAllocator()); req.set_pid(pid); req.set_pgid(pgid); auto [offer, send_head, recv_resp] = exchangeMsgsSync( getPosixLane(), helix_ng::offer( helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()), helix_ng::recvInline() ) ); HEL_CHECK(offer.error()); HEL_CHECK(send_head.error()); HEL_CHECK(recv_resp.error()); managarm::posix::SvrResponse resp(getSysdepsAllocator()); resp.ParseFromArray(recv_resp.data(), recv_resp.length()); if (resp.error() != managarm::posix::Errors::SUCCESS) return resp.error() | toErrno; return 0; } int sys_getrusage(int scope, struct rusage *usage) { memset(usage, 0, sizeof(struct rusage)); SignalGuard sguard; managarm::posix::CntRequest req(getSysdepsAllocator()); req.set_request_type(managarm::posix::CntReqType::GET_RESOURCE_USAGE); req.set_mode(scope); auto [offer, send_head, recv_resp] = exchangeMsgsSync( getPosixLane(), helix_ng::offer( helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()), helix_ng::recvInline() ) ); HEL_CHECK(offer.error()); HEL_CHECK(send_head.error()); HEL_CHECK(recv_resp.error()); managarm::posix::SvrResponse resp(getSysdepsAllocator()); resp.ParseFromArray(recv_resp.data(), recv_resp.length()); if (resp.error() != managarm::posix::Errors::SUCCESS) return resp.error() | toErrno; usage->ru_utime.tv_sec = resp.ru_user_time() / 1'000'000'000; usage->ru_utime.tv_usec = (resp.ru_user_time() % 1'000'000'000) / 1'000; return 0; } int sys_getschedparam(void *tcb, int *policy, struct sched_param *param) { if (tcb != mlibc::get_current_tcb()) { return ESRCH; } *policy = SCHED_OTHER; int prio = 0; // TODO(no92): use helGetPriority(kHelThisThread) here mlibc::infoLogger() << "\e[31mlibc: sys_getschedparam always returns priority 0\e[39m" << frg::endlog; param->sched_priority = prio; return 0; } int sys_setschedparam(void *tcb, int policy, const struct sched_param *param) { if (tcb != mlibc::get_current_tcb()) { return ESRCH; } if (policy != SCHED_OTHER) { return EINVAL; } HEL_CHECK(helSetPriority(kHelThisThread, param->sched_priority)); return 0; } int sys_clone(void *tcb, pid_t *tid_out, void *stack) { (void)tcb; HelWord posixErr = 0; HelWord tid = 0; posix::superCloneArgs args{ .flags = (CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_THREAD), }; HEL_CHECK(helSyscall3_2( kHelCallSuper + posix::superClone, reinterpret_cast(__mlibc_start_thread), reinterpret_cast(stack), reinterpret_cast(&args), &posixErr, &tid )); if (posixErr) return managarm::posix::Errors(posixErr) | toErrno; if (tid_out) *tid_out = tid; return 0; } int sys_tcb_set(void *pointer) { #if defined(__x86_64__) HEL_CHECK(helWriteFsBase(pointer)); #elif defined(__aarch64__) uintptr_t addr = reinterpret_cast(pointer); addr += sizeof(Tcb) - 0x10; asm volatile("msr tpidr_el0, %0" ::"r"(addr)); #elif defined(__riscv) && __riscv_xlen == 64 uintptr_t tp = reinterpret_cast(pointer) + sizeof(Tcb); asm volatile("mv tp, %0" : : "r"(tp) : "memory"); #else #error Unknown architecture #endif return 0; } void sys_thread_exit() { // This implementation is inherently signal-safe. HEL_CHECK(helSyscall1(kHelCallSuper + posix::superThreadExit, 0)); __builtin_trap(); } int sys_thread_setname(void *tcb, const char *name) { if (strlen(name) > 15) { return ERANGE; } auto t = reinterpret_cast(tcb); char *path; int cs = 0; if (asprintf(&path, "/proc/self/task/%d/comm", t->tid) < 0) { return ENOMEM; } pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs); int fd; if (int e = sys_open(path, O_WRONLY, 0, &fd); e) { return e; } if (int e = sys_write(fd, name, strlen(name) + 1, nullptr)) { return e; } sys_close(fd); pthread_setcancelstate(cs, nullptr); return 0; } int sys_thread_getname(void *tcb, char *name, size_t size) { auto t = reinterpret_cast(tcb); char *path; int cs = 0; ssize_t real_size = 0; if (asprintf(&path, "/proc/self/task/%d/comm", t->tid) < 0) { return ENOMEM; } pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs); int fd; if (int e = sys_open(path, O_RDONLY | O_CLOEXEC, 0, &fd); e) { return e; } if (int e = sys_read(fd, name, size, &real_size)) { return e; } name[real_size - 1] = 0; sys_close(fd); pthread_setcancelstate(cs, nullptr); if (static_cast(size) <= real_size) { return ERANGE; } return 0; } } // namespace mlibc