user: implement mlibc as the libc, finally.

It's finally done..

Signed-off-by: kaguya <vpshinomiya@protonmail.com>
This commit is contained in:
kaguya
2026-05-02 03:31:49 -04:00
parent 2fa39ad85a
commit 9a9b91c940
2387 changed files with 152741 additions and 315 deletions
@@ -0,0 +1,10 @@
.section .text
.global _start
_start:
mov x0, sp
adr x1, main
bl __mlibc_entry
brk #0
.section .note.GNU-stack,"",%progbits
@@ -0,0 +1,11 @@
.section .text
.global _start
_start:
mov x0, sp
adrp x1, main
add x1, x1, :lo12:main
bl __mlibc_entry
.section .note.GNU-stack,"",%progbits
@@ -0,0 +1,15 @@
.ident "aarch64-managarm-mlibc crti"
.section .init
.global _init
_init:
stp x29, x30, [sp, -16]!
mov x29, sp
.section .fini
.global _fini
_fini:
stp x29, x30, [sp, -16]!
mov x29, sp
.section .note.GNU-stack,"",%progbits
@@ -0,0 +1,11 @@
.ident "aarch64-managarm-mlibc crtn"
.section .init
ldp x29, x30, [sp], #16
ret
.section .fini
ldp x29, x30, [sp], #16
ret
.section .note.GNU-stack,"",%progbits
@@ -0,0 +1,10 @@
.section .text
.global __mlibc_signal_restore
__mlibc_signal_restore:
ldr x0, =0x80000006
svc 0
brk #1
.section .note.GNU-stack,"",%progbits
@@ -0,0 +1,65 @@
#include <bits/ensure.h>
#include <errno.h>
#include <mlibc/all-sysdeps.hpp>
#include <mlibc/tcb.hpp>
#include <mlibc/thread-entry.hpp>
#include <stddef.h>
#include <stdint.h>
#include <sys/mman.h>
extern "C" void __mlibc_enter_thread(void *entry, void *user_arg, Tcb *tcb) {
// Wait until our parent sets up the TID.
while (!__atomic_load_n(&tcb->tid, __ATOMIC_RELAXED))
mlibc::sys_futex_wait(&tcb->tid, 0, nullptr);
if (mlibc::sys_tcb_set(tcb))
__ensure(!"sys_tcb_set() failed");
tcb->invokeThreadFunc(entry, user_arg);
auto self = reinterpret_cast<Tcb *>(tcb);
__atomic_store_n(&self->didExit, 1, __ATOMIC_RELEASE);
mlibc::sys_futex_wake(&self->didExit);
mlibc::sys_thread_exit();
}
namespace mlibc {
static constexpr size_t default_stacksize = 0x200000;
int sys_prepare_stack(
void **stack,
void *entry,
void *user_arg,
void *tcb,
size_t *stack_size,
size_t *guard_size,
void **stack_base
) {
if (!*stack_size)
*stack_size = default_stacksize;
*guard_size = 0;
if (*stack) {
*stack_base = *stack;
} else {
*stack_base =
mmap(nullptr, *stack_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (*stack_base == MAP_FAILED) {
return errno;
}
}
uintptr_t *sp =
reinterpret_cast<uintptr_t *>(reinterpret_cast<uintptr_t>(*stack_base) + *stack_size);
*--sp = reinterpret_cast<uintptr_t>(tcb);
*--sp = reinterpret_cast<uintptr_t>(user_arg);
*--sp = reinterpret_cast<uintptr_t>(entry);
*stack = reinterpret_cast<void *>(sp);
return 0;
}
} // namespace mlibc
@@ -0,0 +1,11 @@
.section .text
.global __mlibc_start_thread
__mlibc_start_thread:
ldp x0, x1, [sp]
ldr x2, [sp, #16]
add sp, sp, #24
bl __mlibc_enter_thread
.section .note.GNU-stack,"",%progbits
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,37 @@
#include <assert.h>
#include <stdint.h>
#include <string.h>
#include <bits/ensure.h>
#include <mlibc/all-sysdeps.hpp>
#include <mlibc/debug.hpp>
#include <hel-syscalls.h>
#include <hel.h>
void __frigg_assert_fail(
const char *assertion, const char *file, unsigned int line, const char *function
) {
mlibc::panicLogger() << "In function " << function << ", file " << file << ":" << line << "\n"
<< "__ensure(" << assertion << ") failed" << frg::endlog;
}
namespace mlibc {
void sys_libc_log(const char *message) {
// This implementation is inherently signal-safe.
size_t n = 0;
while (message[n])
n++;
HEL_CHECK(helLog(kHelLogSeverityInfo, message, n));
}
void sys_libc_panic() {
// This implementation is inherently signal-safe.
const char *message = "mlibc: Panic!";
size_t n = 0;
while (message[n])
n++;
helPanic(message, n);
}
} // namespace mlibc
@@ -0,0 +1,135 @@
#include <pthread.h>
#include <stdlib.h>
#include <sys/auxv.h>
#include <frg/eternal.hpp>
#include <bits/ensure.h>
#include <mlibc/all-sysdeps.hpp>
#include <mlibc/allocator.hpp>
#include <mlibc/debug.hpp>
#include <mlibc/elf/startup.h>
#include <mlibc/posix-pipe.hpp>
#include <protocols/posix/data.hpp>
#include <protocols/posix/supercalls.hpp>
extern "C" uintptr_t *__dlapi_entrystack();
extern "C" void __dlapi_enter(uintptr_t *);
// declared in posix-pipe.hpp
thread_local Queue globalQueue;
// TODO: clock tracker page and file table don't need to be thread-local!
thread_local HelHandle __mlibc_posix_lane;
thread_local void *__mlibc_clk_tracker_page;
namespace {
thread_local unsigned __mlibc_gsf_nesting;
thread_local posix::ThreadPage *__mlibc_cached_thread_page;
thread_local HelHandle *cachedFileTable;
// This construction is a bit weird: Even though the variables above
// are thread_local we still protect their initialization with a pthread_once_t
// (instead of using a C++ constructor).
// We do this in order to able to clear the pthread_once_t after a fork.
thread_local pthread_once_t has_cached_infos = PTHREAD_ONCE_INIT;
void actuallyCacheInfos() {
posix::ManagarmProcessData data;
HEL_CHECK(
helSyscall1(kHelCallSuper + posix::superGetProcessData, reinterpret_cast<HelWord>(&data))
);
__mlibc_posix_lane = data.posixLane;
__mlibc_cached_thread_page = data.threadPage;
cachedFileTable = data.fileTable;
__mlibc_clk_tracker_page = data.clockTrackerPage;
}
} // namespace
SignalGuard::SignalGuard() {
pthread_once(&has_cached_infos, &actuallyCacheInfos);
if (!__mlibc_cached_thread_page)
return;
if (!__mlibc_gsf_nesting)
__atomic_store_n(&__mlibc_cached_thread_page->globalSignalFlag, 1, __ATOMIC_RELAXED);
__mlibc_gsf_nesting++;
}
SignalGuard::~SignalGuard() {
pthread_once(&has_cached_infos, &actuallyCacheInfos);
if (!__mlibc_cached_thread_page)
return;
__ensure(__mlibc_gsf_nesting > 0);
__mlibc_gsf_nesting--;
if (!__mlibc_gsf_nesting) {
unsigned int result =
__atomic_exchange_n(&__mlibc_cached_thread_page->globalSignalFlag, 0, __ATOMIC_RELAXED);
if (result == 2) {
HEL_CHECK(helSyscall0(kHelCallSuper + posix::superSigRaise));
} else {
__ensure(result == 1);
}
}
}
MemoryAllocator &getSysdepsAllocator() {
// use frg::eternal to prevent a call to __cxa_atexit().
// this is necessary because __cxa_atexit() call this function.
static frg::eternal<VirtualAllocator> virtualAllocator;
static frg::eternal<MemoryPool> heap{virtualAllocator.get()};
static frg::eternal<MemoryAllocator> singleton{&heap.get()};
return singleton.get();
}
HelHandle getPosixLane() {
cacheFileTable();
return __mlibc_posix_lane;
}
HelHandle *cacheFileTable() {
// TODO: Make sure that this is signal-safe (it is called e.g. by sys_clock_get()).
pthread_once(&has_cached_infos, &actuallyCacheInfos);
return cachedFileTable;
}
HelHandle getHandleForFd(int fd) {
if (fd >= 512)
return 0;
return cacheFileTable()[fd];
}
void clearCachedInfos() { has_cached_infos = PTHREAD_ONCE_INIT; }
void resetCancellationId() {
pthread_once(&has_cached_infos, &actuallyCacheInfos);
__atomic_store_n(&__mlibc_cached_thread_page->cancellationId, 0, __ATOMIC_RELEASE);
}
void setCancellationId(uint64_t id, HelHandle handle, int fd) {
pthread_once(&has_cached_infos, &actuallyCacheInfos);
__mlibc_cached_thread_page->lane = handle;
__mlibc_cached_thread_page->fd = fd;
__atomic_store_n(&__mlibc_cached_thread_page->cancellationId, id, __ATOMIC_RELEASE);
}
namespace {
thread_local uint64_t cancellationId = 1;
} // namespace
uint64_t allocateCancellationId() { return cancellationId++; }
extern char **environ;
extern "C" void
__mlibc_entry(uintptr_t *entry_stack, int (*main_fn)(int argc, char *argv[], char *env[])) {
__dlapi_enter(entry_stack);
auto result = main_fn(mlibc::entry_stack.argc, mlibc::entry_stack.argv, environ);
exit(result);
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,791 @@
// for _Exit()
#include <bits/errors.hpp>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
// for fork() and execve()
#include <unistd.h>
// for sched_yield()
#include <sched.h>
#include <stdio.h>
// for getrusage()
#include <sys/resource.h>
// for waitpid()
#include <pthread.h>
#include <sys/wait.h>
#include <bits/ensure.h>
#include <mlibc/all-sysdeps.hpp>
#include <mlibc/allocator.hpp>
#include <mlibc/debug.hpp>
#include <mlibc/posix-pipe.hpp>
#include <mlibc/thread-entry.hpp>
#include <posix.frigg_bragi.hpp>
#include <protocols/posix/supercalls.hpp>
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<MemoryAllocator> 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<MemoryAllocator> 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<MemoryAllocator> 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<MemoryAllocator> 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<MemoryAllocator> args_area(getSysdepsAllocator());
for (auto it = argv; *it; ++it)
args_area += frg::string_view{*it, strlen(*it) + 1};
frg::string<MemoryAllocator> 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<uintptr_t>(path),
strlen(path),
reinterpret_cast<uintptr_t>(args_area.data()),
args_area.size(),
reinterpret_cast<uintptr_t>(env_area.data()),
env_area.size(),
&out
));
return out;
}
gid_t sys_getgid() {
SignalGuard sguard;
managarm::posix::GetGidRequest<MemoryAllocator> 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<MemoryAllocator> 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<MemoryAllocator> 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<MemoryAllocator> 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<MemoryAllocator> 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<MemoryAllocator> 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<MemoryAllocator> 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<MemoryAllocator> 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<MemoryAllocator> 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<MemoryAllocator> 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<MemoryAllocator> 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<MemoryAllocator> 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<MemoryAllocator> 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<MemoryAllocator> 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<MemoryAllocator> 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<MemoryAllocator> 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<MemoryAllocator> 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<MemoryAllocator> 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<MemoryAllocator> 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<MemoryAllocator> 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<MemoryAllocator> 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<MemoryAllocator> 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<MemoryAllocator> 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<MemoryAllocator> 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<MemoryAllocator> 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<MemoryAllocator> 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<MemoryAllocator> 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<MemoryAllocator> 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<HelWord>(__mlibc_start_thread),
reinterpret_cast<HelWord>(stack),
reinterpret_cast<HelWord>(&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<uintptr_t>(pointer);
addr += sizeof(Tcb) - 0x10;
asm volatile("msr tpidr_el0, %0" ::"r"(addr));
#elif defined(__riscv) && __riscv_xlen == 64
uintptr_t tp = reinterpret_cast<uintptr_t>(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 *>(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 *>(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<ssize_t>(size) <= real_size) {
return ERANGE;
}
return 0;
}
} // namespace mlibc
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,29 @@
#include <string.h>
#include <bits/ensure.h>
#include <mlibc/all-sysdeps.hpp>
#include <mlibc/allocator.hpp>
#include <protocols/posix/supercalls.hpp>
#include <hel-syscalls.h>
#include <hel.h>
namespace mlibc {
int sys_anon_allocate(size_t size, void **pointer) {
// This implementation is inherently signal-safe.
__ensure(!(size & 0xFFF));
HelWord out;
HEL_CHECK(helSyscall1_1(kHelCallSuper + posix::superAnonAllocate, size, &out));
*pointer = reinterpret_cast<void *>(out);
return 0;
}
int sys_anon_free(void *pointer, size_t size) {
// This implementation is inherently signal-safe.
HEL_CHECK(helSyscall2(kHelCallSuper + posix::superAnonDeallocate, (HelWord)pointer, size));
return 0;
}
} // namespace mlibc
@@ -0,0 +1,44 @@
#include <errno.h>
#include <string.h>
#include <sys/mount.h>
#include <bits/ensure.h>
#include <bits/errors.hpp>
#include <bragi/helpers-frigg.hpp>
#include <mlibc/all-sysdeps.hpp>
#include <mlibc/allocator.hpp>
#include <mlibc/posix-pipe.hpp>
#include <posix.frigg_bragi.hpp>
namespace mlibc {
int
sys_mount(const char *source, const char *target, const char *fstype, unsigned long, const void *) {
SignalGuard sguard;
managarm::posix::MountRequest<MemoryAllocator> req(getSysdepsAllocator());
req.set_path(frg::string<MemoryAllocator>(getSysdepsAllocator(), source ? source : ""));
req.set_target_path(frg::string<MemoryAllocator>(getSysdepsAllocator(), target ? target : ""));
req.set_fs_type(frg::string<MemoryAllocator>(getSysdepsAllocator(), fstype ? fstype : ""));
auto [offer, send_head, send_tail, recv_resp] = exchangeMsgsSync(
getPosixLane(),
helix_ng::offer(
helix_ng::sendBragiHeadTail(req, getSysdepsAllocator()), helix_ng::recvInline()
)
);
HEL_CHECK(offer.error());
HEL_CHECK(send_head.error());
HEL_CHECK(send_tail.error());
HEL_CHECK(recv_resp.error());
auto resp =
*bragi::parse_head_only<managarm::posix::SvrResponse>(recv_resp, getSysdepsAllocator());
if (resp.error() != managarm::posix::Errors::SUCCESS)
return resp.error() | toErrno;
return 0;
}
} // namespace mlibc
@@ -0,0 +1,115 @@
#include <errno.h>
#include <mlibc/all-sysdeps.hpp>
#include <sys/ioctl.h>
#include <unistd.h>
#include "generic-helpers/netlink.hpp"
namespace mlibc {
int sys_if_indextoname(unsigned int index, char *name) {
int fd = 0;
int r = sys_socket(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, AF_UNSPEC, &fd);
if (r)
return r;
struct ifreq ifr;
ifr.ifr_ifindex = index;
int res = 0;
int ret = sys_ioctl(fd, SIOCGIFNAME, &ifr, &res);
close(fd);
if (ret) {
if (ret == ENODEV)
return ENXIO;
return ret;
}
strncpy(name, ifr.ifr_name, IF_NAMESIZE);
return 0;
}
int sys_if_nametoindex(const char *name, unsigned int *ret) {
int fd = 0;
int r = sys_socket(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, AF_UNSPEC, &fd);
if (r)
return r;
struct ifreq ifr;
strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name);
int res = 0;
r = sys_ioctl(fd, SIOCGIFINDEX, &ifr, &res);
close(fd);
if (r)
return r;
*ret = ifr.ifr_ifindex;
return 0;
}
int sys_getifaddrs(struct ifaddrs **out) {
NetlinkHelper nl;
*out = nullptr;
bool link_ret = nl.send_request(RTM_GETLINK) && nl.recv(&getifaddrs_callback, out);
__ensure(link_ret);
bool addr_ret = nl.send_request(RTM_GETADDR) && nl.recv(&getifaddrs_callback, out);
__ensure(addr_ret);
return 0;
}
#if !defined(MLIBC_BUILDING_RTLD)
int sys_inet_configured(bool *ipv4, bool *ipv6) {
struct context {
bool *ipv4;
bool *ipv6;
} context = {.ipv4 = ipv4, .ipv6 = ipv6};
NetlinkHelper nl;
if (!nl.send_request(RTM_GETADDR)) {
*ipv4 = false;
*ipv6 = false;
return 0;
}
auto ret = nl.recv(
[](void *data, const nlmsghdr *hdr) {
if (hdr->nlmsg_type == RTM_NEWADDR || hdr->nlmsg_len >= sizeof(struct ifaddrmsg)) {
const struct ifaddrmsg *ifaddr =
reinterpret_cast<const struct ifaddrmsg *>(NLMSG_DATA(hdr));
struct context *ctx = reinterpret_cast<struct context *>(data);
char name[IF_NAMESIZE];
auto interfaceNameResult = sys_if_indextoname(ifaddr->ifa_index, name);
if (interfaceNameResult || !strncmp(name, "lo", IF_NAMESIZE))
return;
if (ifaddr->ifa_family == AF_INET)
*ctx->ipv4 = true;
else if (ifaddr->ifa_family == AF_INET6)
*ctx->ipv6 = true;
}
},
&context
);
if (!ret) {
*ipv4 = false;
*ipv6 = false;
return 0;
}
return 0;
}
#endif // !defined(MLIBC_BUILDING_RTLD)
} // namespace mlibc
@@ -0,0 +1,106 @@
#include <bits/ensure.h>
#include <unistd.h>
#include <hel-syscalls.h>
#include <hel.h>
#include <mlibc/allocator.hpp>
#include <mlibc/debug.hpp>
#include <mlibc/posix-pipe.hpp>
#include <mlibc/posix-sysdeps.hpp>
#include <posix.frigg_bragi.hpp>
namespace mlibc {
int sys_getscheduler(pid_t, int *policy) {
*policy = SCHED_OTHER;
return 0;
}
int sys_getaffinity(pid_t pid, size_t cpusetsize, cpu_set_t *mask) {
return sys_getthreadaffinity(pid, cpusetsize, mask);
}
int sys_getthreadaffinity(pid_t tid, size_t cpusetsize, cpu_set_t *mask) {
SignalGuard sguard;
managarm::posix::GetAffinityRequest<MemoryAllocator> req(getSysdepsAllocator());
req.set_pid(tid);
req.set_size(cpusetsize);
auto [offer, send_head, recv_resp, recv_data] = exchangeMsgsSync(
getPosixLane(),
helix_ng::offer(
helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
helix_ng::recvInline(),
helix_ng::recvBuffer(mask, cpusetsize)
)
);
HEL_CHECK(offer.error());
HEL_CHECK(send_head.error());
HEL_CHECK(recv_resp.error());
managarm::posix::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
resp.ParseFromArray(recv_resp.data(), recv_resp.length());
if (resp.error() == managarm::posix::Errors::ILLEGAL_ARGUMENTS) {
return EINVAL;
} else if (resp.error() != managarm::posix::Errors::SUCCESS) {
mlibc::infoLogger() << "mlibc: got unexpected error from posix in sys_getaffinity!"
<< frg::endlog;
return EIEIO;
}
HEL_CHECK(recv_data.error());
return 0;
}
int sys_setaffinity(pid_t pid, size_t cpusetsize, const cpu_set_t *mask) {
return sys_setthreadaffinity(pid, cpusetsize, mask);
}
int sys_setthreadaffinity(pid_t tid, size_t cpusetsize, const cpu_set_t *mask) {
SignalGuard sguard;
frg::vector<uint8_t, MemoryAllocator> affinity_mask(getSysdepsAllocator());
affinity_mask.resize(cpusetsize);
memcpy(affinity_mask.data(), mask, cpusetsize);
managarm::posix::SetAffinityRequest<MemoryAllocator> req(getSysdepsAllocator());
req.set_pid(tid);
req.set_mask(affinity_mask);
auto [offer, send_head, send_tail, recv_resp] = exchangeMsgsSync(
getPosixLane(),
helix_ng::offer(
helix_ng::sendBragiHeadTail(req, getSysdepsAllocator()), helix_ng::recvInline()
)
);
HEL_CHECK(offer.error());
HEL_CHECK(send_head.error());
HEL_CHECK(send_tail.error());
HEL_CHECK(recv_resp.error());
managarm::posix::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
resp.ParseFromArray(recv_resp.data(), recv_resp.length());
if (resp.error() == managarm::posix::Errors::ILLEGAL_ARGUMENTS) {
return EINVAL;
} else if (resp.error() != managarm::posix::Errors::SUCCESS) {
mlibc::infoLogger() << "mlibc: got unexpected error from posix in sys_getaffinity!"
<< frg::endlog;
return EIEIO;
}
return 0;
}
int sys_getcpu(int *cpu) {
HEL_CHECK(helGetCurrentCpu(cpu));
return 0;
}
} // namespace mlibc
@@ -0,0 +1,195 @@
#include <bits/ensure.h>
#include <errno.h>
#include <signal.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <hel-syscalls.h>
#include <hel.h>
#include <mlibc/all-sysdeps.hpp>
#include <mlibc/allocator.hpp>
#include <mlibc/debug.hpp>
#include <mlibc/posix-pipe.hpp>
#include <posix.frigg_bragi.hpp>
#include <bragi/helpers-frigg.hpp>
#include <helix/ipc-structs.hpp>
#include <protocols/posix/supercalls.hpp>
extern "C" void __mlibc_signal_restore();
namespace mlibc {
int sys_sigprocmask(int how, const sigset_t *set, sigset_t *retrieve) {
// This implementation is inherently signal-safe.
uint64_t former, unused;
if (set) {
HEL_CHECK(helSyscall2_2(
kHelObserveSuperCall + posix::superSigMask,
how,
*reinterpret_cast<const HelWord *>(set),
&former,
&unused
));
} else {
HEL_CHECK(helSyscall2_2(kHelObserveSuperCall + posix::superSigMask, 0, 0, &former, &unused)
);
}
if (retrieve)
*reinterpret_cast<uint64_t *>(retrieve) = former;
return 0;
}
int sys_sigaction(
int number, const struct sigaction *__restrict action, struct sigaction *__restrict saved_action
) {
SignalGuard sguard;
// TODO: Respect restorer. __ensure(!(action->sa_flags & SA_RESTORER));
managarm::posix::CntRequest<MemoryAllocator> req(getSysdepsAllocator());
req.set_request_type(managarm::posix::CntReqType::SIG_ACTION);
req.set_sig_number(number);
if (action) {
req.set_mode(1);
req.set_flags(action->sa_flags);
req.set_sig_mask(*reinterpret_cast<const uint64_t *>(&action->sa_mask));
if (action->sa_flags & SA_SIGINFO) {
req.set_sig_handler(reinterpret_cast<uintptr_t>(action->sa_sigaction));
} else {
req.set_sig_handler(reinterpret_cast<uintptr_t>(action->sa_handler));
}
req.set_sig_restorer(reinterpret_cast<uintptr_t>(&__mlibc_signal_restore));
} else {
req.set_mode(0);
}
auto [offer, send_req, recv_resp] = exchangeMsgsSync(
getPosixLane(),
helix_ng::offer(
helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()), helix_ng::recvInline()
)
);
HEL_CHECK(offer.error());
HEL_CHECK(send_req.error());
HEL_CHECK(recv_resp.error());
managarm::posix::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
resp.ParseFromArray(recv_resp.data(), recv_resp.length());
if (resp.error() == managarm::posix::Errors::ILLEGAL_REQUEST) {
// This is only returned for servers, not for normal userspace.
return ENOSYS;
} else if (resp.error() == managarm::posix::Errors::ILLEGAL_ARGUMENTS) {
return EINVAL;
}
__ensure(resp.error() == managarm::posix::Errors::SUCCESS);
if (saved_action) {
saved_action->sa_flags = resp.flags();
*reinterpret_cast<uint64_t *>(&saved_action->sa_mask) = resp.sig_mask();
if (resp.flags() & SA_SIGINFO) {
saved_action->sa_sigaction =
reinterpret_cast<void (*)(int, siginfo_t *, void *)>(resp.sig_handler());
} else {
saved_action->sa_handler = reinterpret_cast<void (*)(int)>(resp.sig_handler());
}
// TODO: saved_action->sa_restorer = resp.sig_restorer;
}
return 0;
}
int sys_kill(int pid, int number) {
// This implementation is inherently signal-safe.
HelWord out;
HEL_CHECK(helSyscall2_1(kHelObserveSuperCall + posix::superSigKill, pid, number, &out));
return out;
}
int sys_tgkill(int, int tid, int number) { return sys_kill(tid, number); }
int sys_sigaltstack(const stack_t *ss, stack_t *oss) {
HelWord out;
// This implementation is inherently signal-safe.
HEL_CHECK(helSyscall2_1(
kHelObserveSuperCall + posix::superSigAltStack,
reinterpret_cast<HelWord>(ss),
reinterpret_cast<HelWord>(oss),
&out
));
return out;
}
int sys_sigsuspend(const sigset_t *set) {
// SignalGuard sguard;
uint64_t former, seq, unused;
HEL_CHECK(helSyscall2_2(
kHelObserveSuperCall + posix::superSigMask,
SIG_SETMASK,
*reinterpret_cast<const HelWord *>(set),
&former,
&seq
));
HEL_CHECK(helSyscall1(kHelObserveSuperCall + posix::superSigSuspend, seq));
HEL_CHECK(helSyscall2_2(
kHelObserveSuperCall + posix::superSigMask, SIG_SETMASK, former, &unused, &unused
));
return EINTR;
}
int sys_sigpending(sigset_t *set) {
uint64_t pendingMask;
HEL_CHECK(helSyscall0_1(kHelObserveSuperCall + posix::superSigGetPending, &pendingMask));
*reinterpret_cast<uint64_t *>(set) = pendingMask;
return 0;
}
int sys_pause() {
HelWord set = 0;
uint64_t former, seq;
// no-op to obtain a seqnum
HEL_CHECK(
helSyscall2_2(kHelObserveSuperCall + posix::superSigMask, SIG_BLOCK, set, &former, &seq)
);
HEL_CHECK(helSyscall1(kHelObserveSuperCall + posix::superSigSuspend, seq));
return EINTR;
}
int sys_sigtimedwait(
const sigset_t *__restrict set,
siginfo_t *__restrict info,
const struct timespec *__restrict timeout,
int *out_signal
) {
uint64_t nanos = timeout ? (timeout->tv_nsec + timeout->tv_sec * 1'000'000'000) : UINT64_MAX;
HelWord status;
HelWord signal;
HEL_CHECK(helSyscall3_2(
kHelObserveSuperCall + posix::superSigTimedWait,
*reinterpret_cast<const HelWord *>(set),
nanos,
reinterpret_cast<HelWord>(info),
&status,
&signal
));
if (status)
return status;
*out_signal = signal;
return 0;
}
} // namespace mlibc
@@ -0,0 +1,625 @@
#include <array>
#include <asm/socket.h>
#include <bits/ensure.h>
#include <errno.h>
#include <linux/filter.h>
#include <linux/if_packet.h>
#include <linux/netlink.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <sys/socket.h>
#include <bits/errors.hpp>
#include <fs.frigg_bragi.hpp>
#include <mlibc/all-sysdeps.hpp>
#include <mlibc/allocator.hpp>
#include <mlibc/debug.hpp>
#include <mlibc/posix-pipe.hpp>
#include <posix.frigg_bragi.hpp>
namespace {
int fcntl_helper(int fd, int request, int *result, ...) {
va_list args;
va_start(args, result);
if (!mlibc::sys_fcntl) {
return ENOSYS;
}
int ret = mlibc::sys_fcntl(fd, request, args, result);
va_end(args);
return ret;
}
} // namespace
namespace mlibc {
int sys_accept(int fd, int *newfd, struct sockaddr *addr_ptr, socklen_t *addr_length, int flags) {
SignalGuard sguard;
managarm::posix::AcceptRequest<MemoryAllocator> req(getSysdepsAllocator());
req.set_fd(fd);
auto [offer, sendReq, recvResp] = exchangeMsgsSync(
getPosixLane(),
helix_ng::offer(
helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()), helix_ng::recvInline()
)
);
HEL_CHECK(offer.error());
HEL_CHECK(sendReq.error());
HEL_CHECK(recvResp.error());
managarm::posix::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
resp.ParseFromArray(recvResp.data(), recvResp.length());
if (resp.error() != managarm::posix::Errors::SUCCESS) {
return resp.error() | toErrno;
} else {
*newfd = resp.fd();
}
if (addr_ptr && addr_length) {
if (int e = mlibc::sys_peername(*newfd, addr_ptr, *addr_length, addr_length); e) {
errno = e;
return -1;
}
}
if (flags & SOCK_NONBLOCK) {
int fcntl_ret = 0;
fcntl_helper(*newfd, F_GETFL, &fcntl_ret);
fcntl_helper(*newfd, F_SETFL, &fcntl_ret, fcntl_ret | O_NONBLOCK);
}
if (flags & SOCK_CLOEXEC) {
int fcntl_ret = 0;
fcntl_helper(*newfd, F_GETFD, &fcntl_ret);
fcntl_helper(*newfd, F_SETFD, &fcntl_ret, fcntl_ret | FD_CLOEXEC);
}
return 0;
}
int sys_bind(int fd, const struct sockaddr *addr_ptr, socklen_t addr_length) {
SignalGuard sguard;
auto handle = getHandleForFd(fd);
if (!handle)
return EBADF;
managarm::fs::CntRequest<MemoryAllocator> req(getSysdepsAllocator());
req.set_req_type(managarm::fs::CntReqType::PT_BIND);
auto [offer, send_req, send_creds, send_buf, recv_resp] = exchangeMsgsSync(
handle,
helix_ng::offer(
helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
helix_ng::imbueCredentials(),
helix_ng::sendBuffer(addr_ptr, addr_length),
helix_ng::recvInline()
)
);
HEL_CHECK(offer.error());
HEL_CHECK(send_req.error());
HEL_CHECK(send_creds.error());
HEL_CHECK(send_buf.error());
HEL_CHECK(recv_resp.error());
managarm::fs::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
resp.ParseFromArray(recv_resp.data(), recv_resp.length());
return resp.error() | toErrno;
}
int sys_connect(int fd, const struct sockaddr *addr_ptr, socklen_t addr_length) {
SignalGuard sguard;
auto handle = getHandleForFd(fd);
if (!handle)
return EBADF;
managarm::fs::CntRequest<MemoryAllocator> req(getSysdepsAllocator());
req.set_req_type(managarm::fs::CntReqType::PT_CONNECT);
frg::string<MemoryAllocator> ser(getSysdepsAllocator());
req.SerializeToString(&ser);
auto [offer, send_req, imbue_creds, send_addr, recv_resp] = exchangeMsgsSync(
handle,
helix_ng::offer(
helix_ng::sendBuffer(ser.data(), ser.size()),
helix_ng::imbueCredentials(),
helix_ng::sendBuffer(const_cast<struct sockaddr *>(addr_ptr), addr_length),
helix_ng::recvInline()
)
);
HEL_CHECK(offer.error());
HEL_CHECK(send_req.error());
HEL_CHECK(imbue_creds.error());
HEL_CHECK(send_addr.error());
HEL_CHECK(recv_resp.error());
managarm::fs::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
resp.ParseFromArray(recv_resp.data(), recv_resp.length());
return resp.error() | toErrno;
}
int sys_sockname(
int fd, struct sockaddr *addr_ptr, socklen_t max_addr_length, socklen_t *actual_length
) {
SignalGuard sguard;
auto handle = getHandleForFd(fd);
if (!handle)
return EBADF;
managarm::fs::CntRequest<MemoryAllocator> req(getSysdepsAllocator());
req.set_req_type(managarm::fs::CntReqType::PT_SOCKNAME);
req.set_fd(fd);
req.set_size(max_addr_length);
auto [offer, send_req, recv_resp, recv_addr] = exchangeMsgsSync(
handle,
helix_ng::offer(
helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
helix_ng::recvInline(),
helix_ng::recvBuffer(addr_ptr, max_addr_length)
)
);
HEL_CHECK(offer.error());
HEL_CHECK(send_req.error());
HEL_CHECK(recv_resp.error());
managarm::fs::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
resp.ParseFromArray(recv_resp.data(), recv_resp.length());
if (resp.error() == managarm::fs::Errors::SUCCESS) {
HEL_CHECK(recv_addr.error());
*actual_length = resp.file_size();
return 0;
}
return resp.error() | toErrno;
}
int sys_peername(
int fd, struct sockaddr *addr_ptr, socklen_t max_addr_length, socklen_t *actual_length
) {
SignalGuard sguard;
auto handle = getHandleForFd(fd);
if (!handle)
return EBADF;
managarm::fs::CntRequest<MemoryAllocator> req(getSysdepsAllocator());
req.set_req_type(managarm::fs::CntReqType::PT_PEERNAME);
req.set_fd(fd);
req.set_size(max_addr_length);
frg::string<MemoryAllocator> ser(getSysdepsAllocator());
req.SerializeToString(&ser);
auto [offer, sendReq, recvResp, recvData] = exchangeMsgsSync(
handle,
helix_ng::offer(
helix_ng::sendBuffer(ser.data(), ser.size()),
helix_ng::recvInline(),
helix_ng::recvBuffer(addr_ptr, max_addr_length)
)
);
HEL_CHECK(offer.error());
HEL_CHECK(sendReq.error());
if (recvResp.error() == kHelErrDismissed)
return ENOTSOCK;
HEL_CHECK(recvResp.error());
managarm::fs::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
resp.ParseFromArray(recvResp.data(), recvResp.length());
if (resp.error() == managarm::fs::Errors::SUCCESS) {
HEL_CHECK(recvData.error());
*actual_length = resp.file_size();
return 0;
}
return resp.error() | toErrno;
}
namespace {
std::array<std::pair<int, int>, 6> getsockopt_passthrough = {{
{SOL_SOCKET, SO_PROTOCOL},
{SOL_SOCKET, SO_PEERCRED},
{SOL_NETLINK, NETLINK_LIST_MEMBERSHIPS},
{SOL_SOCKET, SO_TYPE},
{SOL_SOCKET, SO_ACCEPTCONN},
{SOL_SOCKET, SO_PEERPIDFD},
}};
} // namespace
int
sys_getsockopt(int fd, int layer, int number, void *__restrict buffer, socklen_t *__restrict size) {
SignalGuard sguard;
if (layer == SOL_SOCKET && number == SO_SNDBUF) {
// This is really only relevant on Linux
*(int *)buffer = 4096;
return 0;
} else if (layer == SOL_SOCKET && number == SO_RCVBUF) {
// This is really only relevant on Linux
*(int *)buffer = 4096;
return 0;
} else if (layer == SOL_SOCKET && number == SO_ERROR) {
mlibc::infoLogger() << "\e[31mmlibc: getsockopt() call with SOL_SOCKET and SO_ERROR is "
"unimplemented, hardcoding 0\e[39m"
<< frg::endlog;
*(int *)buffer = 0;
return 0;
} else if (layer == SOL_SOCKET && number == SO_KEEPALIVE) {
mlibc::infoLogger() << "\e[31mmlibc: getsockopt() call with SOL_SOCKET and SO_KEEPALIVE is "
"unimplemented, hardcoding 0\e[39m"
<< frg::endlog;
*(int *)buffer = 0;
return 0;
} else if (layer == SOL_SOCKET && number == SO_LINGER) {
mlibc::infoLogger() << "\e[31mmlibc: getsockopt() call with SOL_SOCKET and SO_LINGER is "
"unimplemented, hardcoding 0\e[39m"
<< frg::endlog;
*(int *)buffer = 0;
return 0;
} else if (layer == SOL_SOCKET && number == SO_PEERSEC) {
mlibc::infoLogger() << "\e[31mmlibc: getsockopt() call with SOL_SOCKET and SO_PEERSEC is "
"unimplemented, hardcoding 0\e[39m"
<< frg::endlog;
*(int *)buffer = 0;
return 0;
} else if (layer == SOL_SOCKET && number == SO_PEERGROUPS) {
mlibc::infoLogger() << "\e[31mmlibc: getsockopt() call with SOL_SOCKET and SO_PEERGROUPS "
"is unimplemented, hardcoding 0\e[39m"
<< frg::endlog;
*(int *)buffer = 0;
return 0;
} else if (layer == IPPROTO_TCP && number == TCP_MAXSEG) {
mlibc::infoLogger() << "\e[31mmlibc: getsockopt() call with IPPROTO_TCP and TCP_MAXSEG is "
"unimplemented\e[39m"
<< frg::endlog;
return 0;
} else if (layer == IPPROTO_TCP && number == TCP_CONGESTION) {
mlibc::infoLogger(
) << "\e[31mmlibc: getsockopt() call with IPPROTO_TCP and TCP_CONGESTION is "
"unimplemented\e[39m"
<< frg::endlog;
return 0;
} else if (std::find(
getsockopt_passthrough.begin(),
getsockopt_passthrough.end(),
std::pair<int, int>{layer, number}
)
!= getsockopt_passthrough.end()) {
auto handle = getHandleForFd(fd);
if (!handle)
return EBADF;
managarm::fs::GetSockOpt<MemoryAllocator> req(getSysdepsAllocator());
req.set_layer(layer);
req.set_number(number);
req.set_optlen(size ? *size : 0);
auto [offer, send_req, send_creds, recv_resp] = exchangeMsgsSync(
handle,
helix_ng::offer(
helix_ng::want_lane,
helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
helix_ng::imbueCredentials(),
helix_ng::recvInline()
)
);
HEL_CHECK(offer.error());
HEL_CHECK(send_req.error());
HEL_CHECK(send_creds.error());
HEL_CHECK(recv_resp.error());
managarm::fs::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
resp.ParseFromArray(recv_resp.data(), recv_resp.length());
*size = resp.size();
auto [recv_buffer] =
exchangeMsgsSync(offer.descriptor().getHandle(), helix_ng::recvBuffer(buffer, *size));
HEL_CHECK(recv_buffer.error());
return resp.error() | toErrno;
} else {
mlibc::infoLogger() << "\e[31mmlibc: Unexpected getsockopt() call, layer: " << layer
<< " number: " << number << "\e[39m" << frg::endlog;
return EINVAL;
}
}
namespace {
std::array<std::pair<int, int>, 6> setsockopt_readonly = {{
{SOL_SOCKET, SO_ACCEPTCONN},
{SOL_SOCKET, SO_DOMAIN},
{SOL_SOCKET, SO_ERROR},
{SOL_SOCKET, SO_PROTOCOL},
{SOL_SOCKET, SO_TYPE},
{SOL_IP, SO_PEERSEC},
}};
std::array<std::pair<int, int>, 12> setsockopt_passthrough = {{
{SOL_PACKET, PACKET_AUXDATA},
{SOL_SOCKET, SO_LOCK_FILTER},
{SOL_SOCKET, SO_BINDTODEVICE},
{SOL_SOCKET, SO_TIMESTAMP},
{SOL_SOCKET, SO_PASSCRED},
{SOL_SOCKET, SO_RCVTIMEO},
{SOL_SOCKET, SO_SNDTIMEO},
{SOL_IP, IP_PKTINFO},
{SOL_IP, IP_RECVTTL},
{SOL_IP, IP_RETOPTS},
{SOL_NETLINK, NETLINK_ADD_MEMBERSHIP},
{SOL_NETLINK, NETLINK_PKTINFO},
}};
std::array<std::pair<int, int>, 2> setsockopt_passthrough_noopt = {{
{SOL_SOCKET, SO_DETACH_FILTER},
}};
} // namespace
int sys_setsockopt(int fd, int layer, int number, const void *buffer, socklen_t size) {
SignalGuard sguard;
if (std::find(
setsockopt_passthrough.begin(),
setsockopt_passthrough.end(),
std::pair<int, int>{layer, number}
)
!= setsockopt_passthrough.end()) {
auto handle = getHandleForFd(fd);
if (!handle)
return EBADF;
managarm::fs::SetSockOpt<MemoryAllocator> req(getSysdepsAllocator());
req.set_layer(layer);
req.set_number(number);
req.set_optlen(size);
auto [offer, send_req, send_buf, recv_resp] = exchangeMsgsSync(
handle,
helix_ng::offer(
helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
helix_ng::sendBuffer(buffer, size),
helix_ng::recvInline()
)
);
HEL_CHECK(offer.error());
HEL_CHECK(send_req.error());
HEL_CHECK(send_buf.error());
HEL_CHECK(recv_resp.error());
managarm::fs::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
resp.ParseFromArray(recv_resp.data(), recv_resp.length());
return resp.error() | toErrno;
} else if (std::find(
setsockopt_passthrough_noopt.begin(),
setsockopt_passthrough_noopt.end(),
std::pair<int, int>{layer, number}
)
!= setsockopt_passthrough_noopt.end()) {
auto handle = getHandleForFd(fd);
if (!handle)
return EBADF;
managarm::fs::SetSockOpt<MemoryAllocator> req(getSysdepsAllocator());
req.set_layer(layer);
req.set_number(number);
req.set_optlen(0);
auto [offer, send_req, recv_resp] = exchangeMsgsSync(
handle,
helix_ng::offer(
helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()), helix_ng::recvInline()
)
);
HEL_CHECK(offer.error());
HEL_CHECK(send_req.error());
HEL_CHECK(recv_resp.error());
managarm::fs::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
resp.ParseFromArray(recv_resp.data(), recv_resp.length());
return resp.error() | toErrno;
} else if (std::find(
setsockopt_readonly.begin(),
setsockopt_readonly.end(),
std::pair<int, int>{layer, number}
)
!= setsockopt_readonly.end()) {
// this is purely read-only
return ENOPROTOOPT;
} else if (layer == SOL_SOCKET && number == SO_ATTACH_FILTER) {
auto handle = getHandleForFd(fd);
if (!handle)
return EBADF;
if (size != sizeof(sock_fprog))
return EINVAL;
auto fprog = reinterpret_cast<const sock_fprog *>(buffer);
managarm::fs::SetSockOpt<MemoryAllocator> req(getSysdepsAllocator());
req.set_layer(layer);
req.set_number(number);
req.set_optlen(fprog->len * sizeof(*fprog->filter));
auto [offer, send_req, send_buf, recv_resp] = exchangeMsgsSync(
handle,
helix_ng::offer(
helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
helix_ng::sendBuffer(fprog->filter, req.optlen()),
helix_ng::recvInline()
)
);
HEL_CHECK(offer.error());
HEL_CHECK(send_req.error());
HEL_CHECK(send_buf.error());
HEL_CHECK(recv_resp.error());
managarm::fs::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
resp.ParseFromArray(recv_resp.data(), recv_resp.length());
return resp.error() | toErrno;
} else if (layer == SOL_SOCKET && number == SO_RCVBUFFORCE) {
mlibc::infoLogger() << "\e[31mmlibc: setsockopt(SO_RCVBUFFORCE) is not implemented"
" correctly\e[39m"
<< frg::endlog;
return 0;
} else if (layer == SOL_SOCKET && number == SO_SNDBUF) {
// This is really only relevant on Linux
return 0;
} else if (layer == SOL_SOCKET && number == SO_SNDBUFFORCE) {
// This is really only relevant on Linux
return 0;
} else if (layer == SOL_SOCKET && number == SO_KEEPALIVE) {
mlibc::infoLogger() << "\e[31mmlibc: setsockopt() call with SOL_SOCKET and SO_KEEPALIVE is "
"unimplemented\e[39m"
<< frg::endlog;
return 0;
} else if (layer == SOL_SOCKET && number == SO_REUSEADDR) {
mlibc::infoLogger() << "\e[31mmlibc: setsockopt() call with SOL_SOCKET and SO_REUSEADDR is "
"unimplemented\e[39m"
<< frg::endlog;
return 0;
} else if (layer == SOL_SOCKET && number == SO_REUSEPORT) {
mlibc::infoLogger() << "\e[31mmlibc: setsockopt() call with SOL_SOCKET and SO_REUSEPORT is "
"unimplemented\e[39m"
<< frg::endlog;
return 0;
} else if (layer == SOL_SOCKET && number == SO_RCVBUF) {
mlibc::infoLogger(
) << "\e[31mmlibc: setsockopt() call with SOL_SOCKET and SO_RCVBUF is unimplemented\e[39m"
<< frg::endlog;
return 0;
} else if (layer == IPPROTO_TCP && number == TCP_NODELAY) {
mlibc::infoLogger() << "\e[31mmlibc: setsockopt() call with IPPROTO_TCP and TCP_NODELAY is "
"unimplemented\e[39m"
<< frg::endlog;
return 0;
} else if (layer == IPPROTO_TCP && number == TCP_MAXSEG) {
mlibc::infoLogger() << "\e[31mmlibc: setsockopt() call with IPPROTO_TCP and TCP_NODELAY is "
"unimplemented\e[39m"
<< frg::endlog;
return 0;
} else if (layer == IPPROTO_TCP && number == TCP_KEEPIDLE) {
mlibc::infoLogger() << "\e[31mmlibc: setsockopt() call with IPPROTO_TCP and TCP_KEEPIDLE "
"is unimplemented\e[39m"
<< frg::endlog;
return 0;
} else if (layer == SOL_NETLINK && number == NETLINK_BROADCAST_ERROR) {
mlibc::infoLogger() << "\e[31mmlibc: setsockopt() call with SOL_NETLINK and "
"NETLINK_BROADCAST_ERROR is unimplemented\e[39m"
<< frg::endlog;
return 0;
} else if (layer == SOL_NETLINK && number == NETLINK_EXT_ACK) {
mlibc::infoLogger() << "\e[31mmlibc: setsockopt() call with SOL_NETLINK and "
"NETLINK_EXT_ACK is unimplemented\e[39m"
<< frg::endlog;
return 0;
} else if (layer == SOL_NETLINK && number == NETLINK_GET_STRICT_CHK) {
mlibc::infoLogger() << "\e[31mmlibc: setsockopt() call with SOL_NETLINK and "
"NETLINK_EXT_ACK is unimplemented\e[39m"
<< frg::endlog;
return 0;
} else if (layer == IPPROTO_TCP && number == TCP_KEEPINTVL) {
mlibc::infoLogger() << "\e[31mmlibc: setsockopt() call with IPPROTO_TCP and TCP_KEEPINTVL "
"is unimplemented\e[39m"
<< frg::endlog;
return 0;
} else if (layer == IPPROTO_TCP && number == TCP_KEEPCNT) {
mlibc::infoLogger() << "\e[31mmlibc: setsockopt() call with IPPROTO_TCP and TCP_KEEPCNT is "
"unimplemented\e[39m"
<< frg::endlog;
return 0;
} else if (layer == SOL_SOCKET && number == SO_OOBINLINE) {
mlibc::infoLogger() << "\e[31mmlibc: setsockopt() call with SOL_SOCKET and SO_OOBINLINE is "
"unimplemented\e[39m"
<< frg::endlog;
return 0;
} else if (layer == SOL_SOCKET && number == SO_PRIORITY) {
mlibc::infoLogger(
) << "\e[31mmlibc: setsockopt() call with SOL_SOCKET and SO_PRIORITY is unimplemented\e[39m"
<< frg::endlog;
return 0;
} else if (layer == SOL_IP && number == IP_RECVERR) {
mlibc::infoLogger(
) << "\e[31mmlibc: setsockopt() call with SOL_IP and IP_RECVERR is unimplemented\e[39m"
<< frg::endlog;
return 0;
} else if (layer == SOL_SOCKET && number == SO_PASSSEC) {
mlibc::infoLogger(
) << "\e[31mmlibc: setsockopt() call with SOL_SOCKET and SO_PASSSEC is unimplemented\e[39m"
<< frg::endlog;
return ENOSYS;
} else {
mlibc::infoLogger() << "\e[31mmlibc: Unexpected setsockopt() call, layer: " << layer
<< " number: " << number << "\e[39m" << frg::endlog;
return EINVAL;
}
}
int sys_listen(int fd, int) {
SignalGuard sguard;
auto handle = getHandleForFd(fd);
if (!handle)
return EBADF;
managarm::fs::CntRequest<MemoryAllocator> req(getSysdepsAllocator());
req.set_req_type(managarm::fs::CntReqType::PT_LISTEN);
frg::string<MemoryAllocator> ser(getSysdepsAllocator());
req.SerializeToString(&ser);
auto [offer, send_req, recv_resp] = exchangeMsgsSync(
handle,
helix_ng::offer(helix_ng::sendBuffer(ser.data(), ser.size()), helix_ng::recvInline())
);
HEL_CHECK(offer.error());
HEL_CHECK(send_req.error());
HEL_CHECK(recv_resp.error());
managarm::fs::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
resp.ParseFromArray(recv_resp.data(), recv_resp.length());
return resp.error() | toErrno;
}
int sys_shutdown(int fd, int how) {
SignalGuard sguard;
auto handle = getHandleForFd(fd);
if (!handle)
return EBADF;
managarm::fs::ShutdownSocket<MemoryAllocator> req(getSysdepsAllocator());
req.set_how(how);
auto [offer, send_req, recv_resp] = exchangeMsgsSync(
handle,
helix_ng::offer(
helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()), helix_ng::recvInline()
)
);
HEL_CHECK(offer.error());
HEL_CHECK(send_req.error());
HEL_CHECK(recv_resp.error());
managarm::fs::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
resp.ParseFromArray(recv_resp.data(), recv_resp.length());
if (resp.error() != managarm::fs::Errors::SUCCESS)
return resp.error() | toErrno;
return 0;
}
} // namespace mlibc
@@ -0,0 +1,418 @@
#include <bits/ensure.h>
#include <bits/errors.hpp>
#include <pthread.h>
#include <sys/time.h>
#include <time.h>
#include <frg/allocation.hpp>
#include <hel-syscalls.h>
#include <hel.h>
#include <mlibc/all-sysdeps.hpp>
#include <mlibc/allocator.hpp>
#include <mlibc/debug.hpp>
#include <mlibc/posix-pipe.hpp>
#include <protocols/posix/supercalls.hpp>
#include "posix.frigg_bragi.hpp"
struct TrackerPage {
uint64_t seqlock;
int32_t state;
int32_t padding;
int64_t refClock;
int64_t baseRealtime;
};
extern thread_local TrackerPage *__mlibc_clk_tracker_page;
namespace mlibc {
int sys_clock_get(int clock, time_t *secs, long *nanos) {
// This implementation is inherently signal-safe.
if (clock == CLOCK_MONOTONIC || clock == CLOCK_MONOTONIC_RAW
|| clock == CLOCK_MONOTONIC_COARSE) {
uint64_t tick;
HEL_CHECK(helGetClock(&tick));
*secs = tick / 1000000000;
*nanos = tick % 1000000000;
} else if (clock == CLOCK_REALTIME) {
cacheFileTable();
// Start the seqlock read.
auto seqlock = __atomic_load_n(&__mlibc_clk_tracker_page->seqlock, __ATOMIC_ACQUIRE);
__ensure(!(seqlock & 1));
// Perform the actual loads.
auto ref = __atomic_load_n(&__mlibc_clk_tracker_page->refClock, __ATOMIC_RELAXED);
auto base = __atomic_load_n(&__mlibc_clk_tracker_page->baseRealtime, __ATOMIC_RELAXED);
// Finish the seqlock read.
__atomic_thread_fence(__ATOMIC_ACQUIRE);
__ensure(__atomic_load_n(&__mlibc_clk_tracker_page->seqlock, __ATOMIC_RELAXED) == seqlock);
// Calculate the current time.
uint64_t tick;
HEL_CHECK(helGetClock(&tick));
__ensure(
tick >= (uint64_t)__mlibc_clk_tracker_page->refClock
); // TODO: Respect the seqlock!
tick -= ref;
tick += base;
*secs = tick / 1000000000;
*nanos = tick % 1000000000;
} else if (clock == CLOCK_PROCESS_CPUTIME_ID) {
mlibc::infoLogger() << "\e[31mmlibc: clock_gettime does not support the CPU time clocks"
"\e[39m"
<< frg::endlog;
*secs = 0;
*nanos = 0;
} else if (clock == CLOCK_BOOTTIME) {
uint64_t tick;
HEL_CHECK(helGetClock(&tick));
*secs = tick / 1000000000;
*nanos = tick % 1000000000;
} else {
mlibc::panicLogger() << "mlibc: Unexpected clock " << clock << frg::endlog;
}
return 0;
}
int sys_clock_getres(int clock, time_t *secs, long *nanos) {
(void)clock;
(void)secs;
(void)nanos;
mlibc::infoLogger() << "mlibc: clock_getres is a stub" << frg::endlog;
return 0;
}
int sys_setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value) {
SignalGuard sguard;
if (which != ITIMER_REAL) {
mlibc::infoLogger() << "mlibc: setitimers other than ITIMER_REAL are unsupported"
<< frg::endlog;
return EINVAL;
}
managarm::posix::SetIntervalTimerRequest<MemoryAllocator> req(getSysdepsAllocator());
req.set_which(which);
req.set_value_sec(new_value->it_value.tv_sec);
req.set_value_usec(new_value->it_value.tv_usec);
req.set_interval_sec(new_value->it_interval.tv_sec);
req.set_interval_usec(new_value->it_interval.tv_usec);
auto [offer, send_req, recv_resp] = exchangeMsgsSync(
getPosixLane(),
helix_ng::offer(
helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()), helix_ng::recvInline()
)
);
HEL_CHECK(offer.error());
HEL_CHECK(send_req.error());
HEL_CHECK(recv_resp.error());
managarm::posix::SetIntervalTimerResponse<MemoryAllocator> resp(getSysdepsAllocator());
resp.ParseFromArray(recv_resp.data(), recv_resp.length());
__ensure(resp.error() == managarm::posix::Errors::SUCCESS);
if (old_value) {
old_value->it_value.tv_sec = resp.value_sec();
old_value->it_value.tv_usec = resp.value_usec();
old_value->it_interval.tv_sec = resp.interval_sec();
old_value->it_interval.tv_usec = resp.interval_usec();
}
return 0;
}
namespace {
bool timerThreadInit = false;
struct PosixTimerContext {
int setupSem = 0;
int workerSem = 0;
sigevent *sigev;
};
struct TimerHandle {
uint64_t id;
int notify_type;
pthread_t thread = {};
};
void timer_handle(int, siginfo_t *, void *) {}
void *timer_setup(void *arg) {
auto ctx = reinterpret_cast<PosixTimerContext *>(arg);
sigset_t set = {};
sigaddset(&set, SIGTIMER);
// wait for parent setup to be complete
while (__atomic_load_n(&ctx->setupSem, __ATOMIC_RELAXED) == 0)
;
pthread_testcancel();
// copy out the function and argument, as the lifetime of the context ends with
// incrementing workerSem
auto notify = ctx->sigev->sigev_notify_function;
union sigval val = ctx->sigev->sigev_value;
// notify the parent that the context can be dropped
__atomic_store_n(&ctx->workerSem, 1, __ATOMIC_RELEASE);
siginfo_t si;
int signo;
while (true) {
pthread_testcancel();
while (sys_sigtimedwait(&set, &si, nullptr, &signo))
;
pthread_testcancel();
if (si.si_code == SI_TIMER && signo == SIGTIMER)
notify(val);
}
return nullptr;
}
} // namespace
int sys_timer_create(clockid_t clk, struct sigevent *__restrict evp, timer_t *__restrict res) {
SignalGuard sguard;
if (!res)
return EINVAL;
managarm::posix::TimerCreateRequest<MemoryAllocator> req(getSysdepsAllocator());
req.set_clockid(clk);
// TODO: pass sigev_value
if (!evp) {
req.set_sigev_signo(SIGALRM);
req.set_sigev_tid(sys_gettid());
} else if (evp->sigev_notify == SIGEV_NONE) {
req.set_sigev_signo(0);
req.set_sigev_tid(0);
} else if (evp->sigev_notify == SIGEV_SIGNAL) {
req.set_sigev_signo(evp->sigev_signo);
req.set_sigev_tid(sys_gettid());
} else if (evp->sigev_notify == SIGEV_THREAD_ID) {
req.set_sigev_signo(evp->sigev_signo);
req.set_sigev_tid(evp->sigev_notify_thread_id);
} else if (evp->sigev_notify == SIGEV_THREAD) {
if (!timerThreadInit) {
struct sigaction sa{};
sa.sa_flags = SA_SIGINFO | SA_RESTART;
sa.sa_sigaction = timer_handle;
sys_sigaction(SIGTIMER, &sa, nullptr);
timerThreadInit = true;
}
pthread_attr_t attr;
if (evp->sigev_notify_attributes)
attr = *evp->sigev_notify_attributes;
else
pthread_attr_init(&attr);
int ret = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
if (ret)
return ret;
PosixTimerContext context{};
context.sigev = evp;
// mask for all signals except the libc-reserved RT signal range
sigset_t mask = {
#if ULONG_MAX == 0xFFFF'FFFF
0x7FFF'FFFF, 0xFFFF'FFFC
#else
0xFFFF'FFFC'7FFF'FFFF
#endif
};
// but also mask SIGTIMER
sigaddset(&mask, SIGTIMER);
HelWord original_set;
uint64_t unused;
HEL_CHECK(helSyscall2_2(
kHelObserveSuperCall + posix::superSigMask,
SIG_BLOCK,
*reinterpret_cast<const HelWord *>(&mask),
&original_set,
&unused
));
pthread_t pthread;
ret = pthread_create(&pthread, &attr, timer_setup, &context);
// restore previous signal mask
HEL_CHECK(helSyscall2_2(
kHelObserveSuperCall + posix::superSigMask, SIG_SETMASK, original_set, &unused, &unused
));
if (ret)
return ret;
req.set_sigev_signo(SIGTIMER);
req.set_sigev_tid(reinterpret_cast<Tcb *>(pthread)->tid);
infoLogger() << "mlibc: timer_create: created timer thread "
<< reinterpret_cast<Tcb *>(pthread)->tid << frg::endlog;
auto [offer, send_req, recv_resp] = exchangeMsgsSync(
getPosixLane(),
helix_ng::offer(
helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()), helix_ng::recvInline()
)
);
HEL_CHECK(offer.error());
HEL_CHECK(send_req.error());
HEL_CHECK(recv_resp.error());
managarm::posix::TimerCreateResponse<MemoryAllocator> resp(getSysdepsAllocator());
resp.ParseFromArray(recv_resp.data(), recv_resp.length());
if (resp.error() != managarm::posix::Errors::SUCCESS) {
pthread_cancel(pthread);
__atomic_store_n(&context.setupSem, 1, __ATOMIC_RELEASE);
return resp.error() | toErrno;
}
// notify worker that setup is complete
__atomic_store_n(&context.setupSem, 1, __ATOMIC_RELEASE);
// await worker setup to let the context go out of scope
while (__atomic_load_n(&context.workerSem, __ATOMIC_RELAXED) == 0)
;
*res = frg::construct<TimerHandle>(
getSysdepsAllocator(), resp.timer_id(), evp->sigev_notify, pthread
);
return 0;
} else {
mlibc::infoLogger() << "mlibc: timer_create: unsupported sigevent type "
<< evp->sigev_notify << frg::endlog;
return EINVAL;
}
auto [offer, send_req, recv_resp] = exchangeMsgsSync(
getPosixLane(),
helix_ng::offer(
helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()), helix_ng::recvInline()
)
);
HEL_CHECK(offer.error());
HEL_CHECK(send_req.error());
HEL_CHECK(recv_resp.error());
managarm::posix::TimerCreateResponse<MemoryAllocator> resp(getSysdepsAllocator());
resp.ParseFromArray(recv_resp.data(), recv_resp.length());
if (resp.error() != managarm::posix::Errors::SUCCESS)
return resp.error() | toErrno;
*res = frg::construct<TimerHandle>(
getSysdepsAllocator(), resp.timer_id(), evp ? evp->sigev_notify : SIGEV_SIGNAL, pthread_t{}
);
return 0;
}
int sys_timer_settime(
timer_t t, int flags, const struct itimerspec *__restrict val, struct itimerspec *__restrict old
) {
SignalGuard sguard;
auto timerHandle = reinterpret_cast<TimerHandle *>(t);
managarm::posix::TimerSetRequest<MemoryAllocator> req(getSysdepsAllocator());
req.set_timer(timerHandle->id);
req.set_flags(flags);
req.set_value_sec(val->it_value.tv_sec);
req.set_value_nsec(val->it_value.tv_nsec);
req.set_interval_sec(val->it_interval.tv_sec);
req.set_interval_nsec(val->it_interval.tv_nsec);
auto [offer, send_req, recv_resp] = exchangeMsgsSync(
getPosixLane(),
helix_ng::offer(
helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()), helix_ng::recvInline()
)
);
HEL_CHECK(offer.error());
HEL_CHECK(send_req.error());
HEL_CHECK(recv_resp.error());
managarm::posix::TimerSetResponse<MemoryAllocator> resp(getSysdepsAllocator());
resp.ParseFromArray(recv_resp.data(), recv_resp.length());
if (resp.error() != managarm::posix::Errors::SUCCESS)
return resp.error() | toErrno;
if (old) {
old->it_value.tv_sec = resp.value_sec();
old->it_value.tv_nsec = resp.value_nsec();
old->it_interval.tv_sec = resp.interval_sec();
old->it_interval.tv_nsec = resp.interval_nsec();
}
return 0;
}
int sys_timer_gettime(timer_t t, struct itimerspec *val) {
SignalGuard sguard;
auto timerHandle = reinterpret_cast<TimerHandle *>(t);
managarm::posix::TimerGetRequest<MemoryAllocator> req(getSysdepsAllocator());
req.set_timer((timerHandle->id));
auto [offer, send_req, recv_resp] = exchangeMsgsSync(
getPosixLane(),
helix_ng::offer(
helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()), helix_ng::recvInline()
)
);
HEL_CHECK(offer.error());
HEL_CHECK(send_req.error());
HEL_CHECK(recv_resp.error());
managarm::posix::TimerGetResponse<MemoryAllocator> resp(getSysdepsAllocator());
resp.ParseFromArray(recv_resp.data(), recv_resp.length());
if (resp.error() != managarm::posix::Errors::SUCCESS)
return resp.error() | toErrno;
if (val) {
val->it_value.tv_sec = resp.value_sec();
val->it_value.tv_nsec = resp.value_nsec();
val->it_interval.tv_sec = resp.interval_sec();
val->it_interval.tv_nsec = resp.interval_nsec();
}
return 0;
}
int sys_timer_delete(timer_t t) {
SignalGuard sguard;
auto timerHandle = reinterpret_cast<TimerHandle *>(t);
if (timerHandle->notify_type == SIGEV_THREAD) {
pthread_cancel(timerHandle->thread);
pthread_kill(timerHandle->thread, SIGTIMER);
}
managarm::posix::TimerDeleteRequest<MemoryAllocator> req(getSysdepsAllocator());
req.set_timer(timerHandle->id);
auto [offer, send_req, recv_resp] = exchangeMsgsSync(
getPosixLane(),
helix_ng::offer(
helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()), helix_ng::recvInline()
)
);
HEL_CHECK(offer.error());
HEL_CHECK(send_req.error());
HEL_CHECK(recv_resp.error());
managarm::posix::TimerDeleteResponse<MemoryAllocator> resp(getSysdepsAllocator());
resp.ParseFromArray(recv_resp.data(), recv_resp.length());
return resp.error() | toErrno;
}
} // namespace mlibc
@@ -0,0 +1 @@
../../../../abis/linux/access.h
@@ -0,0 +1 @@
../../../../abis/managarm/auxv.h
@@ -0,0 +1 @@
../../../../abis/linux/blkcnt_t.h
@@ -0,0 +1 @@
../../../../abis/linux/blksize_t.h
@@ -0,0 +1 @@
../../../../abis/linux/clockid_t.h
@@ -0,0 +1 @@
../../../../abis/linux/dev_t.h
@@ -0,0 +1 @@
../../../../abis/linux/epoll.h
@@ -0,0 +1 @@
../../../../abis/linux/errno.h
@@ -0,0 +1 @@
../../../../abis/linux/fcntl.h
@@ -0,0 +1 @@
../../../../abis/linux/fsblkcnt_t.h
@@ -0,0 +1 @@
../../../../abis/linux/fsfilcnt_t.h
@@ -0,0 +1 @@
../../../../abis/linux/gid_t.h
@@ -0,0 +1 @@
../../../../abis/linux/in.h
@@ -0,0 +1 @@
../../../../abis/linux/ino_t.h
@@ -0,0 +1 @@
../../../../abis/linux/inotify.h
@@ -0,0 +1 @@
../../../../abis/linux/ioctls.h
@@ -0,0 +1 @@
../../../../abis/linux/ipc.h
@@ -0,0 +1 @@
../../../../abis/linux/limits.h
@@ -0,0 +1 @@
../../../../abis/linux/mode_t.h
@@ -0,0 +1 @@
../../../../abis/linux/mqueue.h
@@ -0,0 +1 @@
../../../../abis/linux/msg.h
@@ -0,0 +1 @@
../../../../abis/linux/nlink_t.h
@@ -0,0 +1 @@
../../../../abis/linux/packet.h
@@ -0,0 +1 @@
../../../../abis/linux/pid_t.h
@@ -0,0 +1 @@
../../../../abis/linux/poll.h
@@ -0,0 +1 @@
../../../../abis/linux/ptrace.h
@@ -0,0 +1 @@
../../../../abis/linux/random.h
@@ -0,0 +1 @@
../../../../abis/linux/reboot.h
@@ -0,0 +1 @@
../../../../abis/linux/resource.h
@@ -0,0 +1 @@
../../../../abis/linux/riscv-hwprobe.h
@@ -0,0 +1 @@
../../../../abis/linux/rlim_t.h
@@ -0,0 +1 @@
../../../../abis/linux/seek-whence.h
@@ -0,0 +1 @@
../../../../abis/linux/shm.h
@@ -0,0 +1 @@
../../../../abis/linux/sigevent.h
@@ -0,0 +1 @@
../../../../abis/linux/signal.h
@@ -0,0 +1 @@
../../../../abis/linux/sigval.h
@@ -0,0 +1 @@
../../../../abis/linux/socket.h
@@ -0,0 +1 @@
../../../../abis/linux/socklen_t.h
@@ -0,0 +1 @@
../../../../abis/linux/stat.h
@@ -0,0 +1 @@
../../../../abis/linux/statfs.h
@@ -0,0 +1 @@
../../../../abis/linux/statvfs.h
@@ -0,0 +1 @@
../../../../abis/linux/statx.h
@@ -0,0 +1 @@
../../../../abis/linux/suseconds_t.h
@@ -0,0 +1 @@
../../../../abis/linux/termios.h
@@ -0,0 +1 @@
../../../../abis/linux/time.h
@@ -0,0 +1 @@
../../../../abis/linux/uid_t.h
@@ -0,0 +1 @@
../../../../abis/linux/utmp-defines.h
@@ -0,0 +1 @@
../../../../abis/linux/utmpx.h
@@ -0,0 +1 @@
../../../../abis/linux/utsname.h
@@ -0,0 +1 @@
../../../../abis/linux/vm-flags.h
@@ -0,0 +1 @@
../../../../abis/linux/vt.h
@@ -0,0 +1 @@
../../../../abis/linux/wait.h
@@ -0,0 +1 @@
../../../../abis/linux/xattr.h
@@ -0,0 +1,167 @@
#pragma once
#include <errno.h>
#include <mlibc/debug.hpp>
#include "fs.frigg_bragi.hpp"
#include "posix.frigg_bragi.hpp"
struct ToErrno {
template <typename E>
auto operator()(E e) const {
return e | *this;
}
};
constexpr ToErrno toErrno;
inline int operator|(managarm::fs::Errors e, ToErrno) {
switch (e) {
case managarm::fs::Errors::SUCCESS:
return 0;
case managarm::fs::Errors::FILE_NOT_FOUND:
return ENOENT;
case managarm::fs::Errors::END_OF_FILE:
return 0;
case managarm::fs::Errors::ILLEGAL_ARGUMENT:
return EINVAL;
case managarm::fs::Errors::WOULD_BLOCK:
return EAGAIN;
case managarm::fs::Errors::SEEK_ON_PIPE:
return ESPIPE;
case managarm::fs::Errors::BROKEN_PIPE:
return EPIPE;
case managarm::fs::Errors::ACCESS_DENIED:
return EPERM;
case managarm::fs::Errors::NOT_DIRECTORY:
return ENOTDIR;
case managarm::fs::Errors::AF_NOT_SUPPORTED:
return EAFNOSUPPORT;
case managarm::fs::Errors::DESTINATION_ADDRESS_REQUIRED:
return EDESTADDRREQ;
case managarm::fs::Errors::NETWORK_UNREACHABLE:
return ENETUNREACH;
case managarm::fs::Errors::MESSAGE_TOO_LARGE:
return EMSGSIZE;
case managarm::fs::Errors::HOST_UNREACHABLE:
return EHOSTUNREACH;
case managarm::fs::Errors::INSUFFICIENT_PERMISSIONS:
return EPERM;
case managarm::fs::Errors::ADDRESS_IN_USE:
return EADDRINUSE;
case managarm::fs::Errors::ADDRESS_NOT_AVAILABLE:
return EADDRNOTAVAIL;
case managarm::fs::Errors::NOT_CONNECTED:
return ENOTCONN;
case managarm::fs::Errors::ALREADY_EXISTS:
return EEXIST;
case managarm::fs::Errors::ILLEGAL_OPERATION_TARGET:
return EINVAL;
case managarm::fs::Errors::NO_SPACE_LEFT:
return ENOSPC;
case managarm::fs::Errors::NOT_A_TERMINAL:
return ENOTTY;
case managarm::fs::Errors::NO_BACKING_DEVICE:
return ENXIO;
case managarm::fs::Errors::IS_DIRECTORY:
return EISDIR;
case managarm::fs::Errors::INVALID_PROTOCOL_OPTION:
return ENOPROTOOPT;
case managarm::fs::Errors::DIRECTORY_NOT_EMPTY:
return ENOTEMPTY;
case managarm::fs::Errors::CONNECTION_REFUSED:
return ECONNREFUSED;
case managarm::fs::Errors::ALREADY_CONNECTED:
return EISCONN;
case managarm::fs::Errors::NOT_A_SOCKET:
return ENOTSOCK;
case managarm::fs::Errors::INTERNAL_ERROR:
return EIO;
case managarm::fs::Errors::INTERRUPTED:
return EINTR;
case managarm::fs::Errors::NO_SUCH_PROCESS:
return ESRCH;
case managarm::fs::Errors::NAME_TOO_LONG:
return ENAMETOOLONG;
case managarm::fs::Errors::NO_FILE_DESCRIPTORS_AVAILABLE:
return EMFILE;
}
mlibc::panicLogger() << "unhandled managarm::fs::Errors " << static_cast<int32_t>(e)
<< frg::endlog;
__builtin_unreachable();
}
inline int operator|(managarm::posix::Errors e, ToErrno) {
switch (e) {
case managarm::posix::Errors::SUCCESS:
return 0;
case managarm::posix::Errors::FILE_NOT_FOUND:
return ENOENT;
case managarm::posix::Errors::END_OF_FILE:
return 0;
case managarm::posix::Errors::WOULD_BLOCK:
return EAGAIN;
case managarm::posix::Errors::BROKEN_PIPE:
return EPIPE;
case managarm::posix::Errors::ACCESS_DENIED:
return EPERM;
case managarm::posix::Errors::NOT_A_DIRECTORY:
return ENOTDIR;
case managarm::posix::Errors::INSUFFICIENT_PERMISSION:
return EPERM;
case managarm::posix::Errors::ALREADY_EXISTS:
return EEXIST;
case managarm::posix::Errors::ILLEGAL_OPERATION_TARGET:
return EINVAL;
case managarm::posix::Errors::NO_BACKING_DEVICE:
return ENXIO;
case managarm::posix::Errors::IS_DIRECTORY:
return EISDIR;
case managarm::posix::Errors::DIRECTORY_NOT_EMPTY:
return ENOTEMPTY;
case managarm::posix::Errors::INTERNAL_ERROR:
return EIO;
case managarm::posix::Errors::DEAD_FORK:
return EAGAIN;
case managarm::posix::Errors::ILLEGAL_REQUEST:
return ENOSYS;
case managarm::posix::Errors::ILLEGAL_ARGUMENTS:
return EINVAL;
case managarm::posix::Errors::NO_SUCH_FD:
return EBADF;
case managarm::posix::Errors::BAD_FD:
return EBADFD;
case managarm::posix::Errors::NOT_SUPPORTED:
return ENOTSUP;
case managarm::posix::Errors::RESOURCE_IN_USE:
return EBUSY;
case managarm::posix::Errors::NO_SUCH_RESOURCE:
return ESRCH;
case managarm::posix::Errors::NOT_A_TTY:
return ENOTTY;
case managarm::posix::Errors::PROTOCOL_NOT_SUPPORTED:
return EPROTONOSUPPORT;
case managarm::posix::Errors::ADDRESS_FAMILY_NOT_SUPPORTED:
return EAFNOSUPPORT;
case managarm::posix::Errors::NO_MEMORY:
return ENOMEM;
case managarm::posix::Errors::NO_CHILD_PROCESSES:
return ECHILD;
case managarm::posix::Errors::SYMBOLIC_LINK_LOOP:
return ELOOP;
case managarm::posix::Errors::ALREADY_CONNECTED:
return EISCONN;
case managarm::posix::Errors::UNSUPPORTED_SOCKET_TYPE:
return ESOCKTNOSUPPORT;
case managarm::posix::Errors::NAME_TOO_LONG:
return ENAMETOOLONG;
case managarm::posix::Errors::NO_FILE_DESCRIPTORS_AVAILABLE:
return EMFILE;
case managarm::posix::Errors::INTERRUPTED:
return EINTR;
}
mlibc::panicLogger() << "unhandled managarm::posix::Errors " << static_cast<int32_t>(e)
<< frg::endlog;
__builtin_unreachable();
}
@@ -0,0 +1,336 @@
#ifndef MLIBC_POSIX_PIPE
#define MLIBC_POSIX_PIPE
#include <cstddef>
#include <string.h>
#include <hel-syscalls.h>
#include <hel.h>
#include <bits/ensure.h>
#include <frg/optional.hpp>
#include <mlibc/allocator.hpp>
#include <mlibc/debug.hpp>
struct SignalGuard {
SignalGuard();
SignalGuard(const SignalGuard &) = delete;
~SignalGuard();
SignalGuard &operator=(const SignalGuard &) = delete;
};
// We need an allocator for message structs in sysdeps functions; the "normal" mlibc
// allocator cannot be used, as the sysdeps function might be called from a signal.
MemoryAllocator &getSysdepsAllocator();
struct Queue;
struct ElementHandle {
friend void swap(ElementHandle &u, ElementHandle &v) {
using std::swap;
swap(u._queue, v._queue);
swap(u._n, v._n);
swap(u._data, v._data);
}
ElementHandle() : _queue{nullptr}, _n{-1}, _data{nullptr} {}
ElementHandle(Queue *queue, int n, void *data) : _queue{queue}, _n{n}, _data{data} {}
ElementHandle(const ElementHandle &other);
ElementHandle(ElementHandle &&other) : ElementHandle{} { swap(*this, other); }
~ElementHandle();
ElementHandle &operator=(ElementHandle other) {
swap(*this, other);
return *this;
}
void *data() { return _data; }
void advance(size_t size) { _data = reinterpret_cast<char *>(_data) + size; }
private:
Queue *_queue;
int _n;
void *_data;
};
struct Queue {
Queue() : _handle{kHelNullHandle} {
// We do not need to protect those allocations against signals as this constructor
// is only called during library initialization.
_chunks[0] =
reinterpret_cast<HelChunk *>(getSysdepsAllocator().allocate(sizeof(HelChunk) + 4096));
_chunks[1] =
reinterpret_cast<HelChunk *>(getSysdepsAllocator().allocate(sizeof(HelChunk) + 4096));
recreateQueue();
}
Queue(const Queue &) = delete;
Queue &operator=(const Queue &) = delete;
void recreateQueue() {
// Reset the internal queue state.
_retrieveIndex = 0;
_nextIndex = 0;
_lastProgress = 0;
// Setup the queue header.
HelQueueParameters params{.flags = 0, .ringShift = 1, .numChunks = 2, .chunkSize = 4096};
HEL_CHECK(helCreateQueue(&params, &_handle));
auto chunksOffset = (sizeof(HelQueue) + (sizeof(int) << 1) + 63) & ~size_t(63);
auto reservedPerChunk = (sizeof(HelChunk) + params.chunkSize + 63) & ~size_t(63);
auto overallSize = chunksOffset + params.numChunks * reservedPerChunk;
void *mapping;
HEL_CHECK(helMapMemory(
_handle,
kHelNullHandle,
nullptr,
0,
(overallSize + 0xFFF) & ~size_t(0xFFF),
kHelMapProtRead | kHelMapProtWrite,
&mapping
));
_queue = reinterpret_cast<HelQueue *>(mapping);
auto chunksPtr = reinterpret_cast<std::byte *>(mapping) + chunksOffset;
for (unsigned int i = 0; i < 2; ++i)
_chunks[i] = reinterpret_cast<HelChunk *>(chunksPtr + i * reservedPerChunk);
// Reset and enqueue the chunks.
_chunks[0]->progressFutex = 0;
_chunks[1]->progressFutex = 0;
_refCount[0] = 1;
_refCount[1] = 1;
_queue->indexQueue[0] = 0;
_queue->indexQueue[1] = 1;
_queue->headFutex = 0;
_nextIndex = 2;
_wakeHeadFutex();
}
HelHandle getQueue() { return _handle; }
void trim() {}
frg::optional<ElementHandle> dequeueSingleUnlessCancelled() {
while (true) {
__ensure(_retrieveIndex != _nextIndex);
auto progress = _waitProgressFutex();
auto n = _numberOf(_retrieveIndex);
__ensure(_refCount[n]);
if (progress == FutexProgress::DONE) {
retire(n);
_lastProgress = 0;
_retrieveIndex = ((_retrieveIndex + 1) & kHelHeadMask);
continue;
}
if (progress == FutexProgress::CANCELLED)
return frg::null_opt;
// Dequeue the next element.
auto ptr = (char *)_chunks[n] + sizeof(HelChunk) + _lastProgress;
auto element = reinterpret_cast<HelElement *>(ptr);
_lastProgress += sizeof(HelElement) + element->length;
_refCount[n]++;
return ElementHandle{this, n, ptr + sizeof(HelElement)};
}
}
ElementHandle dequeueSingle() {
while (true) {
auto result = dequeueSingleUnlessCancelled();
if (result)
return *result;
}
}
void retire(int n) {
__ensure(_refCount[n]);
if (_refCount[n]-- > 1)
return;
// Reset and enqueue the chunk again.
_chunks[n]->progressFutex = 0;
_refCount[n] = 1;
_queue->indexQueue[_nextIndex & 1] = n;
_nextIndex = ((_nextIndex + 1) & kHelHeadMask);
_wakeHeadFutex();
}
void reference(int n) { _refCount[n]++; }
private:
int _numberOf(int index) { return _queue->indexQueue[index & 1]; }
HelChunk *_retrieveChunk() { return _chunks[_numberOf(_retrieveIndex)]; }
void _wakeHeadFutex() {
auto futex = __atomic_exchange_n(&_queue->headFutex, _nextIndex, __ATOMIC_RELEASE);
if (futex & kHelHeadWaiters)
HEL_CHECK(helFutexWake(&_queue->headFutex));
}
enum class FutexProgress {
DONE,
PROGRESS,
CANCELLED,
};
FutexProgress _waitProgressFutex() {
while (true) {
auto futex = __atomic_load_n(&_retrieveChunk()->progressFutex, __ATOMIC_ACQUIRE);
__ensure(!(futex & ~(kHelProgressMask | kHelProgressWaiters | kHelProgressDone)));
do {
if (_lastProgress != (futex & kHelProgressMask))
return FutexProgress::PROGRESS;
else if (futex & kHelProgressDone)
return FutexProgress::DONE;
if (futex & kHelProgressWaiters)
break; // Waiters bit is already set (in a previous iteration).
} while (!__atomic_compare_exchange_n(
&_retrieveChunk()->progressFutex,
&futex,
_lastProgress | kHelProgressWaiters,
false,
__ATOMIC_ACQUIRE,
__ATOMIC_ACQUIRE
));
int err = helFutexWait(
&_retrieveChunk()->progressFutex, _lastProgress | kHelProgressWaiters, -1
);
if (err == kHelErrCancelled)
return FutexProgress::CANCELLED;
HEL_CHECK(err);
}
}
private:
HelHandle _handle;
HelQueue *_queue;
HelChunk *_chunks[2];
// Index of the chunk that we are currently retrieving/inserting next.
int _retrieveIndex;
int _nextIndex;
// Progress into the current chunk.
int _lastProgress;
// Number of ElementHandle objects alive.
int _refCount[2];
};
inline ElementHandle::~ElementHandle() {
if (_queue)
_queue->retire(_n);
}
inline ElementHandle::ElementHandle(const ElementHandle &other) {
_queue = other._queue;
_n = other._n;
_data = other._data;
_queue->reference(_n);
}
inline HelSimpleResult *parseSimple(ElementHandle &element) {
auto result = reinterpret_cast<HelSimpleResult *>(element.data());
element.advance(sizeof(HelSimpleResult));
return result;
}
inline HelInlineResult *parseInline(ElementHandle &element) {
auto result = reinterpret_cast<HelInlineResult *>(element.data());
element.advance(sizeof(HelInlineResult) + ((result->length + 7) & ~size_t(7)));
return result;
}
inline HelLengthResult *parseLength(ElementHandle &element) {
auto result = reinterpret_cast<HelLengthResult *>(element.data());
element.advance(sizeof(HelLengthResult));
return result;
}
inline HelHandleResult *parseHandle(ElementHandle &element) {
auto result = reinterpret_cast<HelHandleResult *>(element.data());
element.advance(sizeof(HelHandleResult));
return result;
}
HelHandle getPosixLane();
HelHandle *cacheFileTable();
HelHandle getHandleForFd(int fd);
void resetCancellationId();
void setCancellationId(uint64_t event, HelHandle handle, int fd);
void clearCachedInfos();
uint64_t allocateCancellationId();
extern thread_local Queue globalQueue;
// This include is here because it needs ElementHandle to be declared
#include <helix/ipc-structs.hpp>
template <typename... Args>
auto exchangeMsgsSync(HelHandle descriptor, Args &&...args) {
auto results = helix_ng::createResultsTuple(args...);
auto actions = helix_ng::chainActionArrays(args...);
HEL_CHECK(
helSubmitAsync(descriptor, actions.data(), actions.size(), globalQueue.getQueue(), 0, 0)
);
auto element = globalQueue.dequeueSingle();
void *ptr = element.data();
[&]<size_t... p>(std::index_sequence<p...>) {
(results.template get<p>().parse(ptr, element), ...);
}(std::make_index_sequence<std::tuple_size_v<decltype(results)>>{});
return results;
}
template <typename... Args>
auto exchangeMsgsSyncCancellable(HelHandle descriptor, uint64_t cancelId, int fd, Args &&...args) {
auto results = helix_ng::createResultsTuple(args...);
auto actions = helix_ng::chainActionArrays(args...);
HEL_CHECK(
helSubmitAsync(descriptor, actions.data(), actions.size(), globalQueue.getQueue(), 0, 0)
);
setCancellationId(cancelId, descriptor, fd);
auto element = globalQueue.dequeueSingle();
void *ptr = element.data();
[&]<size_t... p>(std::index_sequence<p...>) {
(results.template get<p>().parse(ptr, element), ...);
}(std::make_index_sequence<std::tuple_size_v<decltype(results)>>{});
resetCancellationId();
return results;
}
#endif // MLIBC_POSIX_PIPE
@@ -0,0 +1,10 @@
#pragma once
#include <mlibc/tcb.hpp>
extern "C" void __mlibc_start_thread(void);
extern "C" void __mlibc_enter_thread(void *entry, void *user_arg, Tcb *tcb);
namespace mlibc {
void *prepare_stack(void *entry, void *user_arg, void *tcb);
}
@@ -0,0 +1,176 @@
sysdep_supported_options = {
'posix': true,
'linux': true,
'glibc': true,
'bsd': true,
}
managarm = subproject('managarm', default_options: [ 'provide_deps=true' ])
hel_dep = managarm.get_variable('hel_dep')
proto_posix_dep = managarm.get_variable('posix_extra_dep')
bragi = find_program('bragi')
bragi_dep = subproject('bragi', default_options: ['install_headers=false']).get_variable('bragi_dep')
libdrm_dep = subproject('libdrm-headers').get_variable('libdrm_dep')
bragi_gen = generator(bragi, arguments: [
'@INPUT@',
'-o',
'@OUTPUT@',
'cpp', '-l', 'frigg',
'--protobuf',
],
output: '@BASENAME@.frigg_bragi.hpp')
fs_bragi = bragi_gen.process(managarm.get_variable('fs_bragi_files'))
posix_bragi = bragi_gen.process(managarm.get_variable('posix_bragi_files'))
rtld_sources += files(
'generic/ensure.cpp',
'generic/memory.cpp',
'rtld-generic/support.cpp',
)
rtld_sources += [
fs_bragi,
posix_bragi,
]
libc_deps += [ bragi_dep, hel_dep, proto_posix_dep, libdrm_dep ]
rtld_deps += [ bragi_dep, hel_dep, proto_posix_dep ]
libc_sources += files(
'generic/drm.cpp',
'generic/ensure.cpp',
'generic/entry.cpp',
'generic/file.cpp',
'generic/fork-exec.cpp',
'generic/ioctl.cpp',
'generic/memory.cpp',
'generic/mount.cpp',
'generic/net.cpp',
'generic/sched.cpp',
'generic/signals.cpp',
'generic/socket.cpp',
'generic/time.cpp'
)
libc_sources += [
fs_bragi,
posix_bragi,
]
if host_machine.cpu_family() == 'aarch64'
libc_sources += files(
'aarch64/signals.S',
'aarch64/thread_entry.S',
'aarch64/thread.cpp'
)
elif host_machine.cpu_family() == 'x86_64'
libc_sources += files(
'x86_64/signals.S',
'x86_64/thread_entry.S',
'x86_64/thread.cpp',
'x86_64/assembly-asserts.cpp',
)
elif host_machine.cpu_family() == 'riscv64'
libc_sources += files(
'riscv64/signals.S',
'riscv64/thread_entry.S',
'riscv64/thread.cpp',
)
else
error('Unknown architecture')
endif
if not no_headers
install_headers(
'include/abi-bits/access.h',
'include/abi-bits/auxv.h',
'include/abi-bits/seek-whence.h',
'include/abi-bits/vm-flags.h',
'include/abi-bits/errno.h',
'include/abi-bits/fcntl.h',
'include/abi-bits/in.h',
'include/abi-bits/stat.h',
'include/abi-bits/statx.h',
'include/abi-bits/signal.h',
'include/abi-bits/reboot.h',
'include/abi-bits/resource.h',
'include/abi-bits/socket.h',
'include/abi-bits/termios.h',
'include/abi-bits/time.h',
'include/abi-bits/blkcnt_t.h',
'include/abi-bits/blksize_t.h',
'include/abi-bits/dev_t.h',
'include/abi-bits/gid_t.h',
'include/abi-bits/ino_t.h',
'include/abi-bits/mode_t.h',
'include/abi-bits/nlink_t.h',
'include/abi-bits/pid_t.h',
'include/abi-bits/uid_t.h',
'include/abi-bits/wait.h',
'include/abi-bits/limits.h',
'include/abi-bits/utsname.h',
'include/abi-bits/ptrace.h',
'include/abi-bits/poll.h',
'include/abi-bits/epoll.h',
'include/abi-bits/packet.h',
'include/abi-bits/inotify.h',
'include/abi-bits/clockid_t.h',
'include/abi-bits/ipc.h',
'include/abi-bits/shm.h',
'include/abi-bits/mqueue.h',
'include/abi-bits/suseconds_t.h',
'include/abi-bits/fsfilcnt_t.h',
'include/abi-bits/fsblkcnt_t.h',
'include/abi-bits/socklen_t.h',
'include/abi-bits/statfs.h',
'include/abi-bits/statvfs.h',
'include/abi-bits/ioctls.h',
'include/abi-bits/xattr.h',
'include/abi-bits/msg.h',
'include/abi-bits/vt.h',
'include/abi-bits/random.h',
'include/abi-bits/rlim_t.h',
'include/abi-bits/sigval.h',
'include/abi-bits/sigevent.h',
'include/abi-bits/utmpx.h',
'include/abi-bits/utmp-defines.h',
subdir: 'abi-bits',
follow_symlinks: true
)
if host_machine.cpu_family() == 'riscv64'
install_headers(
'include/abi-bits/riscv-hwprobe.h',
subdir: 'abi-bits',
follow_symlinks: true
)
endif
endif
if not headers_only
crtstuff = ['crt0']
if host_machine.cpu_family() in ['x86_64', 'aarch64', 'riscv64']
crtstuff += [
'Scrt1',
'crti',
'crtn'
]
endif
foreach crtthing : crtstuff
crtf = crtthing + '.S'
crt_src = files(host_machine.cpu_family() / 'crt-src' / crtf)
crt = custom_target(
crtthing,
build_by_default: true,
command: c_compiler.cmd_array() + ['-c', '-o', '@OUTPUT@', '@INPUT@'],
input: crt_src,
output: crtthing + '.o',
install: true,
install_dir: get_option('libdir')
)
endforeach
endif
@@ -0,0 +1,30 @@
.weak __global_pointer$
.hidden __global_pointer$
.section .text
.global _start
_start:
# Load gp.
.option push
.option norelax
lla gp, __global_pointer$
.option pop
mv a0, sp
la a1, main
call __mlibc_entry@plt
unimp
# Load gp from .preinit_array since it may be used by the executable's .init_array.
# We still load it in _start to account for static binaries. This matches glibc's behavior.
load_gp:
.option push
.option norelax
lla gp, __global_pointer$
.option pop
ret
.section .preinit_array,"aw"
.dword load_gp
.section .note.GNU-stack,"",%progbits
@@ -0,0 +1,30 @@
.weak __global_pointer$
.hidden __global_pointer$
.section .text
.global _start
_start:
# Load gp.
.option push
.option norelax
lla gp, __global_pointer$
.option pop
mv a0, sp
la a1, main
call __mlibc_entry
unimp
# Load gp from .preinit_array since it may be used by the executable's .init_array.
# We still load it in _start to account for static binaries. This matches glibc's behavior.
load_gp:
.option push
.option norelax
lla gp, __global_pointer$
.option pop
ret
.section .preinit_array,"aw"
.dword load_gp
.section .note.GNU-stack,"",%progbits
@@ -0,0 +1 @@
@@ -0,0 +1 @@
@@ -0,0 +1,7 @@
.section .text
.global __mlibc_signal_restore
__mlibc_signal_restore:
li a0, 0x80000006
ecall
unimp
.section .note.GNU-stack,"",%progbits
@@ -0,0 +1,64 @@
#include <bits/ensure.h>
#include <errno.h>
#include <mlibc/all-sysdeps.hpp>
#include <mlibc/tcb.hpp>
#include <mlibc/thread-entry.hpp>
#include <stddef.h>
#include <stdint.h>
#include <sys/mman.h>
extern "C" void __mlibc_enter_thread(void *entry, void *user_arg, Tcb *tcb) {
// Wait until our parent sets up the TID.
while (!__atomic_load_n(&tcb->tid, __ATOMIC_RELAXED))
mlibc::sys_futex_wait(&tcb->tid, 0, nullptr);
if (mlibc::sys_tcb_set(tcb))
__ensure(!"sys_tcb_set() failed");
tcb->invokeThreadFunc(entry, user_arg);
auto self = reinterpret_cast<Tcb *>(tcb);
__atomic_store_n(&self->didExit, 1, __ATOMIC_RELEASE);
mlibc::sys_futex_wake(&self->didExit);
mlibc::sys_thread_exit();
}
namespace mlibc {
static constexpr size_t default_stacksize = 0x200000;
int sys_prepare_stack(
void **stack,
void *entry,
void *user_arg,
void *tcb,
size_t *stack_size,
size_t *guard_size,
void **stack_base
) {
if (!*stack_size)
*stack_size = default_stacksize;
*guard_size = 0;
if (*stack) {
*stack_base = *stack;
} else {
*stack_base =
mmap(nullptr, *stack_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (*stack_base == MAP_FAILED) {
return errno;
}
}
uintptr_t *sp =
reinterpret_cast<uintptr_t *>(reinterpret_cast<uintptr_t>(*stack_base) + *stack_size);
*--sp = reinterpret_cast<uintptr_t>(tcb);
*--sp = reinterpret_cast<uintptr_t>(user_arg);
*--sp = reinterpret_cast<uintptr_t>(entry);
*stack = reinterpret_cast<void *>(sp);
return 0;
}
} // namespace mlibc
@@ -0,0 +1,11 @@
.section .text
.global __mlibc_start_thread
__mlibc_start_thread:
ld a0, 0x0(sp)
ld a1, 0x8(sp)
ld a2, 0x10(sp)
addi sp, sp, 24
call __mlibc_enter_thread
.section .note.GNU-stack,"",%progbits
@@ -0,0 +1,497 @@
#include <cstddef>
#include <fcntl.h>
#include <stdint.h>
#include <string.h>
#include <frg/manual_box.hpp>
#include <frg/string.hpp>
#include <hel-syscalls.h>
#include <hel.h>
#include <fs.frigg_bragi.hpp>
#include <mlibc/all-sysdeps.hpp>
#include <mlibc/allocator.hpp>
#include <posix.frigg_bragi.hpp>
#include <protocols/posix/data.hpp>
#include <protocols/posix/supercalls.hpp>
// --------------------------------------------------------
// POSIX I/O functions.
// --------------------------------------------------------
HelHandle posixLane;
HelHandle *fileTable;
extern "C" [[gnu::visibility("hidden")]] void abort() {
mlibc::panicLogger() << "rtld: abort() called" << frg::endlog;
__builtin_unreachable();
}
void cacheFileTable() {
if (fileTable)
return;
posix::ManagarmProcessData data;
HEL_CHECK(
helSyscall1(kHelCallSuper + posix::superGetProcessData, reinterpret_cast<HelWord>(&data))
);
posixLane = data.posixLane;
fileTable = data.fileTable;
}
template <typename T>
T load(void *ptr) {
T result;
memcpy(&result, ptr, sizeof(T));
return result;
}
// This Queue implementation is more simplistic than the ones in mlibc and helix.
// In fact, we only manage a single chunk; this minimizes the memory usage of the queue.
struct Queue {
Queue() : _handle{kHelNullHandle}, _lastProgress(0) {
HelQueueParameters params{.flags = 0, .ringShift = 0, .numChunks = 1, .chunkSize = 4096};
HEL_CHECK(helCreateQueue(&params, &_handle));
auto chunksOffset = (sizeof(HelQueue) + (sizeof(int) << 0) + 63) & ~size_t(63);
auto reservedPerChunk = (sizeof(HelChunk) + params.chunkSize + 63) & ~size_t(63);
auto overallSize = chunksOffset + params.numChunks * reservedPerChunk;
void *mapping;
HEL_CHECK(helMapMemory(
_handle,
kHelNullHandle,
nullptr,
0,
(overallSize + 0xFFF) & ~size_t(0xFFF),
kHelMapProtRead | kHelMapProtWrite,
&mapping
));
_queue = reinterpret_cast<HelQueue *>(mapping);
_chunk =
reinterpret_cast<HelChunk *>(reinterpret_cast<std::byte *>(mapping) + chunksOffset);
// Reset and enqueue the first chunk.
_chunk->progressFutex = 0;
_queue->indexQueue[0] = 0;
_queue->headFutex = 1;
_nextIndex = 1;
_wakeHeadFutex();
}
Queue(const Queue &) = delete;
Queue &operator=(const Queue &) = delete;
HelHandle getHandle() { return _handle; }
void *dequeueSingle() {
while (true) {
bool done;
_waitProgressFutex(&done);
if (done) {
// Reset and enqueue the chunk again.
_chunk->progressFutex = 0;
_queue->indexQueue[0] = 0;
_nextIndex = ((_nextIndex + 1) & kHelHeadMask);
_wakeHeadFutex();
_lastProgress = 0;
continue;
}
// Dequeue the next element.
auto ptr = (char *)_chunk + sizeof(HelChunk) + _lastProgress;
auto element = load<HelElement>(ptr);
_lastProgress += sizeof(HelElement) + element.length;
return ptr + sizeof(HelElement);
}
}
private:
void _wakeHeadFutex() {
auto futex = __atomic_exchange_n(&_queue->headFutex, _nextIndex, __ATOMIC_RELEASE);
if (futex & kHelHeadWaiters)
HEL_CHECK(helFutexWake(&_queue->headFutex));
}
void _waitProgressFutex(bool *done) {
while (true) {
auto futex = __atomic_load_n(&_chunk->progressFutex, __ATOMIC_ACQUIRE);
__ensure(!(futex & ~(kHelProgressMask | kHelProgressWaiters | kHelProgressDone)));
do {
if (_lastProgress != (futex & kHelProgressMask)) {
*done = false;
return;
} else if (futex & kHelProgressDone) {
*done = true;
return;
}
if (futex & kHelProgressWaiters)
break; // Waiters bit is already set (in a previous iteration).
} while (!__atomic_compare_exchange_n(
&_chunk->progressFutex,
&futex,
_lastProgress | kHelProgressWaiters,
false,
__ATOMIC_ACQUIRE,
__ATOMIC_ACQUIRE
));
int err = helFutexWait(&_chunk->progressFutex, _lastProgress | kHelProgressWaiters, -1);
if (err == kHelErrCancelled)
continue;
HEL_CHECK(err);
}
}
private:
HelHandle _handle;
HelQueue *_queue;
HelChunk *_chunk;
int _nextIndex;
int _lastProgress;
};
frg::manual_box<Queue> globalQueue;
HelSimpleResult *parseSimple(void *&element) {
auto result = reinterpret_cast<HelSimpleResult *>(element);
element = (char *)element + sizeof(HelSimpleResult);
return result;
}
HelInlineResult *parseInline(void *&element) {
auto result = reinterpret_cast<HelInlineResult *>(element);
element = (char *)element + sizeof(HelInlineResult) + ((result->length + 7) & ~size_t(7));
return result;
}
HelLengthResult *parseLength(void *&element) {
auto result = reinterpret_cast<HelLengthResult *>(element);
element = (char *)element + sizeof(HelLengthResult);
return result;
}
HelHandleResult *parseHandle(void *&element) {
auto result = reinterpret_cast<HelHandleResult *>(element);
element = (char *)element + sizeof(HelHandleResult);
return result;
}
namespace mlibc {
int sys_tcb_set(void *pointer) {
#if defined(__x86_64__)
HEL_CHECK(helWriteFsBase(pointer));
#elif defined(__aarch64__)
uintptr_t addr = reinterpret_cast<uintptr_t>(pointer);
addr += sizeof(Tcb) - 0x10;
asm volatile("msr tpidr_el0, %0" ::"r"(addr));
#elif defined(__riscv) && __riscv_xlen == 64
uintptr_t tp = reinterpret_cast<uintptr_t>(pointer) + sizeof(Tcb);
asm volatile("mv tp, %0" : : "r"(tp) : "memory");
#else
#error Unknown architecture
#endif
return 0;
}
int sys_open(const char *path, int flags, mode_t mode, int *fd) {
cacheFileTable();
HelAction actions[4];
managarm::posix::OpenAtRequest<MemoryAllocator> req(getAllocator());
req.set_fd(AT_FDCWD);
req.set_flags(flags);
req.set_mode(mode);
req.set_path(frg::string<MemoryAllocator>(getAllocator(), path));
if (!globalQueue.valid())
globalQueue.initialize();
frg::string<MemoryAllocator> head(getAllocator());
frg::string<MemoryAllocator> tail(getAllocator());
head.resize(req.size_of_head());
tail.resize(req.size_of_tail());
bragi::limited_writer headWriter{head.data(), head.size()};
bragi::limited_writer tailWriter{tail.data(), tail.size()};
auto headOk = req.encode_head(headWriter);
auto tailOk = req.encode_tail(tailWriter);
__ensure(headOk);
__ensure(tailOk);
actions[0].type = kHelActionOffer;
actions[0].flags = kHelItemAncillary;
actions[1].type = kHelActionSendFromBuffer;
actions[1].flags = kHelItemChain;
actions[1].buffer = head.data();
actions[1].length = head.size();
actions[2].type = kHelActionSendFromBuffer;
actions[2].flags = kHelItemChain;
actions[2].buffer = tail.data();
actions[2].length = tail.size();
actions[3].type = kHelActionRecvInline;
actions[3].flags = 0;
HEL_CHECK(helSubmitAsync(posixLane, actions, 4, globalQueue->getHandle(), 0, 0));
auto element = globalQueue->dequeueSingle();
auto offer = parseHandle(element);
auto send_head = parseSimple(element);
auto send_tail = parseSimple(element);
auto recv_resp = parseInline(element);
HEL_CHECK(offer->error);
HEL_CHECK(send_head->error);
HEL_CHECK(send_tail->error);
HEL_CHECK(recv_resp->error);
managarm::posix::SvrResponse<MemoryAllocator> resp(getAllocator());
resp.ParseFromArray(recv_resp->data, recv_resp->length);
if (resp.error() == managarm::posix::Errors::FILE_NOT_FOUND)
return -1;
__ensure(resp.error() == managarm::posix::Errors::SUCCESS);
*fd = resp.fd();
return 0;
}
int sys_seek(int fd, off_t offset, int whence, off_t *new_offset) {
__ensure(whence == SEEK_SET);
cacheFileTable();
auto lane = fileTable[fd];
HelAction actions[3];
managarm::fs::CntRequest<MemoryAllocator> req(getAllocator());
req.set_req_type(managarm::fs::CntReqType::SEEK_ABS);
req.set_rel_offset(offset);
if (!globalQueue.valid())
globalQueue.initialize();
frg::string<MemoryAllocator> ser(getAllocator());
req.SerializeToString(&ser);
actions[0].type = kHelActionOffer;
actions[0].flags = kHelItemAncillary;
actions[1].type = kHelActionSendFromBuffer;
actions[1].flags = kHelItemChain;
actions[1].buffer = ser.data();
actions[1].length = ser.size();
actions[2].type = kHelActionRecvInline;
actions[2].flags = 0;
HEL_CHECK(helSubmitAsync(lane, actions, 3, globalQueue->getHandle(), 0, 0));
auto element = globalQueue->dequeueSingle();
auto offer = parseHandle(element);
auto send_req = parseSimple(element);
auto recv_resp = parseInline(element);
HEL_CHECK(offer->error);
HEL_CHECK(send_req->error);
HEL_CHECK(recv_resp->error);
managarm::fs::SvrResponse<MemoryAllocator> resp(getAllocator());
resp.ParseFromArray(recv_resp->data, recv_resp->length);
__ensure(resp.error() == managarm::fs::Errors::SUCCESS);
*new_offset = offset;
return 0;
}
int sys_read(int fd, void *data, size_t length, ssize_t *bytes_read) {
cacheFileTable();
auto lane = fileTable[fd];
HelAction actions[5];
managarm::fs::CntRequest<MemoryAllocator> req(getAllocator());
req.set_req_type(managarm::fs::CntReqType::READ);
req.set_size(length);
if (!globalQueue.valid())
globalQueue.initialize();
frg::string<MemoryAllocator> ser(getAllocator());
req.SerializeToString(&ser);
actions[0].type = kHelActionOffer;
actions[0].flags = kHelItemAncillary;
actions[1].type = kHelActionSendFromBuffer;
actions[1].flags = kHelItemChain;
actions[1].buffer = ser.data();
actions[1].length = ser.size();
actions[2].type = kHelActionImbueCredentials;
actions[2].handle = kHelThisThread;
actions[2].flags = kHelItemChain;
actions[3].type = kHelActionRecvInline;
actions[3].flags = kHelItemChain;
actions[4].type = kHelActionRecvToBuffer;
actions[4].flags = 0;
actions[4].buffer = data;
actions[4].length = length;
HEL_CHECK(helSubmitAsync(lane, actions, 5, globalQueue->getHandle(), 0, 0));
auto element = globalQueue->dequeueSingle();
auto offer = parseHandle(element);
auto send_req = parseSimple(element);
auto imbue_creds = parseSimple(element);
auto recv_resp = parseInline(element);
auto recv_data = parseLength(element);
HEL_CHECK(offer->error);
HEL_CHECK(send_req->error);
HEL_CHECK(imbue_creds->error);
HEL_CHECK(recv_resp->error);
HEL_CHECK(recv_data->error);
managarm::fs::SvrResponse<MemoryAllocator> resp(getAllocator());
resp.ParseFromArray(recv_resp->data, recv_resp->length);
__ensure(resp.error() == managarm::fs::Errors::SUCCESS);
*bytes_read = recv_data->length;
return 0;
}
int sys_vm_map(void *hint, size_t size, int prot, int flags, int fd, off_t offset, void **window) {
cacheFileTable();
HelAction actions[3];
managarm::posix::VmMapRequest<MemoryAllocator> req(getAllocator());
req.set_address_hint(reinterpret_cast<uintptr_t>(hint));
req.set_size(size);
req.set_mode(prot);
req.set_flags(flags);
req.set_fd(fd);
req.set_rel_offset(offset);
if (!globalQueue.valid())
globalQueue.initialize();
frg::string<MemoryAllocator> ser(getAllocator());
req.SerializeToString(&ser);
actions[0].type = kHelActionOffer;
actions[0].flags = kHelItemAncillary;
actions[1].type = kHelActionSendFromBuffer;
actions[1].flags = kHelItemChain;
actions[1].buffer = ser.data();
actions[1].length = ser.size();
actions[2].type = kHelActionRecvInline;
actions[2].flags = 0;
HEL_CHECK(helSubmitAsync(posixLane, actions, 3, globalQueue->getHandle(), 0, 0));
auto element = globalQueue->dequeueSingle();
auto offer = parseHandle(element);
auto send_req = parseSimple(element);
auto recv_resp = parseInline(element);
HEL_CHECK(offer->error);
HEL_CHECK(send_req->error);
HEL_CHECK(recv_resp->error);
managarm::posix::SvrResponse<MemoryAllocator> resp(getAllocator());
resp.ParseFromArray(recv_resp->data, recv_resp->length);
__ensure(resp.error() == managarm::posix::Errors::SUCCESS);
*window = reinterpret_cast<void *>(resp.offset());
return 0;
}
int sys_close(int fd) {
cacheFileTable();
HelAction actions[3];
managarm::posix::CloseRequest<MemoryAllocator> req(getAllocator());
req.set_fd(fd);
if (!globalQueue.valid())
globalQueue.initialize();
frg::string<MemoryAllocator> ser(getAllocator());
req.SerializeToString(&ser);
actions[0].type = kHelActionOffer;
actions[0].flags = kHelItemAncillary;
actions[1].type = kHelActionSendFromBuffer;
actions[1].flags = kHelItemChain;
actions[1].buffer = ser.data();
actions[1].length = ser.size();
actions[2].type = kHelActionRecvInline;
actions[2].flags = 0;
HEL_CHECK(helSubmitAsync(posixLane, actions, 3, globalQueue->getHandle(), 0, 0));
auto element = globalQueue->dequeueSingle();
auto offer = parseHandle(element);
auto send_req = parseSimple(element);
auto recv_resp = parseInline(element);
HEL_CHECK(offer->error);
HEL_CHECK(send_req->error);
HEL_CHECK(recv_resp->error);
managarm::posix::SvrResponse<MemoryAllocator> resp(getAllocator());
resp.ParseFromArray(recv_resp->data, recv_resp->length);
__ensure(resp.error() == managarm::posix::Errors::SUCCESS);
return 0;
}
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_vm_protect(void *pointer, size_t size, int prot) {
managarm::posix::CntRequest<MemoryAllocator> req(getAllocator());
req.set_request_type(managarm::posix::CntReqType::VM_PROTECT);
req.set_address(reinterpret_cast<uintptr_t>(pointer));
req.set_size(size);
req.set_mode(prot);
if (!globalQueue.valid())
globalQueue.initialize();
frg::string<MemoryAllocator> ser(getAllocator());
req.SerializeToString(&ser);
HelAction actions[3];
actions[0].type = kHelActionOffer;
actions[0].flags = kHelItemAncillary;
actions[1].type = kHelActionSendFromBuffer;
actions[1].flags = kHelItemChain;
actions[1].buffer = ser.data();
actions[1].length = ser.size();
actions[2].type = kHelActionRecvInline;
actions[2].flags = 0;
HEL_CHECK(helSubmitAsync(posixLane, actions, 3, globalQueue->getHandle(), 0, 0));
auto element = globalQueue->dequeueSingle();
auto offer = parseHandle(element);
auto send_req = parseSimple(element);
auto recv_resp = parseInline(element);
HEL_CHECK(offer->error);
HEL_CHECK(send_req->error);
HEL_CHECK(recv_resp->error);
managarm::posix::SvrResponse<MemoryAllocator> resp(getAllocator());
resp.ParseFromArray(recv_resp->data, recv_resp->length);
__ensure(resp.error() == managarm::posix::Errors::SUCCESS);
return 0;
}
} // namespace mlibc
@@ -0,0 +1,24 @@
#include <abi-bits/signal.h>
#include <stddef.h>
#include "context-offsets.h"
// offsets int ucontext_t as used in signals.S
static_assert(offsetof(ucontext_t, uc_mcontext) == UCONTEXT_GREGS_OFFSET);
static_assert(offsetof(ucontext_t, uc_mcontext.gregs[REG_R8]) == UCONTEXT_OFFSET_R8);
static_assert(offsetof(ucontext_t, uc_mcontext.gregs[REG_R9]) == UCONTEXT_OFFSET_R9);
static_assert(offsetof(ucontext_t, uc_mcontext.gregs[REG_R10]) == UCONTEXT_OFFSET_R10);
static_assert(offsetof(ucontext_t, uc_mcontext.gregs[REG_R11]) == UCONTEXT_OFFSET_R11);
static_assert(offsetof(ucontext_t, uc_mcontext.gregs[REG_R12]) == UCONTEXT_OFFSET_R12);
static_assert(offsetof(ucontext_t, uc_mcontext.gregs[REG_R13]) == UCONTEXT_OFFSET_R13);
static_assert(offsetof(ucontext_t, uc_mcontext.gregs[REG_R14]) == UCONTEXT_OFFSET_R14);
static_assert(offsetof(ucontext_t, uc_mcontext.gregs[REG_R15]) == UCONTEXT_OFFSET_R15);
static_assert(offsetof(ucontext_t, uc_mcontext.gregs[REG_RDI]) == UCONTEXT_OFFSET_RDI);
static_assert(offsetof(ucontext_t, uc_mcontext.gregs[REG_RSI]) == UCONTEXT_OFFSET_RSI);
static_assert(offsetof(ucontext_t, uc_mcontext.gregs[REG_RBP]) == UCONTEXT_OFFSET_RBP);
static_assert(offsetof(ucontext_t, uc_mcontext.gregs[REG_RBX]) == UCONTEXT_OFFSET_RBX);
static_assert(offsetof(ucontext_t, uc_mcontext.gregs[REG_RDX]) == UCONTEXT_OFFSET_RDX);
static_assert(offsetof(ucontext_t, uc_mcontext.gregs[REG_RAX]) == UCONTEXT_OFFSET_RAX);
static_assert(offsetof(ucontext_t, uc_mcontext.gregs[REG_RCX]) == UCONTEXT_OFFSET_RCX);
static_assert(offsetof(ucontext_t, uc_mcontext.gregs[REG_RSP]) == UCONTEXT_OFFSET_RSP);
static_assert(offsetof(ucontext_t, uc_mcontext.gregs[REG_RIP]) == UCONTEXT_OFFSET_RIP);
@@ -0,0 +1,21 @@
#pragma once
#define UCONTEXT_GREGS_OFFSET 40
#define UCONTEXT_OFFSET_R8 (UCONTEXT_GREGS_OFFSET + 0)
#define UCONTEXT_OFFSET_R9 (UCONTEXT_GREGS_OFFSET + 8)
#define UCONTEXT_OFFSET_R10 (UCONTEXT_GREGS_OFFSET + 16)
#define UCONTEXT_OFFSET_R11 (UCONTEXT_GREGS_OFFSET + 24)
#define UCONTEXT_OFFSET_R12 (UCONTEXT_GREGS_OFFSET + 32)
#define UCONTEXT_OFFSET_R13 (UCONTEXT_GREGS_OFFSET + 40)
#define UCONTEXT_OFFSET_R14 (UCONTEXT_GREGS_OFFSET + 48)
#define UCONTEXT_OFFSET_R15 (UCONTEXT_GREGS_OFFSET + 56)
#define UCONTEXT_OFFSET_RDI (UCONTEXT_GREGS_OFFSET + 64)
#define UCONTEXT_OFFSET_RSI (UCONTEXT_GREGS_OFFSET + 72)
#define UCONTEXT_OFFSET_RBP (UCONTEXT_GREGS_OFFSET + 80)
#define UCONTEXT_OFFSET_RBX (UCONTEXT_GREGS_OFFSET + 88)
#define UCONTEXT_OFFSET_RDX (UCONTEXT_GREGS_OFFSET + 96)
#define UCONTEXT_OFFSET_RAX (UCONTEXT_GREGS_OFFSET + 104)
#define UCONTEXT_OFFSET_RCX (UCONTEXT_GREGS_OFFSET + 112)
#define UCONTEXT_OFFSET_RSP (UCONTEXT_GREGS_OFFSET + 120)
#define UCONTEXT_OFFSET_RIP (UCONTEXT_GREGS_OFFSET + 128)
@@ -0,0 +1,8 @@
.section .text
.global _start
_start:
mov %rsp, %rdi
lea main(%rip), %rsi
call __mlibc_entry
.section .note.GNU-stack,"",%progbits
@@ -0,0 +1,10 @@
.section .text
.global _start
_start:
mov %rsp, %rdi
mov $main, %rsi
call __mlibc_entry
.section .note.GNU-stack,"",%progbits
@@ -0,0 +1,15 @@
.ident "x86_64-managarm-mlibc crti"
.section .init
.globl _init
.type _init,@function
_init:
push %rax
.section .fini
.globl _fini
.type _fini,@function
_fini:
push %rax
.section .note.GNU-stack,"",%progbits
@@ -0,0 +1,11 @@
.ident "x86_64-managarm-mlibc crtn"
.section .init
pop %rax
ret
.section .fini
pop %rax
ret
.section .note.GNU-stack,"",%progbits
@@ -0,0 +1,33 @@
#include "mlibc-asm/dwarf-helpers.h"
#include "mlibc-asm/helpers.h"
#include "context-offsets.h"
.section .text
.cfi_startproc
.cfi_signal_frame
cfi_set_cfa_to_ptr_with_offset DWARF_REG_RSP, UCONTEXT_OFFSET_RSP
cfi_set_prev_reg_value DWARF_REG_R8, DWARF_REG_RSP, UCONTEXT_OFFSET_R8
cfi_set_prev_reg_value DWARF_REG_R9, DWARF_REG_RSP, UCONTEXT_OFFSET_R9
cfi_set_prev_reg_value DWARF_REG_R10, DWARF_REG_RSP, UCONTEXT_OFFSET_R10
cfi_set_prev_reg_value DWARF_REG_R11, DWARF_REG_RSP, UCONTEXT_OFFSET_R11
cfi_set_prev_reg_value DWARF_REG_R12, DWARF_REG_RSP, UCONTEXT_OFFSET_R12
cfi_set_prev_reg_value DWARF_REG_R13, DWARF_REG_RSP, UCONTEXT_OFFSET_R13
cfi_set_prev_reg_value DWARF_REG_R14, DWARF_REG_RSP, UCONTEXT_OFFSET_R14
cfi_set_prev_reg_value DWARF_REG_R15, DWARF_REG_RSP, UCONTEXT_OFFSET_R15
cfi_set_prev_reg_value DWARF_REG_RDI, DWARF_REG_RSP, UCONTEXT_OFFSET_RDI
cfi_set_prev_reg_value DWARF_REG_RSI, DWARF_REG_RSP, UCONTEXT_OFFSET_RSI
cfi_set_prev_reg_value DWARF_REG_RBP, DWARF_REG_RSP, UCONTEXT_OFFSET_RBP
cfi_set_prev_reg_value DWARF_REG_RBX, DWARF_REG_RSP, UCONTEXT_OFFSET_RBX
cfi_set_prev_reg_value DWARF_REG_RDX, DWARF_REG_RSP, UCONTEXT_OFFSET_RDX
cfi_set_prev_reg_value DWARF_REG_RAX, DWARF_REG_RSP, UCONTEXT_OFFSET_RAX
cfi_set_prev_reg_value DWARF_REG_RCX, DWARF_REG_RSP, UCONTEXT_OFFSET_RCX
cfi_set_prev_reg_value DWARF_REG_RETURN_ADDRESS, DWARF_REG_RSP, UCONTEXT_OFFSET_RIP
nop
PROC_START_NOCFI(__mlibc_signal_restore)
mov $0x80000006, %rdi
syscall
ud2
PROC_END(__mlibc_signal_restore)
GNU_STACK_NOTE()
@@ -0,0 +1,65 @@
#include <bits/ensure.h>
#include <errno.h>
#include <mlibc/all-sysdeps.hpp>
#include <mlibc/tcb.hpp>
#include <mlibc/thread-entry.hpp>
#include <stddef.h>
#include <stdint.h>
#include <sys/mman.h>
extern "C" void __mlibc_enter_thread(void *entry, void *user_arg, Tcb *tcb) {
// Wait until our parent sets up the TID.
while (!__atomic_load_n(&tcb->tid, __ATOMIC_RELAXED))
mlibc::sys_futex_wait(&tcb->tid, 0, nullptr);
if (mlibc::sys_tcb_set(tcb))
__ensure(!"sys_tcb_set() failed");
tcb->invokeThreadFunc(entry, user_arg);
auto self = reinterpret_cast<Tcb *>(tcb);
__atomic_store_n(&self->didExit, 1, __ATOMIC_RELEASE);
mlibc::sys_futex_wake(&self->didExit);
mlibc::sys_thread_exit();
}
namespace mlibc {
static constexpr size_t default_stacksize = 0x200000;
int sys_prepare_stack(
void **stack,
void *entry,
void *user_arg,
void *tcb,
size_t *stack_size,
size_t *guard_size,
void **stack_base
) {
if (!*stack_size)
*stack_size = default_stacksize;
*guard_size = 0;
if (*stack) {
*stack_base = *stack;
} else {
*stack_base =
mmap(nullptr, *stack_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (*stack_base == MAP_FAILED) {
return errno;
}
}
uintptr_t *sp =
reinterpret_cast<uintptr_t *>(reinterpret_cast<uintptr_t>(*stack_base) + *stack_size);
*--sp = reinterpret_cast<uintptr_t>(tcb);
*--sp = reinterpret_cast<uintptr_t>(user_arg);
*--sp = reinterpret_cast<uintptr_t>(entry);
*stack = reinterpret_cast<void *>(sp);
return 0;
}
} // namespace mlibc
@@ -0,0 +1,22 @@
.section .text
.global __mlibc_start_thread
.type __mlibc_start_thread, "function"
.cfi_startproc
__mlibc_start_thread:
.cfi_undefined %rip
.cfi_undefined %rbp
pop %rdi
.cfi_adjust_cfa_offset -8
.cfi_undefined %rdi
pop %rsi
.cfi_adjust_cfa_offset -8
.cfi_undefined %rsi
pop %rdx
.cfi_adjust_cfa_offset -8
.cfi_undefined %rdx
call __mlibc_enter_thread
ud2
.cfi_endproc
.section .note.GNU-stack,"",%progbits