= TODO

These have various sub-cases that vary significantly in complexity.

== mmap

== mprotect

== munmap

= Trivial

== arch_prctl(ARCH_SET_FS, ptr)

This is just `SETFS`.
Safe to pass through, but also trivial to implement.

== ioctl(fd, TCGETS, …)

gcc likes doing these on various fds, but `return -ENOTTY;` is enough of an implementation.

== close(fd)

Close a file descriptor.

== getrusage(RUSAGE_SELF, ptr)

gcc is rather eager to know how long it's taking.
We can probably stub this out safely.

== sysinfo(ptr)

I'm honestly not sure why gcc reads this, but we can probably stub it out.

== umask(mask)

Thread-unsafe, but we should probably bail if this gets invoked while multithreaded.
We just `mode &= ~mask` any filesystem call, and that's the only place this matters.

== prlimit(0, resource, NULL, ptr)

Retrieve a resource limit.
We can stub all of these out.

=== prlimit64(0, RLIMIT_STACK, NULL, ptr)

Retrieve the maximum stack size, presumably specifically of the main thread.

=== prlimit64(0, RLIMIT_AS, NULL, ptr)

Retrieve the maximum virtual memory size.

=== prlimit64(0, RLIMIT_RSS, NULL, ptr)

The maximum amount of resident memory.

=== prlimit64(0, RLIMIT_NOFILE, NULL, ptr)

The maximum amount of file descriptors.

= Easy

== brk

Should be fairly trivial.
`validate_prctl_map_addr` documents the requirements on address space layout.

== exit_group(status)

Kill the process, aka thread group.
Straightforward enough, although we do need to emit `SIGCHLD` appropriately.

== fcntl(fd, F_GETFD)

Grab the current flags for an fd.
Used only immediately preceding `fcntl(fd, F_SETFD, FD_CLOEXEC)`.

== fcntl(fd, F_SETFD, FD_CLOEXEC)

Set the `CLOEXEC` flag.

= This is where they start to trick you

== set_robust_list

The code for this itself isn't complex (we just store a pointer), but we have to actually do futex signalling on process death and execve.
Technically trivial if `futex` is never called.

== set_tid_address
The code for this itself isn't complex (we just store a pointer), but we have to actually do futex signalling on thread termination.
Technically trivial if `futex` is never called.

== getcwd(buf, len)

Read the current working directory path into a buffer.
Equivalent to `readlink("/proc/self/cwd", buf, len)`.
Probably bail if anything funny happens.

== readlink(path, buf, len)

Read the target of a symlink.

== unlink(path)

Remove a directory entry that is itself not a directory.
Equivalent to `unlinkat(AT_FDCWD, path, 0)`.

== chmod(path, mode)

Change the mode of an inode.

== pipe2(fds_out, flags)

Technically easy, but almost certainly being invoked for imminent IPC.

== read(fd, ptr, len)

Read bytes from a file descriptor.
Hard if it's IPC, easier if it's filesystem I/O.

== lseek(fd, offset, SEEK_SET)

Reposition the file offset, relative to the start of the file.

== lseek(fd, offset, SEEK_CUR)

Reposition the file offset, relative to the current offset.

== pread64(fd, buf, len, offset)

Equivalent to atomic `lseek(fd, offset, SEEK_SET); read(fd, buf, len)`, but without the side effects on the file descriptor offset.

== write(fd, ptr, len)

Write bytes to a file descriptor.
Hard if it's IPC, easier if it's filesystem I/O.

== execve(pathname, argv, envp)

Replace the current process image with another, loaded from a file.
Technically easy, modulo `O_CLOEXEC` handling, but almost certainly spells imminent IPC.

== access(path, mode)

Equivalent to atomic `int fd = open(path, mode); if (fd >= 0) close(fd);`, except if `eugid != ugid`.
Build systems are rarely setuid, so this distinction seems ok to bail on.

== openat(AT_FDCWD, path, flags, mode)

Filesystem I/O.

= This one's hard

== newfstatat(dirfd, name, stat_out, flag)

Filesystem I/O, primarily complicated by mtime and inode numbers.

== vfork()

Strictly less troubling than fork, but this spells imminent IPC.

== wait4(wstatus, 0, NULL)

Pure IPC.
This probably isn't much trouble if nothing unusual happens, however.

== rt_sigaction

Signals are complicated, underspecified, *and* IPC.
This is going to hurt.

== rt_sigprocmask

Signal mask manipulation is a doozy, but unblocking signals can deliver queued signals…
